diff --git a/anolis/configs/L0-MANDATORY/default/CONFIG_PCI_PF_STUB b/anolis/configs/L0-MANDATORY/arm64/CONFIG_PCI_PF_STUB similarity index 100% rename from anolis/configs/L0-MANDATORY/default/CONFIG_PCI_PF_STUB rename to anolis/configs/L0-MANDATORY/arm64/CONFIG_PCI_PF_STUB diff --git a/anolis/configs/L0-MANDATORY/default/CONFIG_CPU_MITIGATIONS b/anolis/configs/L0-MANDATORY/default/CONFIG_CPU_MITIGATIONS deleted file mode 100644 index 3d6f96778a81718c24ec0b00635ee3071f36a5da..0000000000000000000000000000000000000000 --- a/anolis/configs/L0-MANDATORY/default/CONFIG_CPU_MITIGATIONS +++ /dev/null @@ -1 +0,0 @@ -CONFIG_CPU_MITIGATIONS=y diff --git a/anolis/configs/L0-MANDATORY/default/CONFIG_LIVEPATCH b/anolis/configs/L0-MANDATORY/x86/CONFIG_LIVEPATCH similarity index 100% rename from anolis/configs/L0-MANDATORY/default/CONFIG_LIVEPATCH rename to anolis/configs/L0-MANDATORY/x86/CONFIG_LIVEPATCH diff --git a/anolis/configs/L0-MANDATORY/x86/CONFIG_PCI_PF_STUB b/anolis/configs/L0-MANDATORY/x86/CONFIG_PCI_PF_STUB new file mode 100644 index 0000000000000000000000000000000000000000..46eee76194b0cffb73add26b2deed58cb65571d7 --- /dev/null +++ b/anolis/configs/L0-MANDATORY/x86/CONFIG_PCI_PF_STUB @@ -0,0 +1 @@ +CONFIG_PCI_PF_STUB=y diff --git a/anolis/configs/L0-MANDATORY/x86/CONFIG_SPECULATION_MITIGATIONS b/anolis/configs/L0-MANDATORY/x86/CONFIG_SPECULATION_MITIGATIONS new file mode 100644 index 0000000000000000000000000000000000000000..37f78a6f2368aecf7a546b3541a9e4d1d0e8b19f --- /dev/null +++ b/anolis/configs/L0-MANDATORY/x86/CONFIG_SPECULATION_MITIGATIONS @@ -0,0 +1 @@ +CONFIG_SPECULATION_MITIGATIONS=y diff --git a/anolis/configs/L0-MANDATORY/default/CONFIG_UNWINDER_ORC b/anolis/configs/L0-MANDATORY/x86/CONFIG_UNWINDER_ORC similarity index 100% rename from anolis/configs/L0-MANDATORY/default/CONFIG_UNWINDER_ORC rename to anolis/configs/L0-MANDATORY/x86/CONFIG_UNWINDER_ORC diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_ARM64_CONTPTE b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_ARM64_CONTPTE deleted file mode 100644 index 23a09e20f027b625cfe11cac4b1b234bdb88854f..0000000000000000000000000000000000000000 --- a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_ARM64_CONTPTE +++ /dev/null @@ -1 +0,0 @@ -CONFIG_ARM64_CONTPTE=y diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_ARM64_ERRATUM_3194386 b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_ARM64_ERRATUM_3194386 deleted file mode 100644 index f6f6f286638d98a5af6ad13933603ae54440f053..0000000000000000000000000000000000000000 --- a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_ARM64_ERRATUM_3194386 +++ /dev/null @@ -1 +0,0 @@ -CONFIG_ARM64_ERRATUM_3194386=y diff --git a/anolis/configs/L1-RECOMMEND/default/CONFIG_GENERIC_PHY b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_GENERIC_PHY similarity index 100% rename from anolis/configs/L1-RECOMMEND/default/CONFIG_GENERIC_PHY rename to anolis/configs/L1-RECOMMEND/arm64/CONFIG_GENERIC_PHY diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET new file mode 100644 index 0000000000000000000000000000000000000000..c7daa4f60d5d789fd8836673ef1eeebd642f6352 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET @@ -0,0 +1 @@ +# CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET is not set diff --git a/anolis/configs/L1-RECOMMEND/default/CONFIG_HAVE_LIVEPATCH b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_HAVE_LIVEPATCH similarity index 100% rename from anolis/configs/L1-RECOMMEND/default/CONFIG_HAVE_LIVEPATCH rename to anolis/configs/L1-RECOMMEND/arm64/CONFIG_HAVE_LIVEPATCH diff --git a/anolis/configs/L1-RECOMMEND/default/CONFIG_HAVE_RELIABLE_STACKTRACE b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_HAVE_RELIABLE_STACKTRACE similarity index 100% rename from anolis/configs/L1-RECOMMEND/default/CONFIG_HAVE_RELIABLE_STACKTRACE rename to anolis/configs/L1-RECOMMEND/arm64/CONFIG_HAVE_RELIABLE_STACKTRACE diff --git a/anolis/configs/L1-RECOMMEND/default/CONFIG_HAVE_STACK_VALIDATION b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_HAVE_STACK_VALIDATION similarity index 100% rename from anolis/configs/L1-RECOMMEND/default/CONFIG_HAVE_STACK_VALIDATION rename to anolis/configs/L1-RECOMMEND/arm64/CONFIG_HAVE_STACK_VALIDATION diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_LIVEPATCH b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_LIVEPATCH new file mode 100644 index 0000000000000000000000000000000000000000..1b05d0d1a109b049181b54fbb6fb08b303057502 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_LIVEPATCH @@ -0,0 +1 @@ +CONFIG_LIVEPATCH=y diff --git a/anolis/configs/L1-RECOMMEND/default/CONFIG_OBJTOOL b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_OBJTOOL similarity index 100% rename from anolis/configs/L1-RECOMMEND/default/CONFIG_OBJTOOL rename to anolis/configs/L1-RECOMMEND/arm64/CONFIG_OBJTOOL diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_RANDOMIZE_KSTACK_OFFSET b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_RANDOMIZE_KSTACK_OFFSET new file mode 100644 index 0000000000000000000000000000000000000000..759cb13e424ce0a28ea6a717af3162ad7e82a3b2 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_RANDOMIZE_KSTACK_OFFSET @@ -0,0 +1 @@ +# CONFIG_RANDOMIZE_KSTACK_OFFSET is not set diff --git a/anolis/configs/L1-RECOMMEND/x86/CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT similarity index 100% rename from anolis/configs/L1-RECOMMEND/x86/CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT rename to anolis/configs/L1-RECOMMEND/arm64/CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT diff --git a/anolis/configs/L0-MANDATORY/arm64/CONFIG_UNWINDER_FRAME_POINTER b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UNWINDER_FRAME_POINTER similarity index 100% rename from anolis/configs/L0-MANDATORY/arm64/CONFIG_UNWINDER_FRAME_POINTER rename to anolis/configs/L1-RECOMMEND/arm64/CONFIG_UNWINDER_FRAME_POINTER diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UNWINDER_ORC b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UNWINDER_ORC new file mode 100644 index 0000000000000000000000000000000000000000..6b6908419acbf37b0c23a92b55d816eb5ffda505 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_UNWINDER_ORC @@ -0,0 +1 @@ +CONFIG_UNWINDER_ORC=y diff --git a/anolis/configs/L1-RECOMMEND/default/CONFIG_AHCI_ZHAOXIN_SGPIO b/anolis/configs/L1-RECOMMEND/default/CONFIG_AHCI_ZHAOXIN_SGPIO deleted file mode 100644 index 6a7ffe559b94cdfe188641c29026476a83b4b3e5..0000000000000000000000000000000000000000 --- a/anolis/configs/L1-RECOMMEND/default/CONFIG_AHCI_ZHAOXIN_SGPIO +++ /dev/null @@ -1 +0,0 @@ -CONFIG_AHCI_ZHAOXIN_SGPIO=m diff --git a/anolis/configs/L1-RECOMMEND/default/CONFIG_PCP_BATCH_SCALE_MAX b/anolis/configs/L1-RECOMMEND/default/CONFIG_PCP_BATCH_SCALE_MAX deleted file mode 100644 index 8c42e3567daa42e477994ca37502f7320a4b13b9..0000000000000000000000000000000000000000 --- a/anolis/configs/L1-RECOMMEND/default/CONFIG_PCP_BATCH_SCALE_MAX +++ /dev/null @@ -1 +0,0 @@ -CONFIG_PCP_BATCH_SCALE_MAX=5 diff --git a/anolis/configs/L1-RECOMMEND/x86/CONFIG_RANDOMIZE_KSTACK_OFFSET b/anolis/configs/L1-RECOMMEND/default/CONFIG_RANDOMIZE_KSTACK_OFFSET similarity index 100% rename from anolis/configs/L1-RECOMMEND/x86/CONFIG_RANDOMIZE_KSTACK_OFFSET rename to anolis/configs/L1-RECOMMEND/default/CONFIG_RANDOMIZE_KSTACK_OFFSET diff --git a/anolis/configs/L1-RECOMMEND/default/CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT b/anolis/configs/L1-RECOMMEND/default/CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT new file mode 100644 index 0000000000000000000000000000000000000000..d680659c170364c8a31f4791368afd3e3c419620 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/default/CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT @@ -0,0 +1 @@ +# CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT is not set diff --git a/anolis/configs/L1-RECOMMEND/x86/CONFIG_GENERIC_PHY b/anolis/configs/L1-RECOMMEND/x86/CONFIG_GENERIC_PHY new file mode 100644 index 0000000000000000000000000000000000000000..582e87c3b9f58b7e4e14ab58bf26b634f1acad0d --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/x86/CONFIG_GENERIC_PHY @@ -0,0 +1 @@ +# CONFIG_GENERIC_PHY is not set diff --git a/anolis/configs/L1-RECOMMEND/x86/CONFIG_MITIGATION_SPECTRE_BHI b/anolis/configs/L1-RECOMMEND/x86/CONFIG_MITIGATION_SPECTRE_BHI deleted file mode 100644 index 71b428227384df2334f71ea829b3156aa83e2f17..0000000000000000000000000000000000000000 --- a/anolis/configs/L1-RECOMMEND/x86/CONFIG_MITIGATION_SPECTRE_BHI +++ /dev/null @@ -1 +0,0 @@ -CONFIG_MITIGATION_SPECTRE_BHI=y diff --git a/anolis/configs/L1-RECOMMEND/default/CONFIG_TEST_LIVEPATCH b/anolis/configs/L1-RECOMMEND/x86/CONFIG_TEST_LIVEPATCH similarity index 100% rename from anolis/configs/L1-RECOMMEND/default/CONFIG_TEST_LIVEPATCH rename to anolis/configs/L1-RECOMMEND/x86/CONFIG_TEST_LIVEPATCH diff --git a/anolis/configs/L2-OPTIONAL/default/CONFIG_DRM_PANEL_ILITEK_ILI9341 b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_DRM_PANEL_ILITEK_ILI9341 similarity index 100% rename from anolis/configs/L2-OPTIONAL/default/CONFIG_DRM_PANEL_ILITEK_ILI9341 rename to anolis/configs/L2-OPTIONAL/arm64/CONFIG_DRM_PANEL_ILITEK_ILI9341 diff --git a/anolis/configs/L2-OPTIONAL/arm64/CONFIG_TEST_LIVEPATCH b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_TEST_LIVEPATCH new file mode 100644 index 0000000000000000000000000000000000000000..0dd7700464a819795bdb3ac2e74acff1012eabf4 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_TEST_LIVEPATCH @@ -0,0 +1 @@ +CONFIG_TEST_LIVEPATCH=m diff --git a/anolis/configs/L2-OPTIONAL/default/CONFIG_BCACHE_ASYNC_REGISTRATION b/anolis/configs/L2-OPTIONAL/default/CONFIG_BCACHE_ASYNC_REGISTRATION deleted file mode 100644 index d966c9744d3d034845bc3b36e2ba1acfd5cb9d29..0000000000000000000000000000000000000000 --- a/anolis/configs/L2-OPTIONAL/default/CONFIG_BCACHE_ASYNC_REGISTRATION +++ /dev/null @@ -1 +0,0 @@ -# CONFIG_BCACHE_ASYNC_REGISTRATION is not set diff --git a/anolis/configs/L2-OPTIONAL/default/CONFIG_BCACHE_CLOSURES_DEBUG b/anolis/configs/L2-OPTIONAL/default/CONFIG_BCACHE_CLOSURES_DEBUG deleted file mode 100644 index eb3f1af90e6cdc42c991522389887e40f204a960..0000000000000000000000000000000000000000 --- a/anolis/configs/L2-OPTIONAL/default/CONFIG_BCACHE_CLOSURES_DEBUG +++ /dev/null @@ -1 +0,0 @@ -# CONFIG_BCACHE_CLOSURES_DEBUG is not set diff --git a/anolis/configs/L2-OPTIONAL/default/CONFIG_BCACHE_DEBUG b/anolis/configs/L2-OPTIONAL/default/CONFIG_BCACHE_DEBUG deleted file mode 100644 index 36426027b2d10d39b3dcee18fdfcc421c5914d70..0000000000000000000000000000000000000000 --- a/anolis/configs/L2-OPTIONAL/default/CONFIG_BCACHE_DEBUG +++ /dev/null @@ -1 +0,0 @@ -# CONFIG_BCACHE_DEBUG is not set diff --git a/anolis/configs/L2-OPTIONAL/default/CONFIG_CRYPTO_SIG b/anolis/configs/L2-OPTIONAL/default/CONFIG_CRYPTO_SIG deleted file mode 100644 index 01ee2034fbdcbf1b861226829cd5b11a72865469..0000000000000000000000000000000000000000 --- a/anolis/configs/L2-OPTIONAL/default/CONFIG_CRYPTO_SIG +++ /dev/null @@ -1 +0,0 @@ -CONFIG_CRYPTO_SIG=y diff --git a/anolis/configs/L2-OPTIONAL/default/CONFIG_FB_IOMEM_FOPS b/anolis/configs/L2-OPTIONAL/default/CONFIG_FB_IOMEM_FOPS deleted file mode 100644 index 485cf9b71de5bc92aa9662c4ce05178444f036c0..0000000000000000000000000000000000000000 --- a/anolis/configs/L2-OPTIONAL/default/CONFIG_FB_IOMEM_FOPS +++ /dev/null @@ -1 +0,0 @@ -CONFIG_FB_IOMEM_FOPS=y diff --git a/anolis/configs/L1-RECOMMEND/x86/CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET b/anolis/configs/L2-OPTIONAL/default/CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET similarity index 100% rename from anolis/configs/L1-RECOMMEND/x86/CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET rename to anolis/configs/L2-OPTIONAL/default/CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET diff --git a/anolis/configs/L2-OPTIONAL/default/CONFIG_PATA_HPT3X3_DMA b/anolis/configs/L2-OPTIONAL/default/CONFIG_PATA_HPT3X3_DMA deleted file mode 100644 index 723cb8bb73ac173caa7e6b2e06f67b09282d6d07..0000000000000000000000000000000000000000 --- a/anolis/configs/L2-OPTIONAL/default/CONFIG_PATA_HPT3X3_DMA +++ /dev/null @@ -1 +0,0 @@ -# CONFIG_PATA_HPT3X3_DMA is not set diff --git a/anolis/configs/L2-OPTIONAL/default/CONFIG_SCREEN_INFO b/anolis/configs/L2-OPTIONAL/default/CONFIG_SCREEN_INFO deleted file mode 100644 index e5a12d9c60dbf35555f6d07b22dd67e91b1d6e82..0000000000000000000000000000000000000000 --- a/anolis/configs/L2-OPTIONAL/default/CONFIG_SCREEN_INFO +++ /dev/null @@ -1 +0,0 @@ -CONFIG_SCREEN_INFO=y diff --git a/anolis/configs/L2-OPTIONAL/default/CONFIG_SCSI_MVSAS_DEBUG b/anolis/configs/L2-OPTIONAL/default/CONFIG_SCSI_MVSAS_DEBUG deleted file mode 100644 index aa295ebbb545f15dea010d3e28bc1aeec3a9374f..0000000000000000000000000000000000000000 --- a/anolis/configs/L2-OPTIONAL/default/CONFIG_SCSI_MVSAS_DEBUG +++ /dev/null @@ -1 +0,0 @@ -CONFIG_SCSI_MVSAS_DEBUG=y diff --git a/anolis/configs/L2-OPTIONAL/default/CONFIG_SCSI_MVSAS_TASKLET b/anolis/configs/L2-OPTIONAL/default/CONFIG_SCSI_MVSAS_TASKLET deleted file mode 100644 index 028f7d8e3d2556789f083574c2f8275e9240d770..0000000000000000000000000000000000000000 --- a/anolis/configs/L2-OPTIONAL/default/CONFIG_SCSI_MVSAS_TASKLET +++ /dev/null @@ -1 +0,0 @@ -# CONFIG_SCSI_MVSAS_TASKLET is not set diff --git a/anolis/configs/L2-OPTIONAL/x86/CONFIG_AD9467 b/anolis/configs/L2-OPTIONAL/x86/CONFIG_AD9467 deleted file mode 100644 index 421ac1f25eec2765aa1eb61699b5b41152503b77..0000000000000000000000000000000000000000 --- a/anolis/configs/L2-OPTIONAL/x86/CONFIG_AD9467 +++ /dev/null @@ -1 +0,0 @@ -# CONFIG_AD9467 is not set diff --git a/anolis/configs/L2-OPTIONAL/x86/CONFIG_ADI_AXI_ADC b/anolis/configs/L2-OPTIONAL/x86/CONFIG_ADI_AXI_ADC deleted file mode 100644 index e98b407ac85f87ca0bb365ca3e297ff949ded4c8..0000000000000000000000000000000000000000 --- a/anolis/configs/L2-OPTIONAL/x86/CONFIG_ADI_AXI_ADC +++ /dev/null @@ -1 +0,0 @@ -# CONFIG_ADI_AXI_ADC is not set diff --git a/anolis/configs/L2-OPTIONAL/x86/CONFIG_ARCH_CONFIGURES_CPU_MITIGATIONS b/anolis/configs/L2-OPTIONAL/x86/CONFIG_ARCH_CONFIGURES_CPU_MITIGATIONS deleted file mode 100644 index a7a95432397c43ae047c217d72b1167005737b06..0000000000000000000000000000000000000000 --- a/anolis/configs/L2-OPTIONAL/x86/CONFIG_ARCH_CONFIGURES_CPU_MITIGATIONS +++ /dev/null @@ -1 +0,0 @@ -CONFIG_ARCH_CONFIGURES_CPU_MITIGATIONS=y diff --git a/anolis/configs/L2-OPTIONAL/x86/CONFIG_HAVE_LIVEPATCH b/anolis/configs/L2-OPTIONAL/x86/CONFIG_HAVE_LIVEPATCH new file mode 100644 index 0000000000000000000000000000000000000000..7ebdb924703e83bf53d1cf65d694cc6c3f2435ee --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/x86/CONFIG_HAVE_LIVEPATCH @@ -0,0 +1 @@ +CONFIG_HAVE_LIVEPATCH=y diff --git a/anolis/configs/L2-OPTIONAL/x86/CONFIG_HAVE_RELIABLE_STACKTRACE b/anolis/configs/L2-OPTIONAL/x86/CONFIG_HAVE_RELIABLE_STACKTRACE new file mode 100644 index 0000000000000000000000000000000000000000..2ce8faabc4cf8d22f2e1240b50fe5779c48d7bfb --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/x86/CONFIG_HAVE_RELIABLE_STACKTRACE @@ -0,0 +1 @@ +CONFIG_HAVE_RELIABLE_STACKTRACE=y diff --git a/anolis/configs/L2-OPTIONAL/x86/CONFIG_HAVE_STACK_VALIDATION b/anolis/configs/L2-OPTIONAL/x86/CONFIG_HAVE_STACK_VALIDATION new file mode 100644 index 0000000000000000000000000000000000000000..6f36a32d84ae3d13712676f7140d81a67343ce55 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/x86/CONFIG_HAVE_STACK_VALIDATION @@ -0,0 +1 @@ +CONFIG_HAVE_STACK_VALIDATION=y diff --git a/anolis/configs/L2-OPTIONAL/x86/CONFIG_OBJTOOL b/anolis/configs/L2-OPTIONAL/x86/CONFIG_OBJTOOL new file mode 100644 index 0000000000000000000000000000000000000000..cf3a9f20f93d2cfe8baa006f1f94c41c819e9162 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/x86/CONFIG_OBJTOOL @@ -0,0 +1 @@ +CONFIG_OBJTOOL=y diff --git a/include/asm-generic/orc_lookup.h b/arch/arm64/include/asm/orc_lookup.h similarity index 100% rename from include/asm-generic/orc_lookup.h rename to arch/arm64/include/asm/orc_lookup.h diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 7c67e2f29206604a8392c984c018e43d70766944..f9439f96f0ec3039b986051675a84e010461b9e6 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_ARM64_MTE) += mte.o obj-y += vdso-wrap.o obj-$(CONFIG_COMPAT_VDSO) += vdso32-wrap.o obj-$(CONFIG_UNWIND_PATCH_PAC_INTO_SCS) += patch-scs.o +obj-$(CONFIG_UNWINDER_ORC) += orc_lookup.o CFLAGS_patch-scs.o += -mbranch-protection=none # Force dependency (vdso*-wrap.S includes vdso.so through incbin) diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c index 09251ad0ff4e205848ee5f4de76c62b530debf73..8f19c7a7d65d5ee9cbdb83444d79cdd68b0b9d45 100644 --- a/arch/arm64/kernel/module.c +++ b/arch/arm64/kernel/module.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include static u64 module_direct_base __ro_after_init = 0; static u64 module_plt_base __ro_after_init = 0; diff --git a/kernel/orc_lookup.c b/arch/arm64/kernel/orc_lookup.c similarity index 99% rename from kernel/orc_lookup.c rename to arch/arm64/kernel/orc_lookup.c index ad845da546b4cd51c8a810c4c6df8d27c8000921..9c062c054dcba047303e0098a53665b328bd9bfe 100644 --- a/kernel/orc_lookup.c +++ b/arch/arm64/kernel/orc_lookup.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include bool orc_init __ro_after_init; static unsigned int lookup_num_blocks __ro_after_init; diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index e7d0cb515d9ce3dbe14c02430b36505ecb8a1e4c..80960d97d55fc5c8afac2725bc3c6cb0df9037f0 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -53,7 +53,7 @@ #include #include #include -#include +#include static int num_standard_resources; static struct resource *standard_resources; diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index 71157c0eb77b9d35d404da4355f4207262354a77..ab9605aa721db0abf3c0e76a7ee997b8c4115136 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -6,7 +6,7 @@ */ #include #include -#include +#include #include #include #include diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index 7cf905d551afc5945fbb12ad07706eae07d44f77..031a5272b91bcd261dc51285a53568d8dcde21e2 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -61,7 +61,7 @@ #define RUNTIME_DISCARD_EXIT #include -#include +#include #include #include #include diff --git a/arch/x86/coco/tdx/tdcall.S b/arch/x86/coco/tdx/tdcall.S index b021281958fe29a556eb4fee94546a9e673a1195..2eca5f43734feb9aa93cddc18800201d6367934e 100644 --- a/arch/x86/coco/tdx/tdcall.S +++ b/arch/x86/coco/tdx/tdcall.S @@ -7,7 +7,6 @@ #include #include #include -#include #include "../../virt/vmx/tdx/tdxcall.S" diff --git a/arch/x86/entry/entry.S b/arch/x86/entry/entry.S index f6f6af24e569d6c1c0f0fee68c3102fe723baca1..718c00367f9a5a3d0f1bee68b9a18f7ae8067c26 100644 --- a/arch/x86/entry/entry.S +++ b/arch/x86/entry/entry.S @@ -4,7 +4,6 @@ */ #include -#include #include #include #include diff --git a/arch/x86/include/asm/orc_lookup.h b/arch/x86/include/asm/orc_lookup.h new file mode 100644 index 0000000000000000000000000000000000000000..241631282e43e7a28cf53af8867656d29c81e24d --- /dev/null +++ b/arch/x86/include/asm/orc_lookup.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2017 Josh Poimboeuf + */ +#ifndef _ORC_LOOKUP_H +#define _ORC_LOOKUP_H + +/* + * This is a lookup table for speeding up access to the .orc_unwind table. + * Given an input address offset, the corresponding lookup table entry + * specifies a subset of the .orc_unwind table to search. + * + * Each block represents the end of the previous range and the start of the + * next range. An extra block is added to give the last range an end. + * + * The block size should be a power of 2 to avoid a costly 'div' instruction. + * + * A block size of 256 was chosen because it roughly doubles unwinder + * performance while only adding ~5% to the ORC data footprint. + */ +#define LOOKUP_BLOCK_ORDER 8 +#define LOOKUP_BLOCK_SIZE (1 << LOOKUP_BLOCK_ORDER) + +#ifndef LINKER_SCRIPT + +extern unsigned int orc_lookup[]; +extern unsigned int orc_lookup_end[]; + +#define LOOKUP_START_IP (unsigned long)_stext +#define LOOKUP_STOP_IP (unsigned long)_etext + +#endif /* LINKER_SCRIPT */ + +#endif /* _ORC_LOOKUP_H */ diff --git a/arch/x86/include/asm/orc_types.h b/arch/x86/include/asm/orc_types.h index 45f21662ac21b333e9de6d44085951569f256044..46d7e06763c9f53afa383ee4dd2a46dc534cd02a 100644 --- a/arch/x86/include/asm/orc_types.h +++ b/arch/x86/include/asm/orc_types.h @@ -8,13 +8,6 @@ #include #include -#include - -/* - * For x86, use the appripriate name for the frame pointer in orc_entry. - */ -#define bp_offset fp_offset -#define bp_reg fp_reg /* * The ORC_REG_* registers are base registers which are used to find other @@ -52,4 +45,34 @@ #define ORC_TYPE_REGS 3 #define ORC_TYPE_REGS_PARTIAL 4 +#ifndef __ASSEMBLY__ +#include + +/* + * This struct is more or less a vastly simplified version of the DWARF Call + * Frame Information standard. It contains only the necessary parts of DWARF + * CFI, simplified for ease of access by the in-kernel unwinder. It tells the + * unwinder how to find the previous SP and BP (and sometimes entry regs) on + * the stack for a given code address. Each instance of the struct corresponds + * to one or more code locations. + */ +struct orc_entry { + s16 sp_offset; + s16 bp_offset; +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned sp_reg:4; + unsigned bp_reg:4; + unsigned type:3; + unsigned signal:1; +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned bp_reg:4; + unsigned sp_reg:4; + unsigned unused:4; + unsigned signal:1; + unsigned type:3; +#endif +} __packed; + +#endif /* __ASSEMBLY__ */ + #endif /* _ORC_TYPES_H */ diff --git a/arch/x86/include/asm/unwind.h b/arch/x86/include/asm/unwind.h index 71af8246c69e6ffdc0ae1236a860ac4669a3094f..7cede4dc21f00326a48e941ba0f9596e06a5a568 100644 --- a/arch/x86/include/asm/unwind.h +++ b/arch/x86/include/asm/unwind.h @@ -94,8 +94,13 @@ static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state, #ifdef CONFIG_UNWINDER_ORC void unwind_init(void); +void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size, + void *orc, size_t orc_size); #else static inline void unwind_init(void) {} +static inline +void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size, + void *orc, size_t orc_size) {} #endif static inline diff --git a/arch/x86/include/asm/unwind_hints.h b/arch/x86/include/asm/unwind_hints.h index 196b82f806e46a05cc58e806988834f9db1c2ee8..85cc57cb65392e197ef8e6cc79d812890004c386 100644 --- a/arch/x86/include/asm/unwind_hints.h +++ b/arch/x86/include/asm/unwind_hints.h @@ -1,75 +1,10 @@ #ifndef _ASM_X86_UNWIND_HINTS_H #define _ASM_X86_UNWIND_HINTS_H -#include +#include #include "orc_types.h" -#ifdef CONFIG_OBJTOOL - -#ifndef __ASSEMBLY__ - -#define UNWIND_HINT(type, sp_reg, sp_offset, signal) \ - "987: \n\t" \ - ".pushsection .discard.unwind_hints\n\t" \ - /* struct unwind_hint */ \ - ".long 987b - .\n\t" \ - ".short " __stringify(sp_offset) "\n\t" \ - ".byte " __stringify(sp_reg) "\n\t" \ - ".byte " __stringify(type) "\n\t" \ - ".byte " __stringify(signal) "\n\t" \ - ".balign 4 \n\t" \ - ".popsection\n\t" - -#else /* __ASSEMBLY__ */ - -/* - * In asm, there are two kinds of code: normal C-type callable functions and - * the rest. The normal callable functions can be called by other code, and - * don't do anything unusual with the stack. Such normal callable functions - * are annotated with the ENTRY/ENDPROC macros. Most asm code falls in this - * category. In this case, no special debugging annotations are needed because - * objtool can automatically generate the ORC data for the ORC unwinder to read - * at runtime. - * - * Anything which doesn't fall into the above category, such as syscall and - * interrupt handlers, tends to not be called directly by other functions, and - * often does unusual non-C-function-type things with the stack pointer. Such - * code needs to be annotated such that objtool can understand it. The - * following CFI hint macros are for this type of code. - * - * These macros provide hints to objtool about the state of the stack at each - * instruction. Objtool starts from the hints and follows the code flow, - * making automatic CFI adjustments when it sees pushes and pops, filling out - * the debuginfo as necessary. It will also warn if it sees any - * inconsistencies. - */ -.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 -.Lhere_\@: - .pushsection .discard.unwind_hints - /* struct unwind_hint */ - .long .Lhere_\@ - . - .short \sp_offset - .byte \sp_reg - .byte \type - .byte \signal - .balign 4 - .popsection -.endm - -#endif /* __ASSEMBLY__ */ - -#else /* !CONFIG_OBJTOOL */ - -#ifndef __ASSEMBLY__ -#define UNWIND_HINT(type, sp_reg, sp_offset, signal) "\n\t" -#else -.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 -.endm -#endif - -#endif /* CONFIG_OBJTOOL */ - #ifdef __ASSEMBLY__ .macro UNWIND_HINT_END_OF_STACK diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c index 2fc4411a22d9bb235db045afcd50fb048fe523d2..5f71a0cf4399a577e1235f6040a70fb26ee56346 100644 --- a/arch/x86/kernel/module.c +++ b/arch/x86/kernel/module.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #if 0 #define DEBUGP(fmt, ...) \ @@ -370,9 +370,8 @@ int module_finalize(const Elf_Ehdr *hdr, } if (orc && orc_ip) - orc_lookup_module_init(me, - (void *)orc_ip->sh_addr, orc_ip->sh_size, - (void *)orc->sh_addr, orc->sh_size); + unwind_module_init(me, (void *)orc_ip->sh_addr, orc_ip->sh_size, + (void *)orc->sh_addr, orc->sh_size); return 0; } diff --git a/arch/x86/kernel/unwind_orc.c b/arch/x86/kernel/unwind_orc.c index b5d27bd3d5154d157f7763dbebb9cc7620d08e09..7e574cf3bf8a29dd31317aea9c829700dcd451fd 100644 --- a/arch/x86/kernel/unwind_orc.c +++ b/arch/x86/kernel/unwind_orc.c @@ -1,18 +1,39 @@ // SPDX-License-Identifier: GPL-2.0-only -#include #include +#include #include #include #include #include -#include #include +#include #include -#include ORC_HEADER; +#define orc_warn(fmt, ...) \ + printk_deferred_once(KERN_WARNING "WARNING: " fmt, ##__VA_ARGS__) + +#define orc_warn_current(args...) \ +({ \ + static bool dumped_before; \ + if (state->task == current && !state->error) { \ + orc_warn(args); \ + if (unwind_debug && !dumped_before) { \ + dumped_before = true; \ + unwind_dump(state); \ + } \ + } \ +}) + +extern int __start_orc_unwind_ip[]; +extern int __stop_orc_unwind_ip[]; +extern struct orc_entry __start_orc_unwind[]; +extern struct orc_entry __stop_orc_unwind[]; + +static bool orc_init __ro_after_init; static bool unwind_debug __ro_after_init; +static unsigned int lookup_num_blocks __ro_after_init; static int __init unwind_debug_cmdline(char *str) { @@ -54,9 +75,60 @@ static void unwind_dump(struct unwind_state *state) } } -#include +static inline unsigned long orc_ip(const int *ip) +{ + return (unsigned long)ip + *ip; +} + +static struct orc_entry *__orc_find(int *ip_table, struct orc_entry *u_table, + unsigned int num_entries, unsigned long ip) +{ + int *first = ip_table; + int *last = ip_table + num_entries - 1; + int *mid = first, *found = first; + + if (!num_entries) + return NULL; + + /* + * Do a binary range search to find the rightmost duplicate of a given + * starting address. Some entries are section terminators which are + * "weak" entries for ensuring there are no gaps. They should be + * ignored when they conflict with a real entry. + */ + while (first <= last) { + mid = first + ((last - first) / 2); + + if (orc_ip(mid) <= ip) { + found = mid; + first = mid + 1; + } else + last = mid - 1; + } + + return u_table + (found - ip_table); +} + +#ifdef CONFIG_MODULES +static struct orc_entry *orc_module_find(unsigned long ip) +{ + struct module *mod; + + mod = __module_address(ip); + if (!mod || !mod->arch.orc_unwind || !mod->arch.orc_unwind_ip) + return NULL; + return __orc_find(mod->arch.orc_unwind_ip, mod->arch.orc_unwind, + mod->arch.num_orcs, ip); +} +#else +static struct orc_entry *orc_module_find(unsigned long ip) +{ + return NULL; +} +#endif #ifdef CONFIG_DYNAMIC_FTRACE +static struct orc_entry *orc_find(unsigned long ip); /* * Ftrace dynamic trampolines do not have orc entries of their own. @@ -100,10 +172,19 @@ static struct orc_entry *orc_ftrace_find(unsigned long ip) } #endif -struct orc_entry *arch_orc_find(unsigned long ip) -{ - return orc_ftrace_find(ip); -} +/* + * If we crash with IP==0, the last successfully executed instruction + * was probably an indirect function call with a NULL function pointer, + * and we don't have unwind information for NULL. + * This hardcoded ORC entry for IP==0 allows us to unwind from a NULL function + * pointer into its parent and then continue normally from there. + */ +static struct orc_entry null_orc_entry = { + .sp_offset = sizeof(long), + .sp_reg = ORC_REG_SP, + .bp_reg = ORC_REG_UNDEFINED, + .type = ORC_TYPE_CALL +}; /* Fake frame pointer entry -- used as a fallback for generated code */ static struct orc_entry orc_fp_entry = { @@ -114,9 +195,170 @@ static struct orc_entry orc_fp_entry = { .bp_offset = -16, }; +static struct orc_entry *orc_find(unsigned long ip) +{ + static struct orc_entry *orc; + + if (ip == 0) + return &null_orc_entry; + + /* For non-init vmlinux addresses, use the fast lookup table: */ + if (ip >= LOOKUP_START_IP && ip < LOOKUP_STOP_IP) { + unsigned int idx, start, stop; + + idx = (ip - LOOKUP_START_IP) / LOOKUP_BLOCK_SIZE; + + if (unlikely((idx >= lookup_num_blocks-1))) { + orc_warn("WARNING: bad lookup idx: idx=%u num=%u ip=%pB\n", + idx, lookup_num_blocks, (void *)ip); + return NULL; + } + + start = orc_lookup[idx]; + stop = orc_lookup[idx + 1] + 1; + + if (unlikely((__start_orc_unwind + start >= __stop_orc_unwind) || + (__start_orc_unwind + stop > __stop_orc_unwind))) { + orc_warn("WARNING: bad lookup value: idx=%u num=%u start=%u stop=%u ip=%pB\n", + idx, lookup_num_blocks, start, stop, (void *)ip); + return NULL; + } + + return __orc_find(__start_orc_unwind_ip + start, + __start_orc_unwind + start, stop - start, ip); + } + + /* vmlinux .init slow lookup: */ + if (is_kernel_inittext(ip)) + return __orc_find(__start_orc_unwind_ip, __start_orc_unwind, + __stop_orc_unwind_ip - __start_orc_unwind_ip, ip); + + /* Module lookup: */ + orc = orc_module_find(ip); + if (orc) + return orc; + + return orc_ftrace_find(ip); +} + +#ifdef CONFIG_MODULES + +static DEFINE_MUTEX(sort_mutex); +static int *cur_orc_ip_table = __start_orc_unwind_ip; +static struct orc_entry *cur_orc_table = __start_orc_unwind; + +static void orc_sort_swap(void *_a, void *_b, int size) +{ + struct orc_entry *orc_a, *orc_b; + int *a = _a, *b = _b, tmp; + int delta = _b - _a; + + /* Swap the .orc_unwind_ip entries: */ + tmp = *a; + *a = *b + delta; + *b = tmp - delta; + + /* Swap the corresponding .orc_unwind entries: */ + orc_a = cur_orc_table + (a - cur_orc_ip_table); + orc_b = cur_orc_table + (b - cur_orc_ip_table); + swap(*orc_a, *orc_b); +} + +static int orc_sort_cmp(const void *_a, const void *_b) +{ + struct orc_entry *orc_a; + const int *a = _a, *b = _b; + unsigned long a_val = orc_ip(a); + unsigned long b_val = orc_ip(b); + + if (a_val > b_val) + return 1; + if (a_val < b_val) + return -1; + + /* + * The "weak" section terminator entries need to always be first + * to ensure the lookup code skips them in favor of real entries. + * These terminator entries exist to handle any gaps created by + * whitelisted .o files which didn't get objtool generation. + */ + orc_a = cur_orc_table + (a - cur_orc_ip_table); + return orc_a->type == ORC_TYPE_UNDEFINED ? -1 : 1; +} + +void unwind_module_init(struct module *mod, void *_orc_ip, size_t orc_ip_size, + void *_orc, size_t orc_size) +{ + int *orc_ip = _orc_ip; + struct orc_entry *orc = _orc; + unsigned int num_entries = orc_ip_size / sizeof(int); + + WARN_ON_ONCE(orc_ip_size % sizeof(int) != 0 || + orc_size % sizeof(*orc) != 0 || + num_entries != orc_size / sizeof(*orc)); + + /* + * The 'cur_orc_*' globals allow the orc_sort_swap() callback to + * associate an .orc_unwind_ip table entry with its corresponding + * .orc_unwind entry so they can both be swapped. + */ + mutex_lock(&sort_mutex); + cur_orc_ip_table = orc_ip; + cur_orc_table = orc; + sort(orc_ip, num_entries, sizeof(int), orc_sort_cmp, orc_sort_swap); + mutex_unlock(&sort_mutex); + + mod->arch.orc_unwind_ip = orc_ip; + mod->arch.orc_unwind = orc; + mod->arch.num_orcs = num_entries; +} +#endif + void __init unwind_init(void) { - orc_lookup_init(); + size_t orc_ip_size = (void *)__stop_orc_unwind_ip - (void *)__start_orc_unwind_ip; + size_t orc_size = (void *)__stop_orc_unwind - (void *)__start_orc_unwind; + size_t num_entries = orc_ip_size / sizeof(int); + struct orc_entry *orc; + int i; + + if (!num_entries || orc_ip_size % sizeof(int) != 0 || + orc_size % sizeof(struct orc_entry) != 0 || + num_entries != orc_size / sizeof(struct orc_entry)) { + orc_warn("WARNING: Bad or missing .orc_unwind table. Disabling unwinder.\n"); + return; + } + + /* + * Note, the orc_unwind and orc_unwind_ip tables were already + * sorted at build time via the 'sorttable' tool. + * It's ready for binary search straight away, no need to sort it. + */ + + /* Initialize the fast lookup table: */ + lookup_num_blocks = orc_lookup_end - orc_lookup; + for (i = 0; i < lookup_num_blocks-1; i++) { + orc = __orc_find(__start_orc_unwind_ip, __start_orc_unwind, + num_entries, + LOOKUP_START_IP + (LOOKUP_BLOCK_SIZE * i)); + if (!orc) { + orc_warn("WARNING: Corrupt .orc_unwind table. Disabling unwinder.\n"); + return; + } + + orc_lookup[i] = orc - __start_orc_unwind; + } + + /* Initialize the ending block: */ + orc = __orc_find(__start_orc_unwind_ip, __start_orc_unwind, num_entries, + LOOKUP_STOP_IP); + if (!orc) { + orc_warn("WARNING: Corrupt .orc_unwind table. Disabling unwinder.\n"); + return; + } + orc_lookup[lookup_num_blocks-1] = orc - __start_orc_unwind; + + orc_init = true; } unsigned long unwind_get_return_address(struct unwind_state *state) diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S index 7e7b7846c632539dd556dfa7ad64eb5db44e1e72..54a5596adaa61e2fd9bd47ca086e4a985d5a9d82 100644 --- a/arch/x86/kernel/vmlinux.lds.S +++ b/arch/x86/kernel/vmlinux.lds.S @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/include/linux/objtool.h b/include/linux/objtool.h index 865caa2d1232388cafb80db9bd45c11ff9a3ce1f..ac959bbbffd55fbd4f9dc90e23bbc1252a6d16be 100644 --- a/include/linux/objtool.h +++ b/include/linux/objtool.h @@ -12,6 +12,18 @@ #ifndef __ASSEMBLY__ +#define UNWIND_HINT(type, sp_reg, sp_offset, signal) \ + "987: \n\t" \ + ".pushsection .discard.unwind_hints\n\t" \ + /* struct unwind_hint */ \ + ".long 987b - .\n\t" \ + ".short " __stringify(sp_offset) "\n\t" \ + ".byte " __stringify(sp_reg) "\n\t" \ + ".byte " __stringify(type) "\n\t" \ + ".byte " __stringify(signal) "\n\t" \ + ".balign 4 \n\t" \ + ".popsection\n\t" + /* * This macro marks the given function's stack frame as "non-standard", which * tells objtool to ignore the function when doing stack metadata validation. @@ -59,6 +71,40 @@ .long 999b; \ .popsection; +/* + * In asm, there are two kinds of code: normal C-type callable functions and + * the rest. The normal callable functions can be called by other code, and + * don't do anything unusual with the stack. Such normal callable functions + * are annotated with the ENTRY/ENDPROC macros. Most asm code falls in this + * category. In this case, no special debugging annotations are needed because + * objtool can automatically generate the ORC data for the ORC unwinder to read + * at runtime. + * + * Anything which doesn't fall into the above category, such as syscall and + * interrupt handlers, tends to not be called directly by other functions, and + * often does unusual non-C-function-type things with the stack pointer. Such + * code needs to be annotated such that objtool can understand it. The + * following CFI hint macros are for this type of code. + * + * These macros provide hints to objtool about the state of the stack at each + * instruction. Objtool starts from the hints and follows the code flow, + * making automatic CFI adjustments when it sees pushes and pops, filling out + * the debuginfo as necessary. It will also warn if it sees any + * inconsistencies. + */ +.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 +.Lhere_\@: + .pushsection .discard.unwind_hints + /* struct unwind_hint */ + .long .Lhere_\@ - . + .short \sp_offset + .byte \sp_reg + .byte \type + .byte \signal + .balign 4 + .popsection +.endm + .macro STACK_FRAME_NON_STANDARD func:req .pushsection .discard.func_stack_frame_non_standard, "aw" .long \func - . @@ -108,12 +154,15 @@ #ifndef __ASSEMBLY__ +#define UNWIND_HINT(type, sp_reg, sp_offset, signal) "\n\t" #define STACK_FRAME_NON_STANDARD(func) #define STACK_FRAME_NON_STANDARD_FP(func) #define ANNOTATE_NOENDBR #define ASM_REACHABLE #else #define ANNOTATE_INTRA_FUNCTION_CALL +.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 +.endm .macro STACK_FRAME_NON_STANDARD func:req .endm .macro ANNOTATE_NOENDBR diff --git a/kernel/Makefile b/kernel/Makefile index 680be35dffc6400fed6bca72237ddf26cbeb0394..ce105a5558fcfadafdc04aeefeefa6231f18008f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -132,8 +132,6 @@ obj-$(CONFIG_WATCH_QUEUE) += watch_queue.o obj-$(CONFIG_RESOURCE_KUNIT_TEST) += resource_kunit.o obj-$(CONFIG_SYSCTL_KUNIT_TEST) += sysctl-test.o -obj-$(CONFIG_UNWINDER_ORC) += orc_lookup.o - CFLAGS_stackleak.o += $(DISABLE_STACKLEAK_PLUGIN) obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak.o KASAN_SANITIZE_stackleak.o := n diff --git a/tools/arch/x86/include/asm/orc_types.h b/tools/arch/x86/include/asm/orc_types.h index 45f21662ac21b333e9de6d44085951569f256044..46d7e06763c9f53afa383ee4dd2a46dc534cd02a 100644 --- a/tools/arch/x86/include/asm/orc_types.h +++ b/tools/arch/x86/include/asm/orc_types.h @@ -8,13 +8,6 @@ #include #include -#include - -/* - * For x86, use the appripriate name for the frame pointer in orc_entry. - */ -#define bp_offset fp_offset -#define bp_reg fp_reg /* * The ORC_REG_* registers are base registers which are used to find other @@ -52,4 +45,34 @@ #define ORC_TYPE_REGS 3 #define ORC_TYPE_REGS_PARTIAL 4 +#ifndef __ASSEMBLY__ +#include + +/* + * This struct is more or less a vastly simplified version of the DWARF Call + * Frame Information standard. It contains only the necessary parts of DWARF + * CFI, simplified for ease of access by the in-kernel unwinder. It tells the + * unwinder how to find the previous SP and BP (and sometimes entry regs) on + * the stack for a given code address. Each instance of the struct corresponds + * to one or more code locations. + */ +struct orc_entry { + s16 sp_offset; + s16 bp_offset; +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned sp_reg:4; + unsigned bp_reg:4; + unsigned type:3; + unsigned signal:1; +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned bp_reg:4; + unsigned sp_reg:4; + unsigned unused:4; + unsigned signal:1; + unsigned type:3; +#endif +} __packed; + +#endif /* __ASSEMBLY__ */ + #endif /* _ORC_TYPES_H */ diff --git a/tools/arch/x86/include/asm/unwind_hints.h b/tools/arch/x86/include/asm/unwind_hints.h deleted file mode 100644 index 196b82f806e46a05cc58e806988834f9db1c2ee8..0000000000000000000000000000000000000000 --- a/tools/arch/x86/include/asm/unwind_hints.h +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef _ASM_X86_UNWIND_HINTS_H -#define _ASM_X86_UNWIND_HINTS_H - -#include - -#include "orc_types.h" - -#ifdef CONFIG_OBJTOOL - -#ifndef __ASSEMBLY__ - -#define UNWIND_HINT(type, sp_reg, sp_offset, signal) \ - "987: \n\t" \ - ".pushsection .discard.unwind_hints\n\t" \ - /* struct unwind_hint */ \ - ".long 987b - .\n\t" \ - ".short " __stringify(sp_offset) "\n\t" \ - ".byte " __stringify(sp_reg) "\n\t" \ - ".byte " __stringify(type) "\n\t" \ - ".byte " __stringify(signal) "\n\t" \ - ".balign 4 \n\t" \ - ".popsection\n\t" - -#else /* __ASSEMBLY__ */ - -/* - * In asm, there are two kinds of code: normal C-type callable functions and - * the rest. The normal callable functions can be called by other code, and - * don't do anything unusual with the stack. Such normal callable functions - * are annotated with the ENTRY/ENDPROC macros. Most asm code falls in this - * category. In this case, no special debugging annotations are needed because - * objtool can automatically generate the ORC data for the ORC unwinder to read - * at runtime. - * - * Anything which doesn't fall into the above category, such as syscall and - * interrupt handlers, tends to not be called directly by other functions, and - * often does unusual non-C-function-type things with the stack pointer. Such - * code needs to be annotated such that objtool can understand it. The - * following CFI hint macros are for this type of code. - * - * These macros provide hints to objtool about the state of the stack at each - * instruction. Objtool starts from the hints and follows the code flow, - * making automatic CFI adjustments when it sees pushes and pops, filling out - * the debuginfo as necessary. It will also warn if it sees any - * inconsistencies. - */ -.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 -.Lhere_\@: - .pushsection .discard.unwind_hints - /* struct unwind_hint */ - .long .Lhere_\@ - . - .short \sp_offset - .byte \sp_reg - .byte \type - .byte \signal - .balign 4 - .popsection -.endm - -#endif /* __ASSEMBLY__ */ - -#else /* !CONFIG_OBJTOOL */ - -#ifndef __ASSEMBLY__ -#define UNWIND_HINT(type, sp_reg, sp_offset, signal) "\n\t" -#else -.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 -.endm -#endif - -#endif /* CONFIG_OBJTOOL */ - -#ifdef __ASSEMBLY__ - -.macro UNWIND_HINT_END_OF_STACK - UNWIND_HINT type=UNWIND_HINT_TYPE_END_OF_STACK -.endm - -.macro UNWIND_HINT_UNDEFINED - UNWIND_HINT type=UNWIND_HINT_TYPE_UNDEFINED -.endm - -.macro UNWIND_HINT_ENTRY - VALIDATE_UNRET_BEGIN - UNWIND_HINT_END_OF_STACK -.endm - -.macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 partial=0 signal=1 - .if \base == %rsp - .if \indirect - .set sp_reg, ORC_REG_SP_INDIRECT - .else - .set sp_reg, ORC_REG_SP - .endif - .elseif \base == %rbp - .set sp_reg, ORC_REG_BP - .elseif \base == %rdi - .set sp_reg, ORC_REG_DI - .elseif \base == %rdx - .set sp_reg, ORC_REG_DX - .elseif \base == %r10 - .set sp_reg, ORC_REG_R10 - .else - .error "UNWIND_HINT_REGS: bad base register" - .endif - - .set sp_offset, \offset - - .if \partial - .set type, UNWIND_HINT_TYPE_REGS_PARTIAL - .elseif \extra == 0 - .set type, UNWIND_HINT_TYPE_REGS_PARTIAL - .set sp_offset, \offset + (16*8) - .else - .set type, UNWIND_HINT_TYPE_REGS - .endif - - UNWIND_HINT sp_reg=sp_reg sp_offset=sp_offset type=type signal=\signal -.endm - -.macro UNWIND_HINT_IRET_REGS base=%rsp offset=0 signal=1 - UNWIND_HINT_REGS base=\base offset=\offset partial=1 signal=\signal -.endm - -.macro UNWIND_HINT_IRET_ENTRY base=%rsp offset=0 signal=1 - VALIDATE_UNRET_BEGIN - UNWIND_HINT_IRET_REGS base=\base offset=\offset signal=\signal -.endm - -.macro UNWIND_HINT_FUNC - UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=8 type=UNWIND_HINT_TYPE_FUNC -.endm - -.macro UNWIND_HINT_SAVE - UNWIND_HINT type=UNWIND_HINT_TYPE_SAVE -.endm - -.macro UNWIND_HINT_RESTORE - UNWIND_HINT type=UNWIND_HINT_TYPE_RESTORE -.endm - -#else - -#define UNWIND_HINT_UNDEFINED \ - UNWIND_HINT(UNWIND_HINT_TYPE_UNDEFINED, 0, 0, 0) - -#define UNWIND_HINT_FUNC \ - UNWIND_HINT(UNWIND_HINT_TYPE_FUNC, ORC_REG_SP, 8, 0) - -#define UNWIND_HINT_SAVE \ - UNWIND_HINT(UNWIND_HINT_TYPE_SAVE, 0, 0, 0) - -#define UNWIND_HINT_RESTORE \ - UNWIND_HINT(UNWIND_HINT_TYPE_RESTORE, 0, 0, 0) - -#endif /* __ASSEMBLY__ */ - -#endif /* _ASM_X86_UNWIND_HINTS_H */ diff --git a/tools/objtool/Build b/tools/objtool/Build index 9da7ebdae86c815425e58ece78b4e835f57d1ef2..b71547b660cedba1fc6c89714bd203bfb784328a 100644 --- a/tools/objtool/Build +++ b/tools/objtool/Build @@ -6,10 +6,6 @@ objtool-$(STATIC_CHECK) += check.o objtool-$(STATIC_CHECK) += special.o objtool-$(DYNAMIC_CHECK) += dcheck.o objtool-y += builtin-check.o -objtool-y += cfi.o -objtool-y += insn.o -objtool-y += decode.o -objtool-y += unwind_hints.o objtool-y += elf.o objtool-y += objtool.o diff --git a/tools/objtool/arch/arm64/Build b/tools/objtool/arch/arm64/Build index 8615abfb12cf6e7e734b6aa61017ea719f6e6f17..8d2f99a5b1ab70ee9229692906fa605619b31a87 100644 --- a/tools/objtool/arch/arm64/Build +++ b/tools/objtool/arch/arm64/Build @@ -1,2 +1,5 @@ objtool-y += decode.o objtool-y += orc.o +objtool-y += unwind_hints.o +objtool-y += insn.o +objtool-y += cfi.o diff --git a/tools/objtool/cfi.c b/tools/objtool/arch/arm64/cfi.c similarity index 99% rename from tools/objtool/cfi.c rename to tools/objtool/arch/arm64/cfi.c index bc3e216f1a94b1b12fbcc4c4b58105c9fd2d20a6..753aa82d20c84bb399756d663b98d95a38cab9b3 100644 --- a/tools/objtool/cfi.c +++ b/tools/objtool/arch/arm64/cfi.c @@ -8,6 +8,7 @@ #include #include +#include #include #include diff --git a/tools/objtool/arch/arm64/decode.c b/tools/objtool/arch/arm64/decode.c index 19ef9b22a7349861f2e2f5759404967c8b465701..49d46feb6a8a22b2526dc0f57f7e381e585608e0 100644 --- a/tools/objtool/arch/arm64/decode.c +++ b/tools/objtool/arch/arm64/decode.c @@ -13,12 +13,15 @@ #include #include -#include +#include #include +#include #include #include +unsigned long nr_insns; + /* ARM64 instructions are all 4 bytes wide. */ #define INSN_SIZE 4 @@ -579,3 +582,128 @@ int arch_decode_instruction(struct objtool_file *file, insn->type = var.type; return 0; } + +/* + * Call the arch-specific instruction decoder for all the instructions and add + * them to the global instruction list. + */ +int decode_instructions(struct objtool_file *file) +{ + struct section *sec; + struct symbol *func; + unsigned long offset; + struct instruction *insn; + int ret; + + for_each_sec(file, sec) { + struct instruction *insns = NULL; + u8 prev_len = 0; + u8 idx = 0; + + if (!(sec->sh.sh_flags & SHF_EXECINSTR)) + continue; + + if (strcmp(sec->name, ".altinstr_replacement") && + strcmp(sec->name, ".altinstr_aux") && + strncmp(sec->name, ".discard.", 9)) + sec->text = true; + + if (!strcmp(sec->name, ".noinstr.text") || + !strcmp(sec->name, ".entry.text") || + !strcmp(sec->name, ".cpuidle.text") || + !strncmp(sec->name, ".text..__x86.", 13)) + sec->noinstr = true; + + /* + * .init.text code is ran before userspace and thus doesn't + * strictly need retpolines, except for modules which are + * loaded late, they very much do need retpoline in their + * .init.text + */ + if (!strcmp(sec->name, ".init.text") && !opts.module) + sec->init = true; + + for (offset = 0; offset < sec->sh.sh_size; offset += insn->len) { + if (!insns || idx == INSN_CHUNK_MAX) { + insns = calloc(sizeof(*insn), INSN_CHUNK_SIZE); + if (!insns) { + WARN("malloc failed"); + return -1; + } + idx = 0; + } else { + idx++; + } + insn = &insns[idx]; + insn->idx = idx; + + INIT_LIST_HEAD(&insn->call_node); + insn->sec = sec; + insn->offset = offset; + insn->prev_len = prev_len; + + ret = arch_decode_instruction(file, sec, offset, + sec->sh.sh_size - offset, + insn); + if (ret) + return ret; + + prev_len = insn->len; + + /* + * By default, "ud2" is a dead end unless otherwise + * annotated, because GCC 7 inserts it for certain + * divide-by-zero cases. + */ + if (insn->type == INSN_BUG) + insn->dead_end = true; + + hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset)); + nr_insns++; + } + +// printf("%s: last chunk used: %d\n", sec->name, (int)idx); + + sec_for_each_sym(sec, func) { + if (func->type != STT_NOTYPE && func->type != STT_FUNC) + continue; + + if (func->offset == sec->sh.sh_size) { + /* Heuristic: likely an "end" symbol */ + if (func->type == STT_NOTYPE) + continue; + WARN("%s(): STT_FUNC at end of section", + func->name); + return -1; + } + + if (func->embedded_insn || func->alias != func) + continue; + + if (!find_insn(file, sec, func->offset)) { + WARN("%s(): can't find starting instruction", + func->name); + return -1; + } + + sym_for_each_insn(file, func, insn) { + insn->sym = func; + if (func->type == STT_FUNC && + insn->type == INSN_ENDBR && + list_empty(&insn->call_node)) { + if (insn->offset == func->offset) { + list_add_tail(&insn->call_node, &file->endbr_list); + file->nr_endbr++; + } else { + file->nr_endbr_int++; + } + } + } + } + } + + if (opts.stats) + printf("nr_insns: %lu\n", nr_insns); + + return 0; +} diff --git a/tools/objtool/arch/arm64/include/arch/cfi.h b/tools/objtool/arch/arm64/include/arch/cfi.h new file mode 100644 index 0000000000000000000000000000000000000000..55f7a988d824cfa7407b9db0bc0480d45640376d --- /dev/null +++ b/tools/objtool/arch/arm64/include/arch/cfi.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2015-2017 Josh Poimboeuf + */ + +#ifndef _OBJTOOL_ARCH_CFI_H +#define _OBJTOOL_ARCH_CFI_H + +#include + +#include + +void init_cfi_state(struct cfi_state *cfi); +bool cficmp(struct cfi_state *cfi1, struct cfi_state *cfi2); +struct cfi_state *cfi_hash_find_or_add(struct cfi_state *cfi); +void cfi_hash_add(struct cfi_state *cfi); +void *cfi_hash_alloc(unsigned long size); +void set_func_state(struct cfi_state *state); + +extern unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache; +extern struct cfi_init_state initial_func_cfi; +extern struct cfi_state init_cfi; +extern struct cfi_state func_cfi; +extern struct cfi_state force_undefined_cfi; + +#endif /* _OBJTOOL_ARCH_CFI_H */ diff --git a/tools/objtool/arch/arm64/include/arch/orc.h b/tools/objtool/arch/arm64/include/arch/orc.h new file mode 100644 index 0000000000000000000000000000000000000000..24fc9cf4de974c575e74ada0f0dbf2115a38fa19 --- /dev/null +++ b/tools/objtool/arch/arm64/include/arch/orc.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2015-2017 Josh Poimboeuf + */ + +#ifndef _OBJTOOL_ORC_H +#define _OBJTOOL_ORC_H + +#include + +int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, + struct instruction *insn); +void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i); +int write_orc_entry(struct elf *elf, struct section *orc_sec, + struct section *ip_sec, unsigned int idx, + struct section *insn_sec, unsigned long insn_off, + struct orc_entry *o); +const char *orc_type_name(unsigned int type); +void orc_print_reg(unsigned int reg, int offset); +void orc_print_sp(void); +void orc_print_fp(void); + +#endif /* _OBJTOOL_ORC_H */ diff --git a/tools/objtool/insn.c b/tools/objtool/arch/arm64/insn.c similarity index 99% rename from tools/objtool/insn.c rename to tools/objtool/arch/arm64/insn.c index b63ec049696aadd5651b47d149c90470d06bc82c..b205868690c2f6fb6dc7ef030c7de1f5e8326c8a 100644 --- a/tools/objtool/insn.c +++ b/tools/objtool/arch/arm64/insn.c @@ -8,6 +8,7 @@ #include #include #include +#include struct instruction *find_insn(struct objtool_file *file, struct section *sec, unsigned long offset) diff --git a/tools/objtool/arch/arm64/orc.c b/tools/objtool/arch/arm64/orc.c index 98e930991ef163a252603c3159c2e924cad10e50..25fba97534a1fc76b025856f52dee6cf1c894245 100644 --- a/tools/objtool/arch/arm64/orc.c +++ b/tools/objtool/arch/arm64/orc.c @@ -9,7 +9,8 @@ #include #include -#include +#include +#include int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn) @@ -39,6 +40,27 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, return 0; } +int write_orc_entry(struct elf *elf, struct section *orc_sec, + struct section *ip_sec, unsigned int idx, + struct section *insn_sec, unsigned long insn_off, + struct orc_entry *o) +{ + struct orc_entry *orc; + + /* populate ORC data */ + orc = (struct orc_entry *)orc_sec->data->d_buf + idx; + memcpy(orc, o, sizeof(*orc)); + orc->sp_offset = bswap_if_needed(elf, orc->sp_offset); + orc->fp_offset = bswap_if_needed(elf, orc->fp_offset); + + /* populate reloc for ip */ + if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx, + insn_sec, insn_off)) + return -1; + + return 0; +} + static const char *reg_name(unsigned int reg) { switch (reg) { @@ -67,6 +89,21 @@ const char *orc_type_name(unsigned int type) } } +void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i) +{ + printf("type:%s", orc_type_name(orc[i].type)); + + printf(" sp:"); + + orc_print_reg(orc[i].sp_reg, bswap_if_needed(dummy_elf, orc[i].sp_offset)); + + printf(" fp:"); + + orc_print_reg(orc[i].fp_reg, bswap_if_needed(dummy_elf, orc[i].fp_offset)); + + printf(" signal:%d\n", orc[i].signal); +} + void orc_print_reg(unsigned int reg, int offset) { if (reg == ORC_REG_UNDEFINED) diff --git a/tools/objtool/unwind_hints.c b/tools/objtool/arch/arm64/unwind_hints.c similarity index 99% rename from tools/objtool/unwind_hints.c rename to tools/objtool/arch/arm64/unwind_hints.c index c59d259d03922fc9905d2896c498499b51fc490a..3e5364623defcdbda6014d66740b0f5e4a32c132 100644 --- a/tools/objtool/unwind_hints.c +++ b/tools/objtool/arch/arm64/unwind_hints.c @@ -8,6 +8,7 @@ #include #include #include +#include int read_unwind_hints(struct objtool_file *file) { diff --git a/tools/objtool/arch/x86/Build b/tools/objtool/arch/x86/Build index 77b9a66cd6da84248878c549a30f81ed9dde4f6e..3dedb2fd8f3a0c3d7502e2536167f5e895fe1d44 100644 --- a/tools/objtool/arch/x86/Build +++ b/tools/objtool/arch/x86/Build @@ -1,6 +1,6 @@ objtool-y += special.o objtool-y += decode.o -objtool-$(BUILD_ORC) += orc.o +objtool-y += orc.o inat_tables_script = ../arch/x86/tools/gen-insn-attr-x86.awk inat_tables_maps = ../arch/x86/lib/x86-opcode-map.txt diff --git a/tools/objtool/arch/x86/include/arch/elf.h b/tools/objtool/arch/x86/include/arch/elf.h index 39f23cb55352fd099430441c406814fab58d408b..7131f7f51a4e8c4109157295033f69417b8c49ea 100644 --- a/tools/objtool/arch/x86/include/arch/elf.h +++ b/tools/objtool/arch/x86/include/arch/elf.h @@ -9,6 +9,5 @@ #define R_DATA64 R_X86_64_PC32 #define R_TEXT32 R_X86_64_PC32 #define R_TEXT64 R_X86_64_PC32 -#define R_PCREL R_X86_64_PC32 #endif /* _OBJTOOL_ARCH_ELF */ diff --git a/tools/objtool/arch/x86/orc.c b/tools/objtool/arch/x86/orc.c index 3526dfd9749d7f8dbc0e3e85f8d62d8ac6bdc970..b6cd943e87f936ef93ce03609d4596b6fd96553a 100644 --- a/tools/objtool/arch/x86/orc.c +++ b/tools/objtool/arch/x86/orc.c @@ -1,21 +1,13 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2017 Josh Poimboeuf - */ - -#include -#include - +// SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include -#include #include #include #include -int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, - struct instruction *insn) +int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn) { struct cfi_reg *bp = &cfi->regs[CFI_BP]; @@ -105,6 +97,27 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, return 0; } +int write_orc_entry(struct elf *elf, struct section *orc_sec, + struct section *ip_sec, unsigned int idx, + struct section *insn_sec, unsigned long insn_off, + struct orc_entry *o) +{ + struct orc_entry *orc; + + /* populate ORC data */ + orc = (struct orc_entry *)orc_sec->data->d_buf + idx; + memcpy(orc, o, sizeof(*orc)); + orc->sp_offset = bswap_if_needed(elf, orc->sp_offset); + orc->bp_offset = bswap_if_needed(elf, orc->bp_offset); + + /* populate reloc for ip */ + if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx, + insn_sec, insn_off)) + return -1; + + return 0; +} + static const char *reg_name(unsigned int reg) { switch (reg) { @@ -131,7 +144,7 @@ static const char *reg_name(unsigned int reg) } } -const char *orc_type_name(unsigned int type) +static const char *orc_type_name(unsigned int type) { switch (type) { case ORC_TYPE_UNDEFINED: @@ -149,7 +162,7 @@ const char *orc_type_name(unsigned int type) } } -void orc_print_reg(unsigned int reg, int offset) +static void print_reg(unsigned int reg, int offset) { if (reg == ORC_REG_BP_INDIRECT) printf("(bp%+d)", offset); @@ -160,3 +173,16 @@ void orc_print_reg(unsigned int reg, int offset) else printf("%s%+d", reg_name(reg), offset); } + +void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i) +{ + printf("type:%s", orc_type_name(orc[i].type)); + + printf(" sp:"); + print_reg(orc[i].sp_reg, bswap_if_needed(dummy_elf, orc[i].sp_offset)); + + printf(" bp:"); + print_reg(orc[i].bp_reg, bswap_if_needed(dummy_elf, orc[i].bp_offset)); + + printf(" signal:%d\n", orc[i].signal); +} diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 68bc4d8b3ca55dedfe56f36c415f1bdb3cc877f9..e3fc263b1b2065a62f03dcf6e767f81eb047caf6 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -27,6 +27,110 @@ struct alternative { bool skip_orig; }; +static unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache; + +static struct cfi_init_state initial_func_cfi; +static struct cfi_state init_cfi; +static struct cfi_state func_cfi; +static struct cfi_state force_undefined_cfi; + +struct instruction *find_insn(struct objtool_file *file, + struct section *sec, unsigned long offset) +{ + struct instruction *insn; + + hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, offset)) { + if (insn->sec == sec && insn->offset == offset) + return insn; + } + + return NULL; +} + +struct instruction *next_insn_same_sec(struct objtool_file *file, + struct instruction *insn) +{ + if (insn->idx == INSN_CHUNK_MAX) + return find_insn(file, insn->sec, insn->offset + insn->len); + + insn++; + if (!insn->len) + return NULL; + + return insn; +} + +static struct instruction *next_insn_same_func(struct objtool_file *file, + struct instruction *insn) +{ + struct instruction *next = next_insn_same_sec(file, insn); + struct symbol *func = insn_func(insn); + + if (!func) + return NULL; + + if (next && insn_func(next) == func) + return next; + + /* Check if we're already in the subfunction: */ + if (func == func->cfunc) + return NULL; + + /* Move to the subfunction: */ + return find_insn(file, func->cfunc->sec, func->cfunc->offset); +} + +static struct instruction *prev_insn_same_sec(struct objtool_file *file, + struct instruction *insn) +{ + if (insn->idx == 0) { + if (insn->prev_len) + return find_insn(file, insn->sec, insn->offset - insn->prev_len); + return NULL; + } + + return insn - 1; +} + +static struct instruction *prev_insn_same_sym(struct objtool_file *file, + struct instruction *insn) +{ + struct instruction *prev = prev_insn_same_sec(file, insn); + + if (prev && insn_func(prev) == insn_func(insn)) + return prev; + + return NULL; +} + +#define for_each_insn(file, insn) \ + for (struct section *__sec, *__fake = (struct section *)1; \ + __fake; __fake = NULL) \ + for_each_sec(file, __sec) \ + sec_for_each_insn(file, __sec, insn) + +#define func_for_each_insn(file, func, insn) \ + for (insn = find_insn(file, func->sec, func->offset); \ + insn; \ + insn = next_insn_same_func(file, insn)) + +#define sym_for_each_insn(file, sym, insn) \ + for (insn = find_insn(file, sym->sec, sym->offset); \ + insn && insn->offset < sym->offset + sym->len; \ + insn = next_insn_same_sec(file, insn)) + +#define sym_for_each_insn_continue_reverse(file, sym, insn) \ + for (insn = prev_insn_same_sec(file, insn); \ + insn && insn->offset >= sym->offset; \ + insn = prev_insn_same_sec(file, insn)) + +#define sec_for_each_insn_from(file, insn) \ + for (; insn; insn = next_insn_same_sec(file, insn)) + +#define sec_for_each_insn_continue(file, insn) \ + for (insn = next_insn_same_sec(file, insn); insn; \ + insn = next_insn_same_sec(file, insn)) + static inline struct symbol *insn_call_dest(struct instruction *insn) { if (insn->type == INSN_JUMP_DYNAMIC || @@ -157,8 +261,231 @@ static bool dead_end_function(struct objtool_file *file, struct symbol *func) return __dead_end_function(file, func, 0); } +static void init_cfi_state(struct cfi_state *cfi) +{ + int i; + + for (i = 0; i < CFI_NUM_REGS; i++) { + cfi->regs[i].base = CFI_UNDEFINED; + cfi->vals[i].base = CFI_UNDEFINED; + } + cfi->cfa.base = CFI_UNDEFINED; + cfi->drap_reg = CFI_UNDEFINED; + cfi->drap_offset = -1; +} + +static void init_insn_state(struct objtool_file *file, struct insn_state *state, + struct section *sec) +{ + memset(state, 0, sizeof(*state)); + init_cfi_state(&state->cfi); + + /* + * We need the full vmlinux for noinstr validation, otherwise we can + * not correctly determine insn_call_dest(insn)->sec (external symbols + * do not have a section). + */ + if (opts.link && opts.noinstr && sec) + state->noinstr = sec->noinstr; +} + +static struct cfi_state *cfi_alloc(void) +{ + struct cfi_state *cfi = calloc(sizeof(struct cfi_state), 1); + if (!cfi) { + WARN("calloc failed"); + exit(1); + } + nr_cfi++; + return cfi; +} + +static int cfi_bits; +static struct hlist_head *cfi_hash; + +static inline bool cficmp(struct cfi_state *cfi1, struct cfi_state *cfi2) +{ + return memcmp((void *)cfi1 + sizeof(cfi1->hash), + (void *)cfi2 + sizeof(cfi2->hash), + sizeof(struct cfi_state) - sizeof(struct hlist_node)); +} + +static inline u32 cfi_key(struct cfi_state *cfi) +{ + return jhash((void *)cfi + sizeof(cfi->hash), + sizeof(*cfi) - sizeof(cfi->hash), 0); +} + +static struct cfi_state *cfi_hash_find_or_add(struct cfi_state *cfi) +{ + struct hlist_head *head = &cfi_hash[hash_min(cfi_key(cfi), cfi_bits)]; + struct cfi_state *obj; + + hlist_for_each_entry(obj, head, hash) { + if (!cficmp(cfi, obj)) { + nr_cfi_cache++; + return obj; + } + } + + obj = cfi_alloc(); + *obj = *cfi; + hlist_add_head(&obj->hash, head); + + return obj; +} + +static void cfi_hash_add(struct cfi_state *cfi) +{ + struct hlist_head *head = &cfi_hash[hash_min(cfi_key(cfi), cfi_bits)]; + + hlist_add_head(&cfi->hash, head); +} + +static void *cfi_hash_alloc(unsigned long size) +{ + cfi_bits = max(10, ilog2(size)); + cfi_hash = mmap(NULL, sizeof(struct hlist_head) << cfi_bits, + PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON, -1, 0); + if (cfi_hash == (void *)-1L) { + WARN("mmap fail cfi_hash"); + cfi_hash = NULL; + } else if (opts.stats) { + printf("cfi_bits: %d\n", cfi_bits); + } + + return cfi_hash; +} + +static unsigned long nr_insns; static unsigned long nr_insns_visited; +/* + * Call the arch-specific instruction decoder for all the instructions and add + * them to the global instruction list. + */ +static int decode_instructions(struct objtool_file *file) +{ + struct section *sec; + struct symbol *func; + unsigned long offset; + struct instruction *insn; + int ret; + + for_each_sec(file, sec) { + struct instruction *insns = NULL; + u8 prev_len = 0; + u8 idx = 0; + + if (!(sec->sh.sh_flags & SHF_EXECINSTR)) + continue; + + if (strcmp(sec->name, ".altinstr_replacement") && + strcmp(sec->name, ".altinstr_aux") && + strncmp(sec->name, ".discard.", 9)) + sec->text = true; + + if (!strcmp(sec->name, ".noinstr.text") || + !strcmp(sec->name, ".entry.text") || + !strcmp(sec->name, ".cpuidle.text") || + !strncmp(sec->name, ".text..__x86.", 13)) + sec->noinstr = true; + + /* + * .init.text code is ran before userspace and thus doesn't + * strictly need retpolines, except for modules which are + * loaded late, they very much do need retpoline in their + * .init.text + */ + if (!strcmp(sec->name, ".init.text") && !opts.module) + sec->init = true; + + for (offset = 0; offset < sec->sh.sh_size; offset += insn->len) { + if (!insns || idx == INSN_CHUNK_MAX) { + insns = calloc(sizeof(*insn), INSN_CHUNK_SIZE); + if (!insns) { + WARN("malloc failed"); + return -1; + } + idx = 0; + } else { + idx++; + } + insn = &insns[idx]; + insn->idx = idx; + + INIT_LIST_HEAD(&insn->call_node); + insn->sec = sec; + insn->offset = offset; + insn->prev_len = prev_len; + + ret = arch_decode_instruction(file, sec, offset, + sec->sh.sh_size - offset, + insn); + if (ret) + return ret; + + prev_len = insn->len; + + /* + * By default, "ud2" is a dead end unless otherwise + * annotated, because GCC 7 inserts it for certain + * divide-by-zero cases. + */ + if (insn->type == INSN_BUG) + insn->dead_end = true; + + hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset)); + nr_insns++; + } + +// printf("%s: last chunk used: %d\n", sec->name, (int)idx); + + sec_for_each_sym(sec, func) { + if (func->type != STT_NOTYPE && func->type != STT_FUNC) + continue; + + if (func->offset == sec->sh.sh_size) { + /* Heuristic: likely an "end" symbol */ + if (func->type == STT_NOTYPE) + continue; + WARN("%s(): STT_FUNC at end of section", + func->name); + return -1; + } + + if (func->embedded_insn || func->alias != func) + continue; + + if (!find_insn(file, sec, func->offset)) { + WARN("%s(): can't find starting instruction", + func->name); + return -1; + } + + sym_for_each_insn(file, func, insn) { + insn->sym = func; + if (func->type == STT_FUNC && + insn->type == INSN_ENDBR && + list_empty(&insn->call_node)) { + if (insn->offset == func->offset) { + list_add_tail(&insn->call_node, &file->endbr_list); + file->nr_endbr++; + } else { + file->nr_endbr_int++; + } + } + } + } + } + + if (opts.stats) + printf("nr_insns: %lu\n", nr_insns); + + return 0; +} + /* * Read the pv_ops[] .data table to find the static initialized values. */ @@ -236,6 +563,19 @@ static int init_pv_ops(struct objtool_file *file) return 0; } +static struct instruction *find_last_insn(struct objtool_file *file, + struct section *sec) +{ + struct instruction *insn = NULL; + unsigned int offset; + unsigned int end = (sec->sh.sh_size > 10) ? sec->sh.sh_size - 10 : 0; + + for (offset = sec->sh.sh_size - 1; offset >= end && !insn; offset--) + insn = find_insn(file, sec, offset); + + return insn; +} + /* * Mark "ud2" instructions and manually annotated dead ends. */ @@ -975,6 +1315,26 @@ __weak bool arch_is_embedded_insn(struct symbol *sym) return false; } +static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn) +{ + struct reloc *reloc; + + if (insn->no_reloc) + return NULL; + + if (!file) + return NULL; + + reloc = find_reloc_by_dest_range(file->elf, insn->sec, + insn->offset, insn->len); + if (!reloc) { + insn->no_reloc = 1; + return NULL; + } + + return reloc; +} + static void remove_insn_ops(struct instruction *insn) { struct stack_op *op, *next; @@ -1134,6 +1494,24 @@ static void add_return_call(struct objtool_file *file, struct instruction *insn, list_add_tail(&insn->call_node, &file->return_thunk_list); } +static bool is_first_func_insn(struct objtool_file *file, + struct instruction *insn, struct symbol *sym) +{ + if (insn->offset == sym->offset) + return true; + + /* Allow direct CALL/JMP past ENDBR */ + if (opts.ibt) { + struct instruction *prev = prev_insn_same_sym(file, insn); + + if (prev && prev->type == INSN_ENDBR && + insn->offset == sym->offset + prev->len) + return true; + } + + return false; +} + /* * A sibling call is a tail-call to another symbol -- to differentiate from a * recursive tail-call which is to the same symbol. @@ -1814,6 +2192,106 @@ static int add_jump_table_alts(struct objtool_file *file) return 0; } +static void set_func_state(struct cfi_state *state) +{ + state->cfa = initial_func_cfi.cfa; + memcpy(&state->regs, &initial_func_cfi.regs, + CFI_NUM_REGS * sizeof(struct cfi_reg)); + state->stack_size = initial_func_cfi.cfa.offset; + state->type = UNWIND_HINT_TYPE_CALL; +} + +static int read_unwind_hints(struct objtool_file *file) +{ + struct cfi_state cfi = init_cfi; + struct section *sec; + struct unwind_hint *hint; + struct instruction *insn; + struct reloc *reloc; + int i; + + sec = find_section_by_name(file->elf, ".discard.unwind_hints"); + if (!sec) + return 0; + + if (!sec->rsec) { + WARN("missing .rela.discard.unwind_hints section"); + return -1; + } + + if (sec->sh.sh_size % sizeof(struct unwind_hint)) { + WARN("struct unwind_hint size mismatch"); + return -1; + } + + file->hints = true; + + for (i = 0; i < sec->sh.sh_size / sizeof(struct unwind_hint); i++) { + hint = (struct unwind_hint *)sec->data->d_buf + i; + + reloc = find_reloc_by_dest(file->elf, sec, i * sizeof(*hint)); + if (!reloc) { + WARN("can't find reloc for unwind_hints[%d]", i); + return -1; + } + + insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc)); + if (!insn) { + WARN("can't find insn for unwind_hints[%d]", i); + return -1; + } + + insn->hint = true; + + if (hint->type == UNWIND_HINT_TYPE_UNDEFINED) { + insn->cfi = &force_undefined_cfi; + continue; + } + + if (hint->type == UNWIND_HINT_TYPE_SAVE) { + insn->hint = false; + insn->save = true; + continue; + } + + if (hint->type == UNWIND_HINT_TYPE_RESTORE) { + insn->restore = true; + continue; + } + + if (hint->type == UNWIND_HINT_TYPE_REGS_PARTIAL) { + struct symbol *sym = find_symbol_by_offset(insn->sec, insn->offset); + + if (sym && sym->bind == STB_GLOBAL) { + if (opts.ibt && insn->type != INSN_ENDBR && !insn->noendbr) { + WARN_INSN(insn, "UNWIND_HINT_IRET_REGS without ENDBR"); + } + } + } + + if (hint->type == UNWIND_HINT_TYPE_FUNC) { + insn->cfi = &func_cfi; + continue; + } + + if (insn->cfi) + cfi = *(insn->cfi); + + if (arch_decode_hint_reg(hint->sp_reg, &cfi.cfa.base)) { + WARN_INSN(insn, "unsupported unwind_hint sp base reg %d", hint->sp_reg); + return -1; + } + + cfi.cfa.offset = bswap_if_needed(file->elf, hint->sp_offset); + cfi.type = hint->type; + cfi.signal = hint->signal; + + insn->cfi = cfi_hash_find_or_add(&cfi); + } + + return 0; +} + static int read_noendbr_hints(struct objtool_file *file) { struct instruction *insn; @@ -2814,6 +3292,53 @@ static int handle_insn_ops(struct instruction *insn, return 0; } +static bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2) +{ + struct cfi_state *cfi1 = insn->cfi; + int i; + + if (!cfi1) { + WARN("CFI missing"); + return false; + } + + if (memcmp(&cfi1->cfa, &cfi2->cfa, sizeof(cfi1->cfa))) { + + WARN_INSN(insn, "stack state mismatch: cfa1=%d%+d cfa2=%d%+d", + cfi1->cfa.base, cfi1->cfa.offset, + cfi2->cfa.base, cfi2->cfa.offset); + + } else if (memcmp(&cfi1->regs, &cfi2->regs, sizeof(cfi1->regs))) { + for (i = 0; i < CFI_NUM_REGS; i++) { + if (!memcmp(&cfi1->regs[i], &cfi2->regs[i], + sizeof(struct cfi_reg))) + continue; + + WARN_INSN(insn, "stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d", + i, cfi1->regs[i].base, cfi1->regs[i].offset, + i, cfi2->regs[i].base, cfi2->regs[i].offset); + break; + } + + } else if (cfi1->type != cfi2->type) { + + WARN_INSN(insn, "stack state mismatch: type1=%d type2=%d", + cfi1->type, cfi2->type); + + } else if (cfi1->drap != cfi2->drap || + (cfi1->drap && cfi1->drap_reg != cfi2->drap_reg) || + (cfi1->drap && cfi1->drap_offset != cfi2->drap_offset)) { + + WARN_INSN(insn, "stack state mismatch: drap1=%d(%d,%d) drap2=%d(%d,%d)", + cfi1->drap, cfi1->drap_reg, cfi1->drap_offset, + cfi2->drap, cfi2->drap_reg, cfi2->drap_offset); + + } else + return true; + + return false; +} + static inline bool func_uaccess_safe(struct symbol *func) { if (func) @@ -3047,7 +3572,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, visited = VISITED_BRANCH << state.uaccess; if (insn->visited & VISITED_BRANCH_MASK) { - if (!insn->hint && !insn_cfi_match(insn, &state.cfi, true)) + if (!insn->hint && !insn_cfi_match(insn, &state.cfi)) return 1; if (insn->visited & visited) diff --git a/tools/objtool/dcheck.c b/tools/objtool/dcheck.c index 39dcd0a30f46fa0aae44b9f7b0467c745d637ad1..a4c342bf697da0615294d212cd4bc08386a66a03 100644 --- a/tools/objtool/dcheck.c +++ b/tools/objtool/dcheck.c @@ -11,6 +11,7 @@ #include #include #include +#include /* * Find the destination instructions for all jumps. diff --git a/tools/objtool/decode.c b/tools/objtool/decode.c deleted file mode 100644 index 59fea7e1d35bf46945bc45c026995d2a803fb305..0000000000000000000000000000000000000000 --- a/tools/objtool/decode.c +++ /dev/null @@ -1,136 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2015-2017 Josh Poimboeuf - */ -#include - -#include -#include -#include - -unsigned long nr_insns; - -/* - * Call the arch-specific instruction decoder for all the instructions and add - * them to the global instruction list. - */ -int decode_instructions(struct objtool_file *file) -{ - struct section *sec; - struct symbol *func; - unsigned long offset; - struct instruction *insn; - int ret; - - for_each_sec(file, sec) { - struct instruction *insns = NULL; - u8 prev_len = 0; - u8 idx = 0; - - if (!(sec->sh.sh_flags & SHF_EXECINSTR)) - continue; - - if (strcmp(sec->name, ".altinstr_replacement") && - strcmp(sec->name, ".altinstr_aux") && - strncmp(sec->name, ".discard.", 9)) - sec->text = true; - - if (!strcmp(sec->name, ".noinstr.text") || - !strcmp(sec->name, ".entry.text") || - !strcmp(sec->name, ".cpuidle.text") || - !strncmp(sec->name, ".text..__x86.", 13)) - sec->noinstr = true; - - /* - * .init.text code is ran before userspace and thus doesn't - * strictly need retpolines, except for modules which are - * loaded late, they very much do need retpoline in their - * .init.text - */ - if (!strcmp(sec->name, ".init.text") && !opts.module) - sec->init = true; - - for (offset = 0; offset < sec->sh.sh_size; offset += insn->len) { - if (!insns || idx == INSN_CHUNK_MAX) { - insns = calloc(sizeof(*insn), INSN_CHUNK_SIZE); - if (!insns) { - WARN("malloc failed"); - return -1; - } - idx = 0; - } else { - idx++; - } - insn = &insns[idx]; - insn->idx = idx; - - INIT_LIST_HEAD(&insn->call_node); - insn->sec = sec; - insn->offset = offset; - insn->prev_len = prev_len; - - ret = arch_decode_instruction(file, sec, offset, - sec->sh.sh_size - offset, - insn); - if (ret) - return ret; - - prev_len = insn->len; - - /* - * By default, "ud2" is a dead end unless otherwise - * annotated, because GCC 7 inserts it for certain - * divide-by-zero cases. - */ - if (insn->type == INSN_BUG) - insn->dead_end = true; - - hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset)); - nr_insns++; - } - -// printf("%s: last chunk used: %d\n", sec->name, (int)idx); - - sec_for_each_sym(sec, func) { - if (func->type != STT_NOTYPE && func->type != STT_FUNC) - continue; - - if (func->offset == sec->sh.sh_size) { - /* Heuristic: likely an "end" symbol */ - if (func->type == STT_NOTYPE) - continue; - WARN("%s(): STT_FUNC at end of section", - func->name); - return -1; - } - - if (func->embedded_insn || func->alias != func) - continue; - - if (!find_insn(file, sec, func->offset)) { - WARN("%s(): can't find starting instruction", - func->name); - return -1; - } - - sym_for_each_insn(file, func, insn) { - insn->sym = func; - if (func->type == STT_FUNC && - insn->type == INSN_ENDBR && - list_empty(&insn->call_node)) { - if (insn->offset == func->offset) { - list_add_tail(&insn->call_node, &file->endbr_list); - file->nr_endbr++; - } else { - file->nr_endbr_int++; - } - } - } - } - } - - if (opts.stats) - printf("nr_insns: %lu\n", nr_insns); - - return 0; -} diff --git a/tools/objtool/include/objtool/cfi.h b/tools/objtool/include/objtool/cfi.h index 5573667993153b7fb168638640e784656ffb4a59..c8a6bec4f6b91ea7f3564d50801d9acddf0d4612 100644 --- a/tools/objtool/include/objtool/cfi.h +++ b/tools/objtool/include/objtool/cfi.h @@ -39,17 +39,4 @@ struct cfi_state { bool force_undefined; }; -void init_cfi_state(struct cfi_state *cfi); -bool cficmp(struct cfi_state *cfi1, struct cfi_state *cfi2); -struct cfi_state *cfi_hash_find_or_add(struct cfi_state *cfi); -void cfi_hash_add(struct cfi_state *cfi); -void *cfi_hash_alloc(unsigned long size); -void set_func_state(struct cfi_state *state); - -extern unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache; -extern struct cfi_init_state initial_func_cfi; -extern struct cfi_state init_cfi; -extern struct cfi_state func_cfi; -extern struct cfi_state force_undefined_cfi; - #endif /* _OBJTOOL_CFI_H */ diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h index 1f63eca11ddd16ed696cbdc1f3042f70ef980eb3..f389816e3fa265da1be8ff16b36ebe47bc06c253 100644 --- a/tools/objtool/include/objtool/check.h +++ b/tools/objtool/include/objtool/check.h @@ -6,8 +6,21 @@ #ifndef _CHECK_H #define _CHECK_H -#include +#ifdef __aarch64__ #include +#else +#include +#include +#include + +struct insn_state { + struct cfi_state cfi; + unsigned int uaccess_stack; + bool uaccess; + bool df; + bool noinstr; + s8 instr; +}; struct alt_group { /* @@ -26,9 +39,95 @@ struct alt_group { struct cfi_state **cfi; }; +#define INSN_CHUNK_BITS 8 +#define INSN_CHUNK_SIZE (1 << INSN_CHUNK_BITS) +#define INSN_CHUNK_MAX (INSN_CHUNK_SIZE - 1) + +struct instruction { + struct hlist_node hash; + struct list_head call_node; + struct section *sec; + unsigned long offset; + unsigned long immediate; + + u8 len; + u8 prev_len; + u8 type; + s8 instr; + + u32 idx : INSN_CHUNK_BITS, + dead_end : 1, + ignore : 1, + ignore_alts : 1, + hint : 1, + save : 1, + restore : 1, + retpoline_safe : 1, + noendbr : 1, + unret : 1, + visited : 4, + no_reloc : 1; + /* 10 bit hole */ + + struct alt_group *alt_group; + struct instruction *jump_dest; + struct instruction *first_jump_src; + union { + struct symbol *_call_dest; + struct reloc *_jump_table; + }; + struct alternative *alts; + struct symbol *sym; + struct stack_op *stack_ops; + struct cfi_state *cfi; +}; + +static inline struct symbol *insn_func(struct instruction *insn) +{ + struct symbol *sym = insn->sym; + + if (sym && sym->type != STT_FUNC) + sym = NULL; + + return sym; +} + #define VISITED_BRANCH 0x01 #define VISITED_BRANCH_UACCESS 0x02 #define VISITED_BRANCH_MASK 0x03 #define VISITED_UNRET 0x04 +static inline bool is_static_jump(struct instruction *insn) +{ + return insn->type == INSN_JUMP_CONDITIONAL || + insn->type == INSN_JUMP_UNCONDITIONAL; +} + +static inline bool is_dynamic_jump(struct instruction *insn) +{ + return insn->type == INSN_JUMP_DYNAMIC || + insn->type == INSN_JUMP_DYNAMIC_CONDITIONAL; +} + +static inline bool is_jump(struct instruction *insn) +{ + return is_static_jump(insn) || is_dynamic_jump(insn); +} + +struct instruction *find_insn(struct objtool_file *file, + struct section *sec, unsigned long offset); + +struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruction *insn); + +#define sec_for_each_insn(file, _sec, insn) \ + for (insn = find_insn(file, _sec, 0); \ + insn && insn->sec == _sec; \ + insn = next_insn_same_sec(file, insn)) + +static inline bool insn_can_reloc(struct instruction *insn) +{ + return true; +} + +#endif /* endof !__aarch64__ */ #endif /* _CHECK_H */ diff --git a/tools/objtool/include/objtool/insn.h b/tools/objtool/include/objtool/insn.h index d19cd2c12f06d4f12194df249e9b28749a8920bf..36922da5ccc700d54c6d3c5af86e64ccb87bb4fb 100644 --- a/tools/objtool/include/objtool/insn.h +++ b/tools/objtool/include/objtool/insn.h @@ -6,6 +6,8 @@ #ifndef _INSN_H #define _INSN_H +/* This is an arm64 specific version for check.h */ +#ifdef __aarch64__ #include #include @@ -61,6 +63,23 @@ struct instruction { struct cfi_state *cfi; }; +struct alt_group { + /* + * Pointer from a replacement group to the original group. NULL if it + * *is* the original group. + */ + struct alt_group *orig_group; + + /* First and last instructions in the group */ + struct instruction *first_insn, *last_insn, *nop; + + /* + * Byte-offset-addressed len-sized array of pointers to CFI structs. + * This is shared with the other alt_groups in the same alternative. + */ + struct cfi_state **cfi; +}; + static inline struct symbol *insn_func(struct instruction *insn) { struct symbol *sym = insn->sym; @@ -110,8 +129,8 @@ bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2, bool is_first_func_insn(struct objtool_file *file, struct instruction *insn, struct symbol *sym); -int decode_instructions(struct objtool_file *file); int read_unwind_hints(struct objtool_file *file); +int decode_instructions(struct objtool_file *file); #define sec_for_each_insn(file, _sec, insn) \ for (insn = find_insn(file, _sec, 0); \ @@ -147,4 +166,5 @@ int read_unwind_hints(struct objtool_file *file); insn = next_insn_same_sec(file, insn)) extern unsigned long nr_insns; +#endif /* __aarch64__ */ #endif /* _INSN_H */ diff --git a/tools/objtool/include/objtool/orc.h b/tools/objtool/include/objtool/orc.h index 11e746786fb4a353e91fffa66788738139d4aece..88dc98a2b8a400ab6597ad891c3ed98cdbb9ca2a 100644 --- a/tools/objtool/include/objtool/orc.h +++ b/tools/objtool/include/objtool/orc.h @@ -6,13 +6,15 @@ #ifndef _OBJTOOL_ORC_H #define _OBJTOOL_ORC_H +#include #include int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn); -const char *orc_type_name(unsigned int type); -void orc_print_reg(unsigned int reg, int offset); -void orc_print_sp(void); -void orc_print_fp(void); +void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i); +int write_orc_entry(struct elf *elf, struct section *orc_sec, + struct section *ip_sec, unsigned int idx, + struct section *insn_sec, unsigned long insn_off, + struct orc_entry *o); #endif /* _OBJTOOL_ORC_H */ diff --git a/tools/objtool/orc_dump.c b/tools/objtool/orc_dump.c index 84545d5e694e47410ec309165561593fb664d5b6..a62247efb64f2e06c293e129ade3abae4fd456c1 100644 --- a/tools/objtool/orc_dump.c +++ b/tools/objtool/orc_dump.c @@ -4,8 +4,8 @@ */ #include +#include #include -#include #include #include #include @@ -150,17 +150,7 @@ int orc_dump(const char *_objname) printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i])); } - printf("type:%s", orc_type_name(orc[i].type)); - - printf(" sp:"); - - orc_print_reg(orc[i].sp_reg, bswap_if_needed(&dummy_elf, orc[i].sp_offset)); - - printf(" fp:"); - - orc_print_reg(orc[i].fp_reg, bswap_if_needed(&dummy_elf, orc[i].fp_offset)); - - printf(" signal:%d\n", orc[i].signal); + orc_print_dump(&dummy_elf, orc, i); } elf_end(elf); diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c index a146666ea9d175cfd297f8ad2e962b540f580fc8..217a4e7d561742603069ca8c4f3c2b41be19b728 100644 --- a/tools/objtool/orc_gen.c +++ b/tools/objtool/orc_gen.c @@ -7,39 +7,22 @@ #include #include -#include +#include #include #include #include #include +#ifdef __aarch64__ +#define bp_reg fp_reg +#endif + bool __weak orc_ignore_section(struct section *sec) { return false; } -static int write_orc_entry(struct elf *elf, struct section *orc_sec, - struct section *ip_sec, unsigned int idx, - struct section *insn_sec, unsigned long insn_off, - struct orc_entry *o) -{ - struct orc_entry *orc; - - /* populate ORC data */ - orc = (struct orc_entry *)orc_sec->data->d_buf + idx; - memcpy(orc, o, sizeof(*orc)); - orc->sp_offset = bswap_if_needed(elf, orc->sp_offset); - orc->fp_offset = bswap_if_needed(elf, orc->fp_offset); - - /* populate reloc for ip */ - if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx, - insn_sec, insn_off)) - return -1; - - return 0; -} - struct orc_list_entry { struct list_head list; struct orc_entry orc; @@ -80,7 +63,7 @@ int orc_create(struct objtool_file *file) struct list_head orc_list; struct orc_entry null = { - .fp_reg = ORC_REG_UNDEFINED, + .bp_reg = ORC_REG_UNDEFINED, .type = UNWIND_HINT_TYPE_CALL, }; diff --git a/tools/objtool/sync-check.sh b/tools/objtool/sync-check.sh index 9bacf219bfe02c6b574d4462f9d0481ef647cfd8..b06b5b8811217ed7dfbde3038fc44beb89840d83 100755 --- a/tools/objtool/sync-check.sh +++ b/tools/objtool/sync-check.sh @@ -14,11 +14,9 @@ arch/x86/include/asm/nops.h arch/x86/include/asm/inat_types.h arch/x86/include/asm/orc_types.h arch/x86/include/asm/emulate_prefix.h -arch/x86/include/asm/unwind_hints.h arch/x86/lib/x86-opcode-map.txt arch/x86/tools/gen-insn-attr-x86.awk include/linux/static_call_types.h -include/linux/orc_entry.h " SYNC_CHECK_FILES='