]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
powerpc/modules: Fix alignment of .toc section in kernel modules
authorAlan Modra <amodra@gmail.com>
Wed, 6 Dec 2017 19:12:28 +0000 (17:12 -0200)
committerMichael Ellerman <mpe@ellerman.id.au>
Mon, 11 Dec 2017 02:03:35 +0000 (13:03 +1100)
powerpc64 gcc can generate code that offsets an address, to access
part of an object in memory. If the address is a -mcmodel=medium toc
pointer relative address then code like the following is possible.

  addis r9,r2,var@toc@ha
  ld r3,var@toc@l(r9)
  ld r4,(var+8)@toc@l(r9)

This works fine so long as var is naturally aligned, *and* r2 is
sufficiently aligned. If not, there is a possibility that the offset
added to access var+8 wraps over a n*64k+32k boundary. Modules don't
have any guarantee that r2 is sufficiently aligned. Moreover, code
generated by older compilers generates a .toc section with 2**0
alignment, which can result in relocation failures at module load time
even without the wrap problem.

Thus, this patch links modules with an aligned .toc section (Makefile
and module.lds changes), and forces alignment for out of tree modules
or those without a .toc section (module_64.c changes).

Signed-off-by: Alan Modra <amodra@gmail.com>
[desnesn: updated patch to apply to powerpc-next kernel v4.15 ]
Signed-off-by: Desnes A. Nunes do Rosario <desnesn@linux.vnet.ibm.com>
[mpe: Fix out-of-tree build, swap -256 for ~0xff, reflow comment]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/Makefile
arch/powerpc/kernel/module.lds [new file with mode: 0644]
arch/powerpc/kernel/module_64.c

index 1381693a4a51bee5b1f37290b604f4df1588b845..ccd2556bdb530db23d6a1ba9f1ffff907d62fb59 100644 (file)
@@ -63,6 +63,7 @@ UTS_MACHINE := $(subst $(space),,$(machine-y))
 ifdef CONFIG_PPC32
 KBUILD_LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o
 else
+KBUILD_LDFLAGS_MODULE += -T $(srctree)/arch/powerpc/kernel/module.lds
 ifeq ($(call ld-ifversion, -ge, 225000000, y),y)
 # Have the linker provide sfpr if possible.
 # There is a corresponding test in arch/powerpc/lib/Makefile
diff --git a/arch/powerpc/kernel/module.lds b/arch/powerpc/kernel/module.lds
new file mode 100644 (file)
index 0000000..cea5dc1
--- /dev/null
@@ -0,0 +1,8 @@
+/* Force alignment of .toc section.  */
+SECTIONS
+{
+       .toc 0 : ALIGN(256)
+       {
+               *(.got .toc)
+       }
+}
index e6c011d566292c7769c156f99bdee26d9585d670..d056b745743d0c6820a6a7edcbe9b445a792fc5a 100644 (file)
@@ -339,8 +339,11 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,
                char *p;
                if (strcmp(secstrings + sechdrs[i].sh_name, ".stubs") == 0)
                        me->arch.stubs_section = i;
-               else if (strcmp(secstrings + sechdrs[i].sh_name, ".toc") == 0)
+               else if (strcmp(secstrings + sechdrs[i].sh_name, ".toc") == 0) {
                        me->arch.toc_section = i;
+                       if (sechdrs[i].sh_addralign < 8)
+                               sechdrs[i].sh_addralign = 8;
+               }
                else if (strcmp(secstrings+sechdrs[i].sh_name,"__versions")==0)
                        dedotify_versions((void *)hdr + sechdrs[i].sh_offset,
                                          sechdrs[i].sh_size);
@@ -373,12 +376,15 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,
        return 0;
 }
 
-/* r2 is the TOC pointer: it actually points 0x8000 into the TOC (this
-   gives the value maximum span in an instruction which uses a signed
-   offset) */
+/*
+ * r2 is the TOC pointer: it actually points 0x8000 into the TOC (this gives the
+ * value maximum span in an instruction which uses a signed offset). Round down
+ * to a 256 byte boundary for the odd case where we are setting up r2 without a
+ * .toc section.
+ */
 static inline unsigned long my_r2(const Elf64_Shdr *sechdrs, struct module *me)
 {
-       return sechdrs[me->arch.toc_section].sh_addr + 0x8000;
+       return (sechdrs[me->arch.toc_section].sh_addr & ~0xfful) + 0x8000;
 }
 
 /* Both low and high 16 bits are added as SIGNED additions, so if low