Usermode/ld-acess - Large cleanup to ELF dynamic linking
[tpg/acess2.git] / Usermode / Libraries / ld-acess.so_src / elf.c
1 /*
2  * Acess2 Dynamic Linker
3  * - By John Hodge (thePowersGang)
4  *
5  * elf.c
6  * - ELF32/ELF64 relocation
7  */
8 #ifndef DEBUG   // This code is #include'd from the kernel, so DEBUG may already be defined
9 # define DEBUG  0
10 #endif
11
12 #ifndef PAGE_SIZE
13 # define PAGE_SIZE      4096
14 #endif
15
16 #include "common.h"
17 #include <stdint.h>
18 #ifndef assert
19 # include <assert.h>
20 #endif
21 #include "elf32.h"
22 #include "elf64.h"
23
24 #if DEBUG
25 # define DEBUG_OUT(...) SysDebug(__VA_ARGS__)
26 #else
27 # define DEBUG_OUT(...) do{}while(0)    //((void)(__VA_ARGS__))
28 #endif
29
30 #define WARNING(f,...)  SysDebug("WARN: "f ,## __VA_ARGS__)     // Malformed file
31 #define NOTICE(f,...)   SysDebug("NOTICE: "f ,## __VA_ARGS__)   // Missing relocation
32 #define TRACE(f,...)    DEBUG_OUT("TRACE:%s:%i "f, __func__, __LINE__ ,## __VA_ARGS__)  // Debugging trace
33
34 #ifndef DISABLE_ELF64
35 # define SUPPORT_ELF64
36 #endif
37
38 typedef struct
39 {
40         void    *Base;
41         intptr_t        iBaseDiff;
42         const char      *strtab;
43         Elf32_Sym       *symtab;
44 } tElfRelocInfo;
45
46 typedef int tElf32RelocFcn(tElfRelocInfo *Info, uint32_t t_info, uint32_t *ptr, Elf32_Addr addend, int bRela);
47
48 // === CONSTANTS ===
49 #if DEBUG
50 //static const char     *csaDT_NAMES[] = {"DT_NULL", "DT_NEEDED", "DT_PLTRELSZ", "DT_PLTGOT", "DT_HASH", "DT_STRTAB", "DT_SYMTAB", "DT_RELA", "DT_RELASZ", "DT_RELAENT", "DT_STRSZ", "DT_SYMENT", "DT_INIT", "DT_FINI", "DT_SONAME", "DT_RPATH", "DT_SYMBOLIC", "DT_REL", "DT_RELSZ", "DT_RELENT", "DT_PLTREL", "DT_DEBUG", "DT_TEXTREL", "DT_JMPREL"};
51 //static const char     *csaR_NAMES[] = {"R_386_NONE", "R_386_32", "R_386_PC32", "R_386_GOT32", "R_386_PLT32", "R_386_COPY", "R_386_GLOB_DAT", "R_386_JMP_SLOT", "R_386_RELATIVE", "R_386_GOTOFF", "R_386_GOTPC", "R_386_LAST"};
52 #endif
53
54 // === PROTOTYPES ===
55 void    *ElfRelocate(void *Base, char **envp, const char *Filename);
56  int    ElfGetSymbol(void *Base, const char *Name, void **Ret, size_t *Size);
57 void    *Elf32Relocate(void *Base, char **envp, const char *Filename);
58  int    Elf32GetSymbolVars(void *Base, Elf32_Sym** symtab, Elf32_Word** pBuckets, const char **dynstrtab, uintptr_t* piBaseDiff);
59  int    Elf32GetSymbolInfo(void *Base, const char *Name, void **Addr, size_t *Size, int* Section, int *Binding, int *Type);
60  int    Elf32GetSymbol(void *Base, const char *Name, void **Ret, size_t *Size);
61 tElf32RelocFcn  elf_doRelocate_386;
62 tElf32RelocFcn  elf_doRelocate_arm;
63 tElf32RelocFcn  elf_doRelocate_unk;
64 #ifdef SUPPORT_ELF64
65 int     _Elf64DoReloc_X86_64(void *Base, const char *strtab, Elf64_Sym *symtab, Elf64_Xword r_info, void *ptr, Elf64_Sxword addend);
66 void    *Elf64Relocate(void *Base, char **envp, const char *Filename);
67  int    Elf64GetSymbol(void *Base, const char *Name, void **Ret, size_t *Size);
68 #endif
69  int    Elf32GetSymbolReloc(tElfRelocInfo *Info, const Elf32_Sym *Symbol, void **Ret, size_t *Size);
70 uint32_t        ElfHashString(const char *name);
71
72 // === CODE ===
73 /**
74  * \fn int ElfRelocate(void *Base, char **envp, const char *Filename)
75  * \brief Relocates a loaded ELF Executable
76  */
77 void *ElfRelocate(void *Base, char **envp, const char *Filename)
78 {
79         Elf32_Ehdr      *hdr = Base;
80         
81         switch(hdr->e_ident[4])
82         {
83         case ELFCLASS32:
84                 return Elf32Relocate(Base, envp, Filename);
85 #ifdef SUPPORT_ELF64
86         case ELFCLASS64:
87                 return Elf64Relocate(Base, envp, Filename);
88 #endif
89         default:
90                 SysDebug("ld-acess - ElfRelocate: Unknown file class %i", hdr->e_ident[4]);
91                 return NULL;
92         }
93 }
94
95 /**
96  * \fn int ElfGetSymbol(Uint Base, const char *name, void **ret)
97  */
98 int ElfGetSymbol(void *Base, const char *Name, void **ret, size_t *Size)
99 {
100         Elf32_Ehdr      *hdr = Base;
101
102         switch(hdr->e_ident[4])
103         {
104         case ELFCLASS32:
105                 return Elf32GetSymbol(Base, Name, ret, Size);
106 #ifdef SUPPORT_ELF64
107         case ELFCLASS64:
108                 return Elf64GetSymbol(Base, Name, ret, Size);
109 #endif
110         default:
111                 SysDebug("ld-acess - ElfRelocate: Unknown file class %i", hdr->e_ident[4]);
112                 return 0;
113         }
114 }
115
116 int elf_doRelocate_386(tElfRelocInfo *Info, uint32_t r_info, uint32_t *ptr, Elf32_Addr addend, int bRela)
117 {
118         const Elf32_Sym *sym = &Info->symtab[ ELF32_R_SYM(r_info) ];
119         void    *symval = (void*)sym->st_value;
120         size_t  size = sym->st_size;
121         TRACE("%i '%s'", ELF32_R_TYPE(r_info), Info->strtab + sym->st_name);
122         switch( ELF32_R_TYPE(r_info) )
123         {
124         // Standard 32 Bit Relocation (S+A)
125         case R_386_32:
126                 TRACE("R_386_32 *0x%x = %p + 0x%x", ptr, symval, addend);
127                 *ptr = (intptr_t)symval + addend;
128                 break;
129                 
130         // 32 Bit Relocation wrt. Offset (S+A-P)
131         case R_386_PC32:
132                 TRACE("R_386_PC32 *0x%x = 0x%x + 0x%p - 0x%x", ptr, *ptr, symval, (intptr_t)ptr );
133                 *ptr = (intptr_t)symval + addend - (intptr_t)ptr;
134                 //*ptr = val + addend - ((Uint)ptr - iBaseDiff);
135                 break;
136
137         // Absolute Value of a symbol (S)
138         case R_386_GLOB_DAT:
139                 TRACE("R_386_GLOB_DAT *0x%x = %p", ptr, symval);        if(0)
140         case R_386_JMP_SLOT:
141                 TRACE("R_386_JMP_SLOT *0x%x = %p", ptr, symval);
142                 *ptr = (intptr_t)symval;
143                 break;
144
145         // Base Address (B+A)
146         case R_386_RELATIVE:
147                 TRACE("R_386_RELATIVE *0x%x = 0x%x + 0x%x", ptr, Info->iBaseDiff, addend);
148                 *ptr = Info->iBaseDiff + addend;
149                 break;
150
151         case R_386_COPY: {
152                 void *old_symval = symval;
153                 GetSymbol(Info->strtab + sym->st_name, &symval, &size, Info->Base);
154                 if( symval == old_symval ) {
155                         WARNING("Can't find required external symbol '%s'", Info->strtab + sym->st_name);
156                         return 1;
157                 }
158                 TRACE("R_386_COPY (%p, %p, %i)", ptr, symval, size);
159                 memcpy(ptr, symval, size);
160                 break; }
161
162         default:
163                 WARNING("Unknown relocation %i", ELF32_ST_TYPE(r_info));
164                 return 2;
165         }
166         return 0;
167 }
168
169 int elf_doRelocate_arm(tElfRelocInfo *Info, uint32_t r_info, uint32_t *ptr, Elf32_Addr addend, int bRela)
170 {
171         const Elf32_Sym *sym = &Info->symtab[ ELF32_R_SYM(r_info) ];
172         void    *symval = (void*)sym->st_value;
173         size_t  size = sym->st_size;
174         TRACE("%i '%s'", ELF32_R_TYPE(r_info), Info->strtab + sym->st_name);
175         uintptr_t       val = (uintptr_t)symval;
176         switch( ELF32_R_TYPE(r_info) )
177         {
178         // (S + A) | T
179         case R_ARM_ABS32:
180                 TRACE("R_ARM_ABS32 %p (%p + %x)", ptr, symval, addend);
181                 *ptr = val + addend;
182                 break;
183         case R_ARM_GLOB_DAT:
184                 TRACE("R_ARM_GLOB_DAT %p (%p + %x)", ptr, symval, addend);
185                 *ptr = val + addend;
186                 break;
187         case R_ARM_JUMP_SLOT:
188                 if(!bRela)      addend = 0;
189                 TRACE("R_ARM_JUMP_SLOT %p (%p + %x)", ptr, symval, addend);
190                 *ptr = val + addend;
191                 break;
192         // Copy
193         case R_ARM_COPY:
194                 TRACE("R_ARM_COPY (%p, %p, %i)", ptr, symval, size);
195                 memcpy(ptr, symval, size);
196                 break;
197         // Delta between link and runtime locations + A
198         case R_ARM_RELATIVE:
199                 TRACE("R_ARM_RELATIVE %p (0x%x + 0x%x)", ptr, Info->iBaseDiff, addend);
200                 if(ELF32_R_SYM(r_info) != 0) {
201                         // TODO: Get delta for a symbol
202                         WARNING("TODO - Implment R_ARM_RELATIVE for symbols");
203                         return 2;
204                 }
205                 else {
206                         *ptr = Info->iBaseDiff + addend;
207                 }
208                 break;
209         default:
210                 WARNING("Unknown Relocation, %i", ELF32_R_TYPE(r_info));
211                 return 2;
212         }
213         return 0;
214 }
215
216 int elf_doRelocate_unk(tElfRelocInfo *Info, uint32_t r_info, uint32_t *ptr, Elf32_Addr addend, int bRela)
217 {
218         return 1;
219 }
220
221 void *Elf32Relocate(void *Base, char **envp, const char *Filename)
222 {
223         const Elf32_Ehdr        *hdr = Base;
224         char    *libPath;
225         intptr_t        iRealBase = -1;
226         intptr_t        iBaseDiff;
227         Elf32_Rel       *rel = NULL;
228         Elf32_Rela      *rela = NULL;
229         void    *plt = NULL;
230          int    relSz=0, relEntSz=8;
231          int    relaSz=0, relaEntSz=8;
232          int    pltSz=0, pltType=0;
233         Elf32_Dyn       *dynamicTab = NULL;     // Dynamic Table Pointer
234         
235         TRACE("(Base=0x%x)", Base);
236         
237         // Check magic header
238         
239         
240         // Parse Program Header to get Dynamic Table
241         // - Determine the linked base of the executable
242         const Elf32_Phdr        *phtab = (void*)( (uintptr_t)Base + hdr->phoff );
243         const int iSegmentCount = hdr->phentcount;
244         for(int i = 0; i < iSegmentCount; i ++)
245         {
246                 switch(phtab[i].Type)
247                 {
248                 case PT_LOAD:
249                         // Determine linked base address
250                         if( iRealBase > phtab[i].VAddr)
251                                 iRealBase = phtab[i].VAddr;
252                         break;
253                 case PT_DYNAMIC:
254                         // Find Dynamic Section
255                         if(!dynamicTab) {
256                                 dynamicTab = (void *) (intptr_t) phtab[i].VAddr;
257                         }
258                         else {
259                                 WARNING("elf_relocate: Multiple PT_DYNAMIC segments");
260                         }
261                         break;
262                 }
263         }
264         
265         // Page Align real base
266         iRealBase &= ~0xFFF;
267         TRACE("True Base = 0x%x, Compiled Base = 0x%x", Base, iRealBase);
268         
269         // Adjust "Real" Base
270         iBaseDiff = (intptr_t)Base - iRealBase;
271
272         // Check if a PT_DYNAMIC segement was found
273         if(!dynamicTab) {
274                 SysDebug(" elf_relocate: No PT_DYNAMIC segment in image %p, returning", Base);
275                 return (void *)(intptr_t)(hdr->entrypoint + iBaseDiff);
276         }
277
278         // Allow writing to read-only segments, just in case they need to be relocated
279         // - Will be reversed at the end of the function
280         for( int i = 0; i < iSegmentCount; i ++ )
281         {
282                 if(phtab[i].Type == PT_LOAD && !(phtab[i].Flags & PF_W) ) {
283                         uintptr_t       addr = phtab[i].VAddr + iBaseDiff;
284                         uintptr_t       end = addr + phtab[i].MemSize;
285                         for( ; addr < end; addr += PAGE_SIZE )
286                                 _SysSetMemFlags(addr, 0, 1);    // Unset RO
287                 }
288         }
289
290         // Adjust Dynamic Table
291         dynamicTab = (void *)( (intptr_t)dynamicTab + iBaseDiff );
292         
293         // === Get Symbol table and String Table ===
294         char    *dynstrtab = NULL;      // .dynamic String Table
295         Elf32_Sym       *dynsymtab = NULL;
296         Elf32_Word      *hashtable = NULL;
297          int    iSymCount = 0;
298         for( int j = 0; dynamicTab[j].d_tag != DT_NULL; j++)
299         {
300                 const Elf32_Dyn *dt = &dynamicTab[j];
301                 switch(dt->d_tag)
302                 {
303                 // --- Symbol Table ---
304                 case DT_SYMTAB:
305                         TRACE("DYNAMIC Symbol Table 0x%x (0x%x)", dt->d_val, dt->d_val + iBaseDiff);
306                         dynsymtab = (void*)((intptr_t)dt->d_val + iBaseDiff);
307                         break;
308                 // --- String Table ---
309                 case DT_STRTAB:
310                         TRACE("DYNAMIC String Table 0x%x (0x%x)", dt->d_val, dt->d_val + iBaseDiff);
311                         dynstrtab = (void*)((intptr_t)dt->d_val + iBaseDiff);
312                         break;
313                 // --- Hash Table --
314                 case DT_HASH:
315                         TRACE("DYNAMIC Hash table %p (%p)", dt->d_val, dt->d_val + iBaseDiff);
316                         hashtable = (void*)((intptr_t)dt->d_val + iBaseDiff);
317                         iSymCount = hashtable[1];
318                         break;
319                 }
320         }
321
322         if(dynsymtab == NULL) {
323                 SysDebug("ld-acess.so - WARNING: No Dynamic Symbol table in %p, returning", hdr);
324                 return (void *)(intptr_t) (hdr->entrypoint + iBaseDiff);
325         }
326         
327         // Apply base offset to locally defined symbols
328         // - #0 is defined as ("" SHN_UNDEF), so skip it
329         for( int i = 1; i < iSymCount; i ++ )
330         {
331                 Elf32_Sym       *sym = &dynsymtab[i];
332                 const char *name = dynstrtab + sym->st_name;
333                 (void)name;
334                 if( sym->st_shndx == SHN_UNDEF )
335                 {
336                         TRACE("Sym %i'%s' deferred (SHN_UNDEF)", i, name);
337                 }
338                 else if( sym->st_shndx == SHN_ABS )
339                 {
340                         // Leave as is
341                         SysDebug("Sym %i'%s' untouched", i, name);
342                 }
343                 else
344                 {
345                         // TODO: What about weak locally-defined symbols?
346                         if( ELF32_ST_BIND(sym->st_info) == STB_WEAK )
347                         {
348                                 WARNING("TODO: Weak bound local symbols '%s'", name);
349                                 assert(ELF32_ST_BIND(sym->st_info) != STB_WEAK);
350                                 return NULL;
351                         }
352                         TRACE("Sym %i'%s' %p += 0x%x", i, name, sym->st_value, iBaseDiff);
353                         sym->st_value += iBaseDiff;
354                 }
355         }
356
357         // === Add to loaded list (can be imported now) ===
358         AddLoaded( Filename, Base );
359
360         // === Parse Relocation Data ===
361         TRACE("dynamicTab = 0x%x", dynamicTab);
362         for( int j = 0; dynamicTab[j].d_tag != DT_NULL; j++)
363         {
364                 const Elf32_Dyn *dt = &dynamicTab[j];
365                 switch(dt->d_tag)
366                 {
367                 // --- Shared Library Name ---
368                 case DT_SONAME:
369                         TRACE(".so Name '%s'", dynstrtab + dt->d_val);
370                         break;
371                 // --- Needed Library ---
372                 case DT_NEEDED:
373                         //assert(dt->d_val <= sizeof_dynstrtab);
374                         libPath = dynstrtab + dt->d_val;
375                         TRACE(" Required Library '%s'", libPath);
376                         if(LoadLibrary(libPath, NULL, envp) == 0) {
377                                 SysDebug("Unable to load required library '%s'", libPath);
378                                 return 0;
379                         }
380                         TRACE(" Lib loaded");
381                         break;
382                 // --- PLT/GOT ---
383 //              case DT_PLTGOT: pltgot = (void*)(iBaseDiff + dt->d_val);        break;
384                 case DT_JMPREL: plt = (void*)(iBaseDiff + dt->d_val);   break;
385                 case DT_PLTREL: pltType = dt->d_val;    break;
386                 case DT_PLTRELSZ:       pltSz = dt->d_val;      break;
387                 
388                 // --- Relocation ---
389                 case DT_REL:    rel = (void*)(iBaseDiff + dt->d_val);   break;
390                 case DT_RELSZ:  relSz = dt->d_val;      break;
391                 case DT_RELENT: relEntSz = dt->d_val;   break;
392                 case DT_RELA:   rela = (void*)(iBaseDiff + dt->d_val);  break;
393                 case DT_RELASZ: relaSz = dt->d_val;     break;
394                 case DT_RELAENT:        relaEntSz = dt->d_val;  break;
395                 
396                 // --- Symbol Table ---
397                 case DT_SYMTAB:
398                 // --- Hash Table ---
399                 case DT_HASH:
400                 // --- String Table ---
401                 case DT_STRTAB:
402                         break;
403                 
404                 // --- Unknown ---
405                 default:
406                         if(dt->d_tag > DT_JMPREL)       continue;
407                         //DEBUGS(" elf_relocate: %i-%i = %s,0x%x",
408                         //      i,j, csaDT_NAMES[dynamicTab[j].d_tag],dynamicTab[j].d_val);
409                         break;
410                 }
411         }
412         
413         // Resolve symbols (second pass)
414         // - #0 is defined as ("" SHN_UNDEF), so skip it
415          int    fail = 0;
416         for( int i = 1; i < iSymCount; i ++ )
417         {
418                 Elf32_Sym       *sym = &dynsymtab[i];
419                 const char *name = dynstrtab + sym->st_name;
420                 if( sym->st_shndx == SHN_UNDEF )
421                 {
422                         void *newval;
423                         size_t  newsize;
424                         if( !GetSymbol(name, &newval, &newsize, Base) ) {
425                                 if( ELF32_ST_BIND(sym->st_info) != STB_WEAK ) {
426                                         // Not a weak binding, set fail and move on
427                                         WARNING("Elf32Relocate: Can't find required symbol '%s' for '%s'",
428                                                 name, Filename);
429                                         fail = 1;
430                                         continue ;
431                                 }
432                                 // Leave the symbol value as-is
433                         }
434                         else {
435                                 TRACE("Sym %i'%s' bound to %p+0x%x", i, name, newval, newsize);
436                                 sym->st_value = (intptr_t)newval;
437                                 sym->st_size = newsize;
438                         }
439                 }
440                 else if( sym->st_shndx == SHN_ABS )
441                 {
442                         // Leave as is
443                 }
444                 else
445                 {
446                         // Handled previously
447                         // TODO: What about weak locally-defined symbols?
448                         assert( ELF32_ST_BIND(sym->st_info) != STB_WEAK );
449                 }
450         }
451         if( fail ) {
452                 WARNING("Relocation of '%s' failed", Filename);
453                 return NULL;
454         }
455         
456         TRACE("Beginning Relocation on '%s'", Filename);
457
458
459         tElf32RelocFcn  *do_relocate;
460         switch(hdr->machine)
461         {
462         case EM_386:
463                 do_relocate = elf_doRelocate_386;
464                 break;
465         case EM_ARM:
466                 do_relocate = elf_doRelocate_arm;
467                 break;
468         default:
469                 SysDebug("Elf32Relocate: Unknown machine type %i", hdr->machine);
470                 do_relocate = elf_doRelocate_unk;
471                 fail = 1;
472                 break;
473         }
474         
475         TRACE("do_relocate = %p (%p or %p)", do_relocate, &elf_doRelocate_386, &elf_doRelocate_arm);
476
477         #define _doRelocate(r_info, ptr, bRela, addend) \
478                 do_relocate(&reloc_info, r_info, ptr, addend, bRela);
479
480         tElfRelocInfo   reloc_info = {
481                 .Base = Base,
482                 .iBaseDiff = iBaseDiff,
483                 .strtab = dynstrtab,
484                 .symtab = dynsymtab
485         };
486
487         // Parse Relocation Entries
488         if(rel && relSz)
489         {
490                 Elf32_Word      *ptr;
491                 TRACE("rel=0x%x, relSz=0x%x, relEntSz=0x%x", rel, relSz, relEntSz);
492                 int max = relSz / relEntSz;
493                 for( int i = 0; i < max; i++ )
494                 {
495                         ptr = (void*)(iBaseDiff + rel[i].r_offset);
496                         fail |= _doRelocate(rel[i].r_info, ptr, 0, *ptr);
497                 }
498         }
499         // Parse Relocation Entries
500         if(rela && relaSz)
501         {
502                 Elf32_Word      *ptr;
503                 TRACE("rela=0x%x, relaSz=0x%x, relaEntSz=0x%x", rela, relaSz, relaEntSz);
504                 int count = relaSz / relaEntSz;
505                 for( int i = 0; i < count; i++ )
506                 {
507                         ptr = (void*)(iBaseDiff + rela[i].r_offset);
508                         fail |= _doRelocate(rel[i].r_info, ptr, 1, rela[i].r_addend);
509                 }
510         }
511         
512         // === Process PLT (Procedure Linkage Table) ===
513         if(plt && pltSz)
514         {
515                 Elf32_Word      *ptr;
516                 TRACE("Relocate PLT, plt=0x%x", plt);
517                 if(pltType == DT_REL)
518                 {
519                         Elf32_Rel       *pltRel = plt;
520                         int count = pltSz / sizeof(Elf32_Rel);
521                         TRACE("PLT Reloc Type = Rel, %i entries", count);
522                         for(int i = 0; i < count; i ++)
523                         {
524                                 ptr = (void*)(iBaseDiff + pltRel[i].r_offset);
525                                 fail |= _doRelocate(pltRel[i].r_info, ptr, 0, *ptr);
526                         }
527                 }
528                 else
529                 {
530                         Elf32_Rela      *pltRela = plt;
531                         int count = pltSz / sizeof(Elf32_Rela);
532                         TRACE("PLT Reloc Type = Rela, %i entries", count);
533                         for(int i=0;i<count;i++)
534                         {
535                                 ptr = (void*)(iRealBase + pltRela[i].r_offset);
536                                 fail |= _doRelocate(pltRela[i].r_info, ptr, 1, pltRela[i].r_addend);
537                         }
538                 }
539         }
540
541         // Re-set readonly
542         for( int i = 0; i < iSegmentCount; i ++ )
543         {
544                 // If load and not writable
545                 if(phtab[i].Type == PT_LOAD && !(phtab[i].Flags & PF_W) ) {
546                         uintptr_t       addr = phtab[i].VAddr + iBaseDiff;
547                         uintptr_t       end = addr + phtab[i].MemSize;
548                         for( ; addr < end; addr += PAGE_SIZE )
549                                 _SysSetMemFlags(addr, 1, 1);    // Unset RO
550                 }
551         }
552
553         if( fail ) {
554                 TRACE("ElfRelocate: Failure");
555                 return NULL;
556         }       
557
558         #undef _doRelocate
559
560         TRACE("RETURN 0x%x to %p", hdr->entrypoint + iBaseDiff, __builtin_return_address(0));
561         return (void*)(intptr_t)( hdr->entrypoint + iBaseDiff );
562 }
563
564 int Elf32GetSymbolVars(void *Base, Elf32_Sym** symtab, Elf32_Word** pBuckets, const char **dynstrtab, uintptr_t* piBaseDiff)
565 {
566         Elf32_Dyn       *dynTab = NULL;
567         uintptr_t       iBaseDiff = -1;
568         Elf32_Ehdr *hdr = Base;
569         Elf32_Phdr *phtab = (void*)( (uintptr_t)Base + hdr->phoff );
570         for( int i = 0; i < hdr->phentcount; i ++ )
571         {
572                 if(phtab[i].Type == PT_LOAD && iBaseDiff > phtab[i].VAddr)
573                         iBaseDiff = phtab[i].VAddr;
574                 if( phtab[i].Type == PT_DYNAMIC ) {
575                         dynTab = (void*)(intptr_t)phtab[i].VAddr;
576                 }
577         }
578         if( !dynTab ) {
579                 SysDebug("ERROR - Unable to find DYNAMIC segment in %p", Base);
580                 return 1;
581         }
582         iBaseDiff = (intptr_t)Base - iBaseDiff; // Make iBaseDiff actually the diff
583         dynTab = (void*)( (intptr_t)dynTab + iBaseDiff );
584         for( int i = 0; dynTab[i].d_tag != DT_NULL; i++)
585         {
586                 switch(dynTab[i].d_tag)
587                 {
588                 // --- Symbol Table ---
589                 case DT_SYMTAB:
590                         *symtab = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);       // Rebased in Relocate
591                         break;
592                 case DT_STRTAB:
593                         *dynstrtab = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);
594                         break;
595                 // --- Hash Table --
596                 case DT_HASH:
597                         *pBuckets = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);
598                         break;
599                 }
600         }
601         
602         if( !*symtab ) {
603                 SysDebug("ERRO - No DT_SYMTAB in %p", Base);
604                 return 1;
605         }
606         if( !*pBuckets ) {
607                 SysDebug("ERRO - No DT_HASH in %p", Base);
608                 return 1;
609         }
610         if( !*dynstrtab ) {
611                 SysDebug("ERRO - No DT_STRTAB in %p", Base);
612                 return 1;
613         }
614
615         // ... ok... maybe they haven't been relocated
616         if( (uintptr_t)*symtab < (uintptr_t)Base )
617         {
618                 SysDebug("Executable not yet relocated (symtab,pBuckets,dynstrtab = %p,%p,%p + 0x%x)",
619                         *symtab,*pBuckets,*dynstrtab, iBaseDiff);
620                 *symtab    = (void*)( (uintptr_t)*symtab    + iBaseDiff );
621                 *pBuckets  = (void*)( (uintptr_t)*pBuckets  + iBaseDiff );
622                 *dynstrtab = (void*)( (uintptr_t)*dynstrtab + iBaseDiff );
623         }
624         *piBaseDiff = iBaseDiff;
625         return 0;
626 }
627
628 int Elf32GetSymbolInfo(void *Base, const char *Name, void **Addr, size_t *Size, int* Section, int *Binding, int *Type)
629 {
630         // Locate the tables
631         uintptr_t       iBaseDiff = -1;
632         Elf32_Sym       *symtab = NULL;
633         Elf32_Word      *pBuckets = NULL;
634         const char      *dynstrtab = NULL;
635         if( Elf32GetSymbolVars(Base, &symtab, &pBuckets, &dynstrtab, &iBaseDiff) )
636                 return 1;
637
638         int nbuckets = pBuckets[0];
639 //      int iSymCount = pBuckets[1];
640         pBuckets = &pBuckets[2];
641         Elf32_Word* pChains = &pBuckets[ nbuckets ];
642         assert(pChains);
643
644         // Get hash
645         int iNameHash = ElfHashString(Name);
646         iNameHash %= nbuckets;
647
648         // Walk Chain
649         int idx = pBuckets[ iNameHash ];
650         do {
651                 const Elf32_Sym *sym = &symtab[idx];
652                 assert(sym);
653                 if( strcmp(dynstrtab + sym->st_name, Name) == 0 )
654                 {
655                         TRACE("*sym = {value:0x%x,size:0x%x,info:0x%x,other:0x%x,shndx:%i}",
656                                 sym->st_value, sym->st_size, sym->st_info,
657                                 sym->st_other, sym->st_shndx);
658                         if(Addr)        *Addr = (void*)( sym->st_value );
659                         if(Size)        *Size = sym->st_size;
660                         if(Binding)     *Binding = ELF32_ST_BIND(sym->st_info);
661                         if(Type)        *Type = ELF32_ST_TYPE(sym->st_info);
662                         if(Section)     *Section = sym->st_shndx;
663                         return 0;
664                 }
665         } while( (idx = pChains[idx]) != STN_UNDEF && idx != pBuckets[iNameHash] );
666         
667         TRACE("No symbol");
668         return 1;
669 }
670
671 int Elf32GetSymbol(void *Base, const char *Name, void **ret, size_t *Size)
672 {
673          int    section;
674         TRACE("Elf32GetSymbol(%p,%s,...)", Base, Name);
675         if( Elf32GetSymbolInfo(Base, Name, ret, Size, &section, NULL, NULL) )
676                 return 0;
677         if( section == SHN_UNDEF ) {
678                 TRACE("Elf32GetSymbol: Undefined", *ret, (Size?*Size:0), section);
679                 return 0;
680         }
681         TRACE("Elf32GetSymbol: Found %p+0x%x,section=%i", *ret, (Size?*Size:0), section);
682         return 1;
683 }
684
685 #ifdef SUPPORT_ELF64
686 typedef int (*t_elf64_doreloc)(void *Base, const char *strtab, Elf64_Sym *symtab, Elf64_Xword r_info, void *ptr, Elf64_Sxword addend);
687
688 int _Elf64DoReloc_X86_64(void *Base, const char *strtab, Elf64_Sym *symtab, Elf64_Xword r_info, void *ptr, Elf64_Sxword addend)
689 {
690          int    sym = ELF64_R_SYM(r_info);
691          int    type = ELF64_R_TYPE(r_info);
692         const char      *symname = strtab + symtab[sym].st_name;
693         void    *symval;
694         //DEBUGS("_Elf64DoReloc: %s", symname);
695         switch( type )
696         {
697         case R_X86_64_NONE:
698                 break;
699         case R_X86_64_64:
700                 if( !GetSymbol(symname, &symval, NULL, NULL)  ) return 1;
701                 *(uint64_t*)ptr = (uintptr_t)symval + addend;
702                 break;
703         case R_X86_64_COPY: {
704                 size_t  size;
705                 if( !GetSymbol(symname, &symval, &size, NULL)  )        return 1;
706                 memcpy(ptr, symval, size);
707                 } break;
708         case R_X86_64_GLOB_DAT:
709                 if( !GetSymbol(symname, &symval, NULL, NULL)  ) return 1;
710                 *(uint64_t*)ptr = (uintptr_t)symval;
711                 break;
712         case R_X86_64_JUMP_SLOT:
713                 if( !GetSymbol(symname, &symval, NULL, NULL)  ) return 1;
714                 *(uint64_t*)ptr = (uintptr_t)symval;
715                 break;
716         case R_X86_64_RELATIVE:
717                 *(uint64_t*)ptr = (uintptr_t)Base + addend;
718                 break;
719         default:
720                 SysDebug("ld-acess - _Elf64DoReloc: Unknown relocation type %i", type);
721                 return 2;
722         }
723         //DEBUGS("_Elf64DoReloc: - Good");
724         return 0;
725 }
726
727 void *Elf64Relocate(void *Base, char **envp, const char *Filename)
728 {
729          int    i;
730         Elf64_Ehdr      *hdr = Base;
731         Elf64_Phdr      *phtab;
732         Elf64_Dyn       *dyntab = NULL;
733         Elf64_Addr      compiledBase = -1, baseDiff;
734         Elf64_Sym       *symtab = NULL;
735         char    *strtab = NULL;
736         Elf64_Word      *hashtab = NULL;
737         Elf64_Rel       *rel = NULL;
738          int    rel_count = 0;
739         Elf64_Rela      *rela = NULL;
740          int    rela_count = 0;
741         void    *pltrel = NULL;
742          int    plt_size = 0, plt_type = 0;
743
744         TRACE("hdr = {");
745         TRACE(" e_ident = '%.16s'", hdr->e_ident);
746         TRACE(" e_type = 0x%x", hdr->e_type);
747         TRACE(" e_machine = 0x%x", hdr->e_machine);
748         TRACE(" e_version = 0x%x", hdr->e_version);
749         TRACE(" e_entry = %p", hdr->e_entry);
750         TRACE(" e_phoff = 0x%llx", hdr->e_phoff);
751         TRACE(" e_shoff = 0x%llx", hdr->e_shoff);
752         TRACE(" e_flags = 0x%x", hdr->e_flags);
753         TRACE(" e_ehsize = 0x%x", hdr->e_ehsize);
754         TRACE(" e_phentsize = 0x%x", hdr->e_phentsize);
755         TRACE(" e_phnum = %i", hdr->e_phnum);
756
757         // Scan for the dynamic table (and find the compiled base)
758         phtab = (void*)((uintptr_t)Base + (uintptr_t)hdr->e_phoff);
759         for( i = 0; i < hdr->e_phnum; i ++ )
760         {
761                 if(phtab[i].p_type == PT_DYNAMIC)
762                         dyntab = (void *)(intptr_t)phtab[i].p_vaddr;
763                 if(phtab[i].p_type == PT_LOAD && compiledBase > phtab[i].p_vaddr)
764                         compiledBase = phtab[i].p_vaddr;
765         }
766
767         baseDiff = (uintptr_t)Base - compiledBase;
768
769         TRACE("baseDiff = %p", baseDiff);
770
771         if(dyntab == NULL) {
772                 SysDebug(" Elf64Relocate: No PT_DYNAMIC segment in image %p, returning", Base);
773                 return (void *)(uintptr_t)(hdr->e_entry + baseDiff);
774         }
775
776         dyntab = (void *)(uintptr_t)((uintptr_t)dyntab + baseDiff);
777
778         // Parse the dynamic table (first pass)
779         // - Search for String, Symbol and Hash tables
780         for(i = 0; dyntab[i].d_tag != DT_NULL; i ++)
781         {
782                 switch(dyntab[i].d_tag)
783                 {
784                 case DT_SYMTAB:
785                         dyntab[i].d_un.d_ptr += baseDiff;
786                         symtab = (void *)(uintptr_t)dyntab[i].d_un.d_ptr;
787                         break;
788                 case DT_STRTAB:
789                         dyntab[i].d_un.d_ptr += baseDiff;
790                         strtab = (void *)(uintptr_t)dyntab[i].d_un.d_ptr;
791                         break;
792                 case DT_HASH:
793                         dyntab[i].d_un.d_ptr += baseDiff;
794                         hashtab = (void *)(uintptr_t)dyntab[i].d_un.d_ptr;
795                         break;
796                 }
797         }
798
799         if( !symtab || !strtab || !hashtab ) {
800                 SysDebug("ld-acess - Elf64Relocate: Missing Symbol, string or hash table");
801                 return NULL;
802         }
803
804         // Ready for symbol use 
805         AddLoaded( Filename, Base );
806
807         // Second pass on dynamic table
808         for(i = 0; dyntab[i].d_tag != DT_NULL; i ++)
809         {
810                 TRACE("dyntab[%i].d_tag = %i", i, dyntab[i].d_tag);
811                 switch(dyntab[i].d_tag)
812                 {
813                 case DT_SONAME: break;
814
815                 case DT_NEEDED: {
816                         char *libPath = strtab + dyntab[i].d_un.d_val;
817                         TRACE("Elf64Relocate: libPath = '%s'", libPath);
818                         if(LoadLibrary(libPath, NULL, envp) == 0) {
819                                 SysDebug("ld-acess - Elf64Relocate: Unable to load '%s'", libPath);
820                                 return NULL;
821                         }
822                         } break;
823                 
824                 // Relocation entries
825                 case DT_REL:
826                         dyntab[i].d_un.d_ptr += baseDiff;
827                         rel = (void *)(uintptr_t)dyntab[i].d_un.d_ptr;
828                         break;
829                 case DT_RELSZ:
830                         rel_count = dyntab[i].d_un.d_val / sizeof(Elf64_Rel);
831                         break;
832                 case DT_RELENT:
833                         if( dyntab[i].d_un.d_val != sizeof(Elf64_Rel) ) {
834                                 SysDebug("ld-acess - Elf64Relocate: DT_RELENT(%i) != sizeof(Elf64_Rel)(%i)",
835                                         dyntab[i].d_un.d_val, sizeof(Elf64_Rel));
836                                 return NULL;
837                         }
838                         break;
839                 case DT_RELA:
840                         dyntab[i].d_un.d_ptr += baseDiff;
841                         rela = (void *)(uintptr_t)dyntab[i].d_un.d_ptr;
842                         break;
843                 case DT_RELASZ:
844                         rela_count = dyntab[i].d_un.d_val / sizeof(Elf64_Rela);
845                         break;
846                 case DT_RELAENT:
847                         if( dyntab[i].d_un.d_val != sizeof(Elf64_Rela) ) {
848                                 SysDebug("ld-acess - Elf64Relocate: DT_RELAENT(%i) != sizeof(Elf64_Rela)(%i)",
849                                         dyntab[i].d_un.d_val, sizeof(Elf64_Rela));
850                                 return NULL;
851                         }
852                         break;
853                 case DT_JMPREL:
854                         dyntab[i].d_un.d_ptr += baseDiff;
855                         pltrel = (void *)(uintptr_t)dyntab[i].d_un.d_ptr;
856                         break;
857                 case DT_PLTREL:
858                         plt_type = dyntab[i].d_un.d_val;
859                         break;
860                 case DT_PLTRELSZ:
861                         plt_size = dyntab[i].d_un.d_val;
862                         break;
863                 }
864         }
865
866         // TODO: Relocate symbols
867         
868         // Relocation function
869         t_elf64_doreloc fpElf64DoReloc = &_Elf64DoReloc_X86_64;
870         #define _Elf64DoReloc(info, ptr, addend)        fpElf64DoReloc(Base, strtab, symtab, info, ptr, addend)
871
872         int fail = 0;
873         if( rel )
874         {
875                 TRACE("rel_count = %i", rel_count);
876                 for( i = 0; i < rel_count; i ++ )
877                 {
878                         uint64_t *ptr = (void *)(uintptr_t)( rel[i].r_offset + baseDiff );
879                         fail |= _Elf64DoReloc( rel[i].r_info, ptr, *ptr);
880                 }
881         }
882
883         if( rela )
884         {
885                 TRACE("rela_count = %i", rela_count);
886                 for( i = 0; i < rela_count; i ++ )
887                 {
888                         uint64_t *ptr = (void *)(uintptr_t)( rela[i].r_offset + baseDiff );
889                         fail |= _Elf64DoReloc( rela[i].r_info, ptr, rela[i].r_addend );
890                 }
891         }
892
893         if( pltrel && plt_type )
894         {
895                 if( plt_type == DT_REL ) {
896                         Elf64_Rel       *plt = pltrel;
897                          int    count = plt_size / sizeof(Elf64_Rel);
898                         TRACE("plt rel count = %i", count);
899                         for( i = 0; i < count; i ++ )
900                         {
901                                 uint64_t *ptr = (void *)(uintptr_t)( plt[i].r_offset + baseDiff );
902                                 fail |= _Elf64DoReloc( plt[i].r_info, ptr, *ptr);
903                         }
904                 }
905                 else {
906                         Elf64_Rela      *plt = pltrel;
907                          int    count = plt_size / sizeof(Elf64_Rela);
908                         TRACE("plt rela count = %i", count);
909                         for( i = 0; i < count; i ++ )
910                         {
911                                 uint64_t *ptr = (void *)(uintptr_t)( plt[i].r_offset + baseDiff );
912                                 fail |= _Elf64DoReloc( plt[i].r_info, ptr, plt[i].r_addend);
913                         }
914                 }
915         }
916
917         if( fail ) {
918                 TRACE("Failure");
919                 return NULL;
920         }
921
922         {
923         void *ret = (void *)(uintptr_t)(hdr->e_entry + baseDiff);
924         TRACE("Relocations done, return %p", ret);
925         return ret;
926         }
927 }
928
929 int Elf64GetSymbol(void *Base, const char *Name, void **Ret, size_t *Size)
930 {
931         Elf64_Ehdr      *hdr = Base;
932         Elf64_Sym       *symtab;
933          int    nbuckets = 0;
934 //       int    iSymCount = 0;
935          int    i;
936         Elf64_Word      *pBuckets;
937         Elf64_Word      *pChains;
938         uint32_t        iNameHash;
939         const char      *dynstrtab;
940         uintptr_t       iBaseDiff = -1;
941
942         dynstrtab = NULL;
943         pBuckets = NULL;
944         symtab = NULL;
945
946         // Catch the current executable
947         if( !pBuckets )
948         {
949                 Elf64_Phdr      *phtab;
950                 Elf64_Dyn       *dynTab = NULL;
951                  int    j;
952                 
953                 // Locate the tables
954                 phtab = (void*)( (intptr_t)Base + (uintptr_t)hdr->e_phoff );
955                 for( i = 0; i < hdr->e_phnum; i ++ )
956                 {
957                         if(phtab[i].p_type == PT_LOAD && iBaseDiff > phtab[i].p_vaddr)
958                                 iBaseDiff = phtab[i].p_vaddr;
959                         if( phtab[i].p_type == PT_DYNAMIC ) {
960                                 dynTab = (void*)(intptr_t)phtab[i].p_vaddr;
961                         }
962                 }
963                 if( !dynTab ) {
964                         SysDebug("ERROR - Unable to find DYNAMIC segment in %p", Base);
965                         return 0;
966                 }
967                 iBaseDiff = (intptr_t)Base - iBaseDiff; // Make iBaseDiff actually the diff
968                 dynTab = (void*)( (intptr_t)dynTab + iBaseDiff );
969                 
970                 for( j = 0; dynTab[j].d_tag != DT_NULL; j++)
971                 {
972                         switch(dynTab[j].d_tag)
973                         {
974                         // --- Symbol Table ---
975                         case DT_SYMTAB:
976                                 symtab = (void*)(intptr_t) dynTab[j].d_un.d_val;        // Rebased in Relocate
977                                 break;
978                         case DT_STRTAB:
979                                 dynstrtab = (void*)(intptr_t) dynTab[j].d_un.d_val;
980                                 break;
981                         // --- Hash Table --
982                         case DT_HASH:
983                                 pBuckets = (void*)(intptr_t) dynTab[j].d_un.d_val;
984                                 break;
985                         }
986                 }
987         }
988
989         nbuckets = pBuckets[0];
990 //      iSymCount = pBuckets[1];
991         pBuckets = &pBuckets[2];
992         pChains = &pBuckets[ nbuckets ];
993         
994         // Get hash
995         iNameHash = ElfHashString(Name);
996         iNameHash %= nbuckets;
997
998         // Walk Chain
999         i = pBuckets[ iNameHash ];
1000         if(symtab[i].st_shndx != SHN_UNDEF && strcmp(dynstrtab + symtab[i].st_name, Name) == 0) {
1001                 *Ret = (void*)( (intptr_t)symtab[i].st_value + iBaseDiff );
1002                 if(Size)        *Size = symtab[i].st_size;
1003                 TRACE("%s = %p", Name, *Ret);
1004                 return 1;
1005         }
1006         
1007         while(pChains[i] != STN_UNDEF)
1008         {
1009                 i = pChains[i];
1010                 if(symtab[i].st_shndx != SHN_UNDEF && strcmp(dynstrtab + symtab[i].st_name, Name) == 0) {
1011                         *Ret = (void*)((intptr_t)symtab[i].st_value + iBaseDiff);
1012                         if(Size)        *Size = symtab[i].st_size;
1013                         TRACE("%s = %p", Name, *Ret);
1014                         return 1;
1015                 }
1016         }
1017         
1018         return 0;
1019 }
1020 #endif
1021
1022 uint32_t ElfHashString(const char *name)
1023 {
1024         uint32_t        h = 0, g;
1025         while(*name)
1026         {
1027                 h = (h << 4) + *(uint8_t*)name++;
1028                 if( (g = h & 0xf0000000) )
1029                         h ^= g >> 24;
1030                 h &= ~g;
1031         }
1032         return h;
1033 }
1034

UCC git Repository :: git.ucc.asn.au