Re: [PATCH 0/4] Section alignment issues?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Nov 23, 2023 at 7:18 AM <deller@xxxxxxxxxx> wrote:
>
> From: Helge Deller <deller@xxxxxx>
>
> While working on the 64-bit parisc kernel, I noticed that the __ksymtab[]
> table was not correctly 64-bit aligned in many modules.
> The following patches do fix some of those issues in the generic code.
>
> But further investigation shows that multiple sections in the kernel and in
> modules are possibly not correctly aligned, and thus may lead to performance
> degregations at runtime (small on x86, huge on parisc, sparc and others which
> need exception handlers). Sometimes wrong alignments may also be simply hidden
> by the linker or kernel module loader which pulls in the sections by luck with
> a correct alignment (e.g. because the previous section was aligned already).
>
> An objdump on a x86 module shows e.g.:
>
> ./kernel/net/netfilter/nf_log_syslog.ko:     file format elf64-x86-64
> Sections:
> Idx Name          Size      VMA               LMA               File off  Algn
>   0 .text         00001fdf  0000000000000000  0000000000000000  00000040  2**4
>                   CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
>   1 .init.text    000000f6  0000000000000000  0000000000000000  00002020  2**4
>                   CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
>   2 .exit.text    0000005c  0000000000000000  0000000000000000  00002120  2**4
>                   CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
>   3 .rodata.str1.8 000000dc  0000000000000000  0000000000000000  00002180  2**3
>                   CONTENTS, ALLOC, LOAD, READONLY, DATA
>   4 .rodata.str1.1 0000030a  0000000000000000  0000000000000000  0000225c  2**0
>                   CONTENTS, ALLOC, LOAD, READONLY, DATA
>   5 .rodata       000000b0  0000000000000000  0000000000000000  00002580  2**5
>                   CONTENTS, ALLOC, LOAD, READONLY, DATA
>   6 .modinfo      0000019e  0000000000000000  0000000000000000  00002630  2**0
>                   CONTENTS, ALLOC, LOAD, READONLY, DATA
>   7 .return_sites 00000034  0000000000000000  0000000000000000  000027ce  2**0
>                   CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
>   8 .call_sites   0000029c  0000000000000000  0000000000000000  00002802  2**0
>                   CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
>
> In this example I believe the ".return_sites" and ".call_sites" should have
> an alignment of at least 32-bit (4 bytes).
>
> On other architectures or modules other sections like ".altinstructions" or
> "__ex_table" may show up wrongly instead.
>
> In general I think it would be beneficial to search for wrong alignments at
> link time, and maybe at runtime.
>
> The patch at the end of this cover letter
> - adds compile time checks to the "modpost" tool, and
> - adds a runtime check to the kernel module loader at runtime.
> And it will possibly show false positives too (!!!)
> I do understand that some of those sections are not performce critical
> and thus any alignment is OK.
>
> The modpost patch will emit at compile time such warnings (on x86-64 kernel build):
>
> WARNING: modpost: vmlinux: section .initcall7.init (type 1, flags 2) has alignment of 1, expected at least 4.
> Maybe you need to add ALIGN() to the modules.lds file (or fix modpost) ?
> WARNING: modpost: vmlinux: section .altinstructions (type 1, flags 2) has alignment of 1, expected at least 2.
> WARNING: modpost: vmlinux: section .initcall6.init (type 1, flags 2) has alignment of 1, expected at least 4.
> WARNING: modpost: vmlinux: section .initcallearly.init (type 1, flags 2) has alignment of 1, expected at least 4.
> WARNING: modpost: vmlinux: section .rodata.cst2 (type 1, flags 18) has alignment of 2, expected at least 64.
> WARNING: modpost: vmlinux: section .static_call_tramp_key (type 1, flags 2) has alignment of 1, expected at least 8.
> WARNING: modpost: vmlinux: section .con_initcall.init (type 1, flags 2) has alignment of 1, expected at least 8.
> WARNING: modpost: vmlinux: section __bug_table (type 1, flags 3) has alignment of 1, expected at least 4.
> ...




modpost acts on vmlinux.o instead of vmlinux.


vmlinux.o is a relocatable ELF, which is not a real layout
because no linker script has been considered yet at this
point.


vmlinux is an executable ELF, produced by a linker,
with the linker script taken into consideration
to determine the final section/symbol layout.


So, checking this in modpost is meaningless.



I did not check the module checking code, but
modules are also relocatable ELF.











>
> while the kernel module loader may show at runtime:
>
> ** WARNING **:   module: efivarfs, dest=0xffffffffc02d08d2, section=.retpoline_sites possibly not correctly aligned.
> ** WARNING **:   module: efivarfs, dest=0xffffffffc02d0bae, section=.ibt_endbr_seal possibly not correctly aligned.
> ** WARNING **:   module: efivarfs, dest=0xffffffffc02d0be6, section=.orc_unwind possibly not correctly aligned.
> ** WARNING **:   module: efivarfs, dest=0xffffffffc02d12a6, section=.orc_unwind_ip possibly not correctly aligned.
> ** WARNING **:   module: efivarfs, dest=0xffffffffc02d178c, section=.note.Linux possibly not correctly aligned.
> ** WARNING **:   module: efivarfs, dest=0xffffffffc02d17bc, section=.orc_header possibly not correctly aligned.
> ** WARNING **:   module: xt_addrtype, dest=0xffffffffc01ef18a, size=0x00000020, section=.return_sites
>
> My questions:
> - Am I wrong with my analysis?
> - What does people see on other architectures?
> - Does it make sense to add a compile- and runtime-check, like the patch below, to the kernel?
>
> Helge
>
> ---
>
> From: Helge Deller <deller@xxxxxx>
> Subject: [PATCH] MODPOST: Detect and report possible section alignment issues
>
> IMPORTANT: THIS PATCH IS NOT INTENDED TO BE APPLIED !!!!
>
> The main idea here is to check at compile time (during modpost run) and at
> runtime (when loading a module) if the sections in kernel modules (and
> vmlinux) are correctly aligned in memory and report if a wrong alignment is
> suspected.
>
> It WILL report false positives. Many section names still need to be added to
> the various tables.
> But even at this stage it gives some insight at the various possibly wrong
> section alignments.
>
> Signed-off-by: Helge Deller <deller@xxxxxx>
>
> diff --git a/kernel/module/main.c b/kernel/module/main.c
> index 98fedfdb8db5..88201d6b0c17 100644
> --- a/kernel/module/main.c
> +++ b/kernel/module/main.c
> @@ -2277,6 +2277,10 @@ static int move_module(struct module *mod, struct load_info *info)
>                                 ret = -ENOEXEC;
>                                 goto out_enomem;
>                         }
> +                       if (((uintptr_t)dest) & (BITS_PER_LONG/8 - 1)) {
> +                               printk("** WARNING **: \tmodule: %s, dest=0x%lx, section=%s possibly not correctly aligned.\n",
> +                                       mod->name, (long)dest, info->secstrings + shdr->sh_name);
> +                       }
>                         memcpy(dest, (void *)shdr->sh_addr, shdr->sh_size);
>                 }
>                 /*
> diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
> index 739402f45509..2add144a05e3 100644
> --- a/scripts/Makefile.modpost
> +++ b/scripts/Makefile.modpost
> @@ -49,6 +49,8 @@ modpost-args =                                                                                \
>         $(if $(KBUILD_NSDEPS),-d $(MODULES_NSDEPS))                                     \
>         $(if $(CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS)$(KBUILD_NSDEPS),-N)       \
>         $(if $(findstring 1, $(KBUILD_EXTRA_WARN)),-W)                                  \
> +       $(if $(CONFIG_64BIT),-6)                                                        \
> +       $(if $(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS),-R)                                 \
>         -o $@
>
>  modpost-deps := $(MODPOST)
> diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
> index cb6406f485a9..70c69db6a17c 100644
> --- a/scripts/mod/modpost.c
> +++ b/scripts/mod/modpost.c
> @@ -43,6 +43,10 @@ static bool ignore_missing_files;
>  /* If set to 1, only warn (instead of error) about missing ns imports */
>  static bool allow_missing_ns_imports;
>
> +/* is target a 64-bit platform and has it prel32 relocation support? */
> +static bool target_64bit;
> +static bool target_prel32_relocations;
> +
>  static bool error_occurred;
>
>  static bool extra_warn;
> @@ -1493,6 +1497,76 @@ static void check_sec_ref(struct module *mod, struct elf_info *elf)
>         }
>  }
>
> +/**
> + * Check alignment of sections in modules.
> + **/
> +static void check_sec_alignment(struct module *mod, struct elf_info *elf)
> +{
> +       /* sections that may use PREL32 relocations and only need 4-byte alignment */
> +       static const char *const prel32_sec_list[] = {
> +               "__tracepoints_ptrs",
> +               "__ksymtab",
> +               "__bug_table",
> +               ".smp_locks",
> +               NULL
> +       };
> +       /* sections that are fine with any/1-byte alignment */
> +       static const char *const byte_sec_list[] = {
> +               ".modinfo",
> +               ".init.ramfs",
> +               NULL
> +       };
> +       /* sections with special alignment */
> +       static struct { int align; const char *name; } const special_list[] = {
> +               { 64,   ".rodata.cst2" },
> +               { 0,    NULL }
> +       };
> +
> +       int i;
> +
> +       /* ignore vmlinux for now? */
> +       // if (mod->is_vmlinux) return;
> +
> +       /* Walk through all sections */
> +       for (i = 0; i < elf->num_sections; i++) {
> +               Elf_Shdr *sechdr = &elf->sechdrs[i];
> +               const char *sec = sech_name(elf, sechdr);
> +               const char *modname = mod->name;
> +               const int is_shalign = sechdr->sh_addralign;
> +               int should_shalign;
> +               int k;
> +
> +               /* ignore some sections */
> +               if ((sechdr->sh_type == SHT_NULL) ||
> +                   !(sechdr->sh_flags & SHF_ALLOC))
> +                       continue;
> +
> +               /* default alignment is 8 for 64-bit and 4 for 32-bit targets * */
> +               should_shalign = target_64bit ? 8 : 4;
> +               if (match(sec, prel32_sec_list))
> +                       should_shalign = target_prel32_relocations ? 4 : should_shalign;
> +               else if (strstr(sec, "text")) /* assume text segments to be 4-byte aligned */
> +                       should_shalign = 4;
> +               else if (match(sec, byte_sec_list)) /* any alignment is OK. */
> +                       continue;
> +               else {
> +                       /* search in list with special alignment */
> +                       k = 0;
> +                       while (special_list[k].align && strstarts(sec, special_list[k].name)) {
> +                               should_shalign = special_list[k].align;
> +                               break;
> +                       }
> +               }
> +
> +               if (is_shalign  >= should_shalign)
> +                       continue;
> +
> +               warn("%s: section %s (type %d, flags %lu) has alignment of %d, expected at least %d.\n"
> +                    "Maybe you need to add ALIGN() to the modules.lds file (or fix modpost) ?\n",
> +                    modname, sec, sechdr->sh_type, sechdr->sh_flags, is_shalign, should_shalign);
> +       }
> +}
> +
>  static char *remove_dot(char *s)
>  {
>         size_t n = strcspn(s, ".");
> @@ -1653,6 +1727,8 @@ static void read_symbols(const char *modname)
>                 handle_moddevtable(mod, &info, sym, symname);
>         }
>
> +       check_sec_alignment(mod, &info);
> +
>         check_sec_ref(mod, &info);
>
>         if (!mod->is_vmlinux) {
> @@ -2183,7 +2259,7 @@ int main(int argc, char **argv)
>         LIST_HEAD(dump_lists);
>         struct dump_list *dl, *dl2;
>
> -       while ((opt = getopt(argc, argv, "ei:MmnT:to:au:WwENd:")) != -1) {
> +       while ((opt = getopt(argc, argv, "ei:MmnT:to:au:WwENd:6R")) != -1) {
>                 switch (opt) {
>                 case 'e':
>                         external_module = true;
> @@ -2232,6 +2308,12 @@ int main(int argc, char **argv)
>                 case 'd':
>                         missing_namespace_deps = optarg;
>                         break;
> +               case '6':
> +                       target_64bit = true;
> +                       break;
> +               case 'R':
> +                       target_prel32_relocations = true;
> +                       break;
>                 default:
>                         exit(1);
>                 }
> diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
> index 70c69db6a17c..b09ab081dc03 100644
> --- a/scripts/mod/modpost.c
> +++ b/scripts/mod/modpost.c
> @@ -1510,15 +1510,24 @@ static void check_sec_alignment(struct module *mod, struct elf_info *elf)
>                 ".smp_locks",
>                 NULL
>         };
> -       /* sections that are fine with any/1-byte alignment */
> +       /* sections that are fine with any alignment */
>         static const char *const byte_sec_list[] = {
>                 ".modinfo",
>                 ".init.ramfs",
>                 NULL
>         };
> -       /* sections with special alignment */
> +       /* sections with special given minimal alignment. Checked with
> +        * startswith(). */
>         static struct { int align; const char *name; } const special_list[] = {
>                 { 64,   ".rodata.cst2" },
> +               { 2,    ".altinstructions" }, /* at least on x86 */
> +               { 1,    ".altinstr_replacement" },
> +               { 1,    ".altinstr_aux" },
> +               { 4,    ".call_sites" },
> +               { 4,    ".return_sites" },
> +               { 1,    ".note.Linux" },        /* correct? */
> +               { 4,    "__ex_table" },
> +               { 4,    ".initcall" },          /* at least 4 ? */
>                 { 0,    NULL }
>         };
>
> @@ -1545,16 +1554,17 @@ static void check_sec_alignment(struct module *mod, struct elf_info *elf)
>                 should_shalign = target_64bit ? 8 : 4;
>                 if (match(sec, prel32_sec_list))
>                         should_shalign = target_prel32_relocations ? 4 : should_shalign;
> -               else if (strstr(sec, "text")) /* assume text segments to be 4-byte aligned */
> -                       should_shalign = 4;
>                 else if (match(sec, byte_sec_list)) /* any alignment is OK. */
>                         continue;
> +               else if (strstr(sec, "text")) /* assume text segments to be 4-byte aligned */
> +                       should_shalign = 4;
>                 else {
>                         /* search in list with special alignment */
> -                       k = 0;
> -                       while (special_list[k].align && strstarts(sec, special_list[k].name)) {
> -                               should_shalign = special_list[k].align;
> -                               break;
> +                       for (k = 0; special_list[k].align; k++) {
> +                               if (strstarts(sec, special_list[k].name)) {
> +                                       should_shalign = special_list[k].align;
> +                                       break;
> +                               }
>                         }
>                 }
>
> Helge Deller (4):
>   linux/export: Fix alignment for 64-bit ksymtab entries
>   modules: Ensure 64-bit alignment on __ksymtab_* sections
>   vmlinux.lds.h: Fix alignment for __ksymtab*, __kcrctab_* and
>     .pci_fixup sections
>   modules: Add missing entry for __ex_table
>
>  include/asm-generic/vmlinux.lds.h | 5 +++++
>  include/linux/export-internal.h   | 5 ++++-
>  scripts/module.lds.S              | 9 +++++----
>  3 files changed, 14 insertions(+), 5 deletions(-)
>
> --
> 2.41.0
>


--
Best Regards
Masahiro Yamada





[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux