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

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