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

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