[PATCH] [powerpc-next] Fix powerpc64 alignment of .toc section in kernel modules

Desnes Augusto Nunes do Rosario desnesn at linux.vnet.ibm.com
Thu Dec 7 06:12:28 AEDT 2017


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 at toc@ha
 ld r3,var at toc@l(r9)
 ld r4,(var+8)@toc at 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 at gmail.com>
[ desnesn: updated patch to apply to powerpc-next kernel v4.15 ]
Signed-off-by: Desnes A. Nunes do Rosario <desnesn at linux.vnet.ibm.com>
---
 arch/powerpc/Makefile           |  1 +
 arch/powerpc/kernel/module.lds  |  8 ++++++++
 arch/powerpc/kernel/module_64.c | 13 +++++++++----
 3 files changed, 18 insertions(+), 4 deletions(-)
 create mode 100644 arch/powerpc/kernel/module.lds

diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index 1381693..c472f5b 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -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 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
index 0000000..cea5dc1
--- /dev/null
+++ b/arch/powerpc/kernel/module.lds
@@ -0,0 +1,8 @@
+/* Force alignment of .toc section.  */
+SECTIONS
+{
+	.toc 0 : ALIGN(256)
+	{
+		*(.got .toc)
+	}
+}
diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
index 759104b..9b2c5c1 100644
--- a/arch/powerpc/kernel/module_64.c
+++ b/arch/powerpc/kernel/module_64.c
@@ -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);
@@ -374,11 +377,13 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,
 }
 
 /* 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) */
+ * 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 & -256) + 0x8000;
 }
 
 /* Both low and high 16 bits are added as SIGNED additions, so if low
-- 
2.9.5



More information about the Linuxppc-dev mailing list