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

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