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

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