diff options
Diffstat (limited to 'recipes/uclibc/uclibc-0.9.28/thumb-resolve.patch')
-rw-r--r-- | recipes/uclibc/uclibc-0.9.28/thumb-resolve.patch | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/recipes/uclibc/uclibc-0.9.28/thumb-resolve.patch b/recipes/uclibc/uclibc-0.9.28/thumb-resolve.patch new file mode 100644 index 0000000000..0a99a7d9bc --- /dev/null +++ b/recipes/uclibc/uclibc-0.9.28/thumb-resolve.patch @@ -0,0 +1,289 @@ +# This change reimplements the ARM _dl_linux_resolve entry point - this is +# called to resolve DLL PLT entries. The assembler is changed to be thumb +# compatible and slightly faster, the C function, _dl_linux_resolver (note +# the extra r) is changed to take a byte address in place of an 8 byte +# count (faster in caller and callee, and slightly easier to understand). +# +--- uClibc-0.9.28/.pc/thumb-resolve.patch/ldso/ldso/arm/elfinterp.c 2005-08-17 15:49:41.000000000 -0700 ++++ uClibc-0.9.28/ldso/ldso/arm/elfinterp.c 2005-09-17 12:55:26.379172744 -0700 +@@ -55,7 +55,7 @@ + + rel_addr = (ELF_RELOC *) tpnt->dynamic_info[DT_JMPREL]; + +- this_reloc = rel_addr + (reloc_entry >> 3); ++ this_reloc = rel_addr + reloc_entry; + reloc_type = ELF32_R_TYPE(this_reloc->r_info); + symtab_index = ELF32_R_SYM(this_reloc->r_info); + +@@ -84,7 +84,9 @@ + _dl_exit(1); + }; + #if defined (__SUPPORT_LD_DEBUG__) ++#if !defined __SUPPORT_LD_DEBUG_EARLY__ + if ((unsigned long) got_addr < 0x40000000) ++#endif + { + if (_dl_debug_bindings) + { +--- uClibc-0.9.28/.pc/thumb-resolve.patch/ldso/ldso/arm/resolve.S 2005-08-17 15:49:41.000000000 -0700 ++++ uClibc-0.9.28/ldso/ldso/arm/resolve.S 2005-09-17 11:02:27.860627464 -0700 +@@ -1,43 +1,163 @@ + /* +- * This function is _not_ called directly. It is jumped to (so no return +- * address is on the stack) when attempting to use a symbol that has not yet +- * been resolved. The first time a jump symbol (such as a function call inside +- * a shared library) is used (before it gets resolved) it will jump here to +- * _dl_linux_resolve. When we get called the stack looks like this: +- * reloc_entry +- * tpnt +- * +- * This function saves all the registers, puts a copy of reloc_entry and tpnt +- * on the stack (as function arguments) then make the function call +- * _dl_linux_resolver(tpnt, reloc_entry). _dl_linux_resolver() figures out +- * where the jump symbol is _really_ supposed to have jumped to and returns +- * that to us. Once we have that, we overwrite tpnt with this fixed up +- * address. We then clean up after ourselves, put all the registers back how we +- * found them, then we jump to the fixed up address, which is where the jump +- * symbol that got us here really wanted to jump to in the first place. +- * -Erik Andersen ++ * On ARM the PLT contains the following three instructions (for ARM calls): ++ * ++ * add ip, pc, #0xNN00000 ++ * add ip, ip, #0xNN000 ++ * ldr pc, [ip, #0xNNN]! ++ * ++ * So that, effectively, causes the following to happen: ++ * ++ * ip := pc+0x0NNNNNNN ++ * pc := *ip ++ * ++ * For thumb the above fragment is preceded by "bx pc, nop" to switch to ARM ++ * mode and the thumb 'bl' must go to PLT-4 - the PLT entry is expanded by ++ * four bytes to accomodate the trampoline code. ++ * ++ * 0x0NNNNNNN is the offset of the GOT entry for this function relative to ++ * the PLT entry for this function (where the code is). So the code in the ++ * PLT causes a branch to whatever is in the GOT, leaving the actual address ++ * of the GOT entry in ip. (Note that the GOT must follow the PLT - the ++ * added value is 28 bit unsigned). ++ * ++ * ip is a pointer to the GOT entry for this function, the first time round ++ * *ip points to this code: ++ * ++ * str lr, [sp, #-4]! @ save lr ++ * ldr lr, [pc, #4] @ lr := *dat (&GOT_TABLE[0]-.) ++ * add lr, pc, lr @ lr += &dat (so lr == &GOT_TABLE[0]) ++ * ldr pc, [lr, #8]! @ pc := GOT_TABLE[2] ++ *dat: .long &GOT_TABLE[0] - . ++ * ++ * (this code is actually held in the first entry of the PLT). The code ++ * preserves lr then uses it as a scratch register (this preserves the ip ++ * value calculated above). GOT_TABLE[2] is initialized by INIT_GOT in ++ * dl-sysdep.h to point to _dl_linux_resolve - this function. The first ++ * three entries in the GOT are reserved, then they are followed by the ++ * entries for the PLT entries, in order. ++ * ++ * The linker initialises the following (non-reserved) GOT entries to ++ * the offset of the PLT with an associated relocation so that on load ++ * the entry is relocated to point to the PLT - the above code. ++ * ++ * The net effect of all this is that on the first call to an external (as ++ * yet unresolved) function all seven of the above instructions are ++ * executed in sequence and the program ends up executing _dl_linux_resolve ++ * with the following important values in registers: ++ * ++ * ip - a pointer to the GOT entry for the as yet unresolved function ++ * lr - &GOT_TABLE[2] ++ * ++ * GOT_TABLE[2] has already been initialised to _dl_linux_resolve, and ++ * GOT_TABLE[1] is a pointer to the (elf_resolve*) from INIT_GOT. ++ * _dl_linux_resolve unfrobnicates the ip and lr values to obtain arguments ++ * for a call to _dl_linux_resolver (not the additional 'r' on the end) - ++ * this is in elfinterp.c in this directory. The call takes arguments: ++ * ++ * _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry) ++ * ++ * And returns the address of the function, it also overwrites the GOT ++ * table entry so that the next time round only the first code fragment will ++ * be executed - it will call the function directly. ++ * ++ * [[Of course, this simply doesn't work on ARM 4T with a thumb target - because ++ * 4T did not do the thumb/arm change on ldr pc! It can be made to work by ++ * changing _dl_linux_resolver to return __%s_from_arm for an STT_TFUNC, but ++ * this hasn't been done, and there is no guarantee that the linker generated ++ * that glue anyway.]] ++ * ++ * _dl_linux_resolve gets the arguments to call the resolver as follows: ++ * ++ * tpnt GOT_TABLE[1], [lr-4] ++ * reloc-entry &GOT-&GOT_TABLE[3], (ip - lr - 4)/4 ++ * ++ * (I.e. 'GOT' means the table entry for this function, the thing for which ++ * ip holds the address.) The reloc-entry is passed as an index, since ++ * since the GOT table has 4 byte entries the code needs to divide this by 4 ++ * to get the actual index. ++ * ++ * John Bowler, August 13, 2005 - determined by experiment and examination ++ * of generated ARM code (there was no documentation...) ++ * ++ * This code is all ARM code - not thumb - _dl_linux_resolver may, itself, ++ * be thumb, in which case the linker will insert the appropriate glue. A ++ * call from thumb to the PLT hits the trampoline code described above. ++ * This code (now) builds a proper stack frame. ++ * ++ * The code does *not* set sb (r9,v6) - to do that the basic PLT instructions ++ * would need to save sb and load the new value and that would require ++ * support in the linker since it generates those instructions. (Also note ++ * that linux/uclibc seems to be using r10 - sl - as a PIC base register - see ++ * dl-startup.c). + */ +- +-#define sl r10 +-#define fp r11 +-#define ip r12 ++#include <sys/syscall.h> + + .text ++.align 4 @ 16 byte boundary and there are 32 bytes below (arm case) ++#if !defined(__thumb__) ++.arm + .globl _dl_linux_resolve + .type _dl_linux_resolve,%function +-.align 4; + + _dl_linux_resolve: +- stmdb sp!, {r0, r1, r2, r3, sl, fp} +- sub r1, ip, lr +- sub r1, r1, #4 +- add r1, r1, r1 +- ldr r0, [lr, #-4] +- mov r3,r0 ++ @ _dl_linux_resolver is a standard subroutine call, therefore it ++ @ preserves everything except r0-r3 (a1-a4), ip and lr. This ++ @ function must branch to the real function, and that expects ++ @ r0-r3 and lr to be as they were before the whole PLT stuff - ++ @ ip can be trashed. ++ stmdb sp!, {r0-r3} ++ ldr r0, [lr, #-4] @ r0 := [lr-4] (GOT_TABLE[1]) ++ sub r1, lr, ip @ r1 := (lr-ip) (a multple of 4) ++ mvn r1, r1, ASR #2 @ r1 := ~((lr-ip)>>2), since -x = (1+~x) ++ @ ~x = -x-1, therefore ~(r1>>2) = (-((lr-ip)>>2)-1) ++ @ = - ((lr-ip)/4) - 1 = (ip - lr - 4)/4, as required + + bl _dl_linux_resolver + +- mov ip, r0 +- ldmia sp!, {r0, r1, r2, r3, sl, fp, lr} +- mov pc,ip ++ mov ip, r0 ++ ldmia sp!, {r0-r3, lr} ++#if defined(__THUMB_INTERWORK__) ++ bx ip ++#else ++ mov pc, ip ++#endif ++.size _dl_linux_resolve, .-_dl_linux_resolve ++#else ++ @ In the thumb case _dl_linux_resolver is thumb. If a bl is used ++ @ from arm code the linker will insert a stub call which, with ++ @ binutils 2.16, is not PIC. Since this code is accessed by an ++ @ ldr pc the reasonable fix is to make _dl_linux_resolve thumb too. ++.thumb ++.globl _dl_linux_resolve ++.thumb_func ++.type _dl_linux_resolve,%function ++ ++_dl_linux_resolve: ++ @ _dl_linux_resolver is a standard subroutine call, therefore it ++ @ preserves everything except r0-r3 (a1-a4), ip and lr. This ++ @ function must branch to the real function, and that expects ++ @ r0-r3 and lr to be as they were before the whole PLT stuff - ++ @ ip can be trashed. ++ push {r0-r3} ++ mov r1, lr @ &GOT_TABLE[2] ++ sub r0, r1, #4 ++ mov r2, ip @ &GOT[n] ++ ldr r0, [r0] @ r0 := GOT_TABLE[1] ++ @ for the function call r1 := n-3 ++ sub r1, r2 ++ asr r1, r1, #2 ++ mvn r1, r1 @ exactly as in the arm code above ++ ++ bl _dl_linux_resolver ++ ++ @ r0 contains the branch address, the return address is above ++ @ the saved r0..r3 ++ mov ip, r0 ++ ldr r1, [sp, #16] ++ mov lr, r1 ++ pop {r0-r3} ++ add sp, #4 ++ bx ip ++ + .size _dl_linux_resolve, .-_dl_linux_resolve ++#endif +--- uClibc-0.9.28/.pc/thumb-resolve.patch/ldso/ldso/dl-hash.c 2005-08-17 15:49:41.000000000 -0700 ++++ uClibc-0.9.28/ldso/ldso/dl-hash.c 2005-09-21 18:56:31.181689732 -0700 +@@ -182,28 +182,52 @@ + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]); + + for (si = tpnt->elf_buckets[hn]; si != STN_UNDEF; si = tpnt->chains[si]) { ++ char *result; + sym = &symtab[si]; + +- if (type_class & (sym->st_shndx == SHN_UNDEF)) ++ if (sym->st_shndx == SHN_UNDEF) + continue; +- if (_dl_strcmp(strtab + sym->st_name, name) != 0) ++ if (ELF_ST_TYPE(sym->st_info) > STT_FUNC ++#if defined(__arm__) || defined(__thumb__) ++ /* On ARM (only) STT_ARM_TFUNC is a function ++ * and has a value >STT_FUNC, so this must ++ * be checked specially. ++ */ ++ && ELF_ST_TYPE(sym->st_info) != STT_ARM_TFUNC ++#endif ++ ) + continue; +- if (sym->st_value == 0) ++ if (_dl_strcmp(strtab + sym->st_name, name) != 0) + continue; +- if (ELF_ST_TYPE(sym->st_info) > STT_FUNC) ++#if 0 ++ /* I don't know how to write this test - need to test shndx ++ * to see if it is the PLT for this module. ++ */ ++ if ((type_class & ELF_RTYPE_CLASS_PLT) && some test) + continue; ++#endif + ++#if defined(__arm__) || defined(__thumb__) ++ /* On ARM the caller needs to know that STT_ARM_TFUNC ++ * is a thumb function call, this is now indicated by ++ * setting the low bit of the value (and newer binutils ++ * will do this and record STT_FUNC). ++ */ ++ result = (char*)tpnt->loadaddr + (sym->st_value | ++ (ELF_ST_TYPE(sym->st_info) == STT_ARM_TFUNC)); ++#else ++ result = (char*)tpnt->loadaddr + sym->st_value; ++#endif + switch (ELF_ST_BIND(sym->st_info)) { + case STB_WEAK: +-#if 0 +-/* Perhaps we should support old style weak symbol handling +- * per what glibc does when you export LD_DYNAMIC_WEAK */ ++ /* Record for use later if we can't find a global. */ + if (!weak_result) +- weak_result = (char *)tpnt->loadaddr + sym->st_value; ++ weak_result = result; + break; +-#endif ++ + case STB_GLOBAL: +- return (char*)tpnt->loadaddr + sym->st_value; ++ return result; ++ + default: /* Local symbols not handled here */ + break; + } |