diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2014-03-18 20:12:22 -0400 |
---|---|---|
committer | Anton Blanchard <anton@samba.org> | 2014-04-22 20:05:32 -0400 |
commit | 008d7a914efee6ee5afe59bcc46d3d6b60657598 (patch) | |
tree | d012fae16e851f7dcffb56b5fc7f263aa75a36be | |
parent | 5c729a115e4727fd71308e4d68846f64fa460ead (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.c | 73 |
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. */ | ||
48 | typedef unsigned long func_desc_t; | ||
49 | |||
50 | static func_desc_t func_desc(unsigned long addr) | ||
51 | { | ||
52 | return addr; | ||
53 | } | ||
54 | static unsigned long func_addr(unsigned long addr) | ||
55 | { | ||
56 | return addr; | ||
57 | } | ||
58 | static 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 | |||
69 | static 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. */ | ||
80 | typedef struct ppc64_opd_entry func_desc_t; | ||
81 | |||
82 | static func_desc_t func_desc(unsigned long addr) | ||
83 | { | ||
84 | return *(struct ppc64_opd_entry *)addr; | ||
85 | } | ||
86 | static unsigned long func_addr(unsigned long addr) | ||
87 | { | ||
88 | return func_desc(addr).funcaddr; | ||
89 | } | ||
90 | static unsigned long stub_func_addr(func_desc_t func) | ||
91 | { | ||
92 | return func.funcaddr; | ||
93 | } | ||
94 | static 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. */ |
296 | static inline int create_stub(Elf64_Shdr *sechdrs, | 346 | static 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. */ |
323 | static unsigned long stub_for_addr(Elf64_Shdr *sechdrs, | 372 | static 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; |