aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2014-03-18 20:12:22 -0400
committerAnton Blanchard <anton@samba.org>2014-04-22 20:05:32 -0400
commit008d7a914efee6ee5afe59bcc46d3d6b60657598 (patch)
treed012fae16e851f7dcffb56b5fc7f263aa75a36be
parent5c729a115e4727fd71308e4d68846f64fa460ead (diff)
powerpc: modules: implement stubs for ELFv2 ABI.
ELFv2 doesn't use function descriptors, because it doesn't need to load a new r2 when calling into a function. On the other hand, you're supposed to use a local entry point for R_PPC_REL24 branches. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
-rw-r--r--arch/powerpc/kernel/module_64.c73
1 files changed, 61 insertions, 12 deletions
diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
index d7222495e24c..042360135260 100644
--- a/arch/powerpc/kernel/module_64.c
+++ b/arch/powerpc/kernel/module_64.c
@@ -43,8 +43,58 @@
43 43
44#if defined(_CALL_ELF) && _CALL_ELF == 2 44#if defined(_CALL_ELF) && _CALL_ELF == 2
45#define R2_STACK_OFFSET 24 45#define R2_STACK_OFFSET 24
46
47/* An address is simply the address of the function. */
48typedef unsigned long func_desc_t;
49
50static func_desc_t func_desc(unsigned long addr)
51{
52 return addr;
53}
54static unsigned long func_addr(unsigned long addr)
55{
56 return addr;
57}
58static unsigned long stub_func_addr(func_desc_t func)
59{
60 return func;
61}
62
63/* PowerPC64 specific values for the Elf64_Sym st_other field. */
64#define STO_PPC64_LOCAL_BIT 5
65#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT)
66#define PPC64_LOCAL_ENTRY_OFFSET(other) \
67 (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2)
68
69static unsigned int local_entry_offset(const Elf64_Sym *sym)
70{
71 /* sym->st_other indicates offset to local entry point
72 * (otherwise it will assume r12 is the address of the start
73 * of function and try to derive r2 from it). */
74 return PPC64_LOCAL_ENTRY_OFFSET(sym->st_other);
75}
46#else 76#else
47#define R2_STACK_OFFSET 40 77#define R2_STACK_OFFSET 40
78
79/* An address is address of the OPD entry, which contains address of fn. */
80typedef struct ppc64_opd_entry func_desc_t;
81
82static func_desc_t func_desc(unsigned long addr)
83{
84 return *(struct ppc64_opd_entry *)addr;
85}
86static unsigned long func_addr(unsigned long addr)
87{
88 return func_desc(addr).funcaddr;
89}
90static unsigned long stub_func_addr(func_desc_t func)
91{
92 return func.funcaddr;
93}
94static unsigned int local_entry_offset(const Elf64_Sym *sym)
95{
96 return 0;
97}
48#endif 98#endif
49 99
50/* Like PPC32, we need little trampolines to do > 24-bit jumps (into 100/* Like PPC32, we need little trampolines to do > 24-bit jumps (into
@@ -56,7 +106,7 @@ struct ppc64_stub_entry
56 u32 jump[7]; 106 u32 jump[7];
57 u32 unused; 107 u32 unused;
58 /* Data for the above code */ 108 /* Data for the above code */
59 struct ppc64_opd_entry opd; 109 func_desc_t funcdata;
60}; 110};
61 111
62/* 112/*
@@ -225,7 +275,7 @@ static Elf64_Sym *find_dot_toc(Elf64_Shdr *sechdrs,
225 275
226 for (i = 1; i < numsyms; i++) { 276 for (i = 1; i < numsyms; i++) {
227 if (syms[i].st_shndx == SHN_UNDEF 277 if (syms[i].st_shndx == SHN_UNDEF
228 && strcmp(strtab + syms[i].st_name, ".TOC.") == 0) 278 && strcmp(strtab + syms[i].st_name, "TOC.") == 0)
229 return &syms[i]; 279 return &syms[i];
230 } 280 }
231 return NULL; 281 return NULL;
@@ -295,7 +345,7 @@ static inline unsigned long my_r2(Elf64_Shdr *sechdrs, struct module *me)
295/* Patch stub to reference function and correct r2 value. */ 345/* Patch stub to reference function and correct r2 value. */
296static inline int create_stub(Elf64_Shdr *sechdrs, 346static inline int create_stub(Elf64_Shdr *sechdrs,
297 struct ppc64_stub_entry *entry, 347 struct ppc64_stub_entry *entry,
298 struct ppc64_opd_entry *opd, 348 unsigned long addr,
299 struct module *me) 349 struct module *me)
300{ 350{
301 long reladdr; 351 long reladdr;
@@ -313,33 +363,31 @@ static inline int create_stub(Elf64_Shdr *sechdrs,
313 363
314 entry->jump[0] |= PPC_HA(reladdr); 364 entry->jump[0] |= PPC_HA(reladdr);
315 entry->jump[1] |= PPC_LO(reladdr); 365 entry->jump[1] |= PPC_LO(reladdr);
316 entry->opd.funcaddr = opd->funcaddr; 366 entry->funcdata = func_desc(addr);
317 entry->opd.r2 = opd->r2;
318 return 1; 367 return 1;
319} 368}
320 369
321/* Create stub to jump to function described in this OPD: we need the 370/* Create stub to jump to function described in this OPD/ptr: we need the
322 stub to set up the TOC ptr (r2) for the function. */ 371 stub to set up the TOC ptr (r2) for the function. */
323static unsigned long stub_for_addr(Elf64_Shdr *sechdrs, 372static unsigned long stub_for_addr(Elf64_Shdr *sechdrs,
324 unsigned long opdaddr, 373 unsigned long addr,
325 struct module *me) 374 struct module *me)
326{ 375{
327 struct ppc64_stub_entry *stubs; 376 struct ppc64_stub_entry *stubs;
328 struct ppc64_opd_entry *opd = (void *)opdaddr;
329 unsigned int i, num_stubs; 377 unsigned int i, num_stubs;
330 378
331 num_stubs = sechdrs[me->arch.stubs_section].sh_size / sizeof(*stubs); 379 num_stubs = sechdrs[me->arch.stubs_section].sh_size / sizeof(*stubs);
332 380
333 /* Find this stub, or if that fails, the next avail. entry */ 381 /* Find this stub, or if that fails, the next avail. entry */
334 stubs = (void *)sechdrs[me->arch.stubs_section].sh_addr; 382 stubs = (void *)sechdrs[me->arch.stubs_section].sh_addr;
335 for (i = 0; stubs[i].opd.funcaddr; i++) { 383 for (i = 0; stub_func_addr(stubs[i].funcdata); i++) {
336 BUG_ON(i >= num_stubs); 384 BUG_ON(i >= num_stubs);
337 385
338 if (stubs[i].opd.funcaddr == opd->funcaddr) 386 if (stub_func_addr(stubs[i].funcdata) == func_addr(addr))
339 return (unsigned long)&stubs[i]; 387 return (unsigned long)&stubs[i];
340 } 388 }
341 389
342 if (!create_stub(sechdrs, &stubs[i], opd, me)) 390 if (!create_stub(sechdrs, &stubs[i], addr, me))
343 return 0; 391 return 0;
344 392
345 return (unsigned long)&stubs[i]; 393 return (unsigned long)&stubs[i];
@@ -480,7 +528,8 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
480 return -ENOENT; 528 return -ENOENT;
481 if (!restore_r2((u32 *)location + 1, me)) 529 if (!restore_r2((u32 *)location + 1, me))
482 return -ENOEXEC; 530 return -ENOEXEC;
483 } 531 } else
532 value += local_entry_offset(sym);
484 533
485 /* Convert value to relative */ 534 /* Convert value to relative */
486 value -= (unsigned long)location; 535 value -= (unsigned long)location;