Usermode/ld-acess - Disabled destructive relocation
[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 void    *Elf64Relocate(void *Base, char **envp, const char *Filename);
47  int    Elf64GetSymbol(void *Base, const char *Name, void **Ret, size_t *Size);
48 #endif
49 uint32_t        ElfHashString(const char *name);
50
51 // === CODE ===
52 /**
53  * \fn int ElfRelocate(void *Base, char **envp, const char *Filename)
54  * \brief Relocates a loaded ELF Executable
55  */
56 void *ElfRelocate(void *Base, char **envp, const char *Filename)
57 {
58         Elf32_Ehdr      *hdr = Base;
59         
60         switch(hdr->e_ident[4])
61         {
62         case ELFCLASS32:
63                 return Elf32Relocate(Base, envp, Filename);
64 #ifdef SUPPORT_ELF64
65         case ELFCLASS64:
66                 return Elf64Relocate(Base, envp, Filename);
67 #endif
68         default:
69                 SysDebug("ld-acess - ElfRelocate: Unknown file class %i", hdr->e_ident[4]);
70                 return NULL;
71         }
72 }
73
74 /**
75  * \fn int ElfGetSymbol(Uint Base, const char *name, void **ret)
76  */
77 int ElfGetSymbol(void *Base, const char *Name, void **ret, size_t *Size)
78 {
79         Elf32_Ehdr      *hdr = Base;
80
81         switch(hdr->e_ident[4])
82         {
83         case ELFCLASS32:
84                 return Elf32GetSymbol(Base, Name, ret, Size);
85 #ifdef SUPPORT_ELF64
86         case ELFCLASS64:
87                 return Elf64GetSymbol(Base, Name, ret, Size);
88 #endif
89         default:
90                 SysDebug("ld-acess - ElfRelocate: Unknown file class %i", hdr->e_ident[4]);
91                 return 0;
92         }
93 }
94
95 int elf_doRelocate_386(uint32_t r_info, uint32_t *ptr, Elf32_Addr addend, int type,
96                 int bRela, const char *Sym, intptr_t iBaseDiff)
97 {
98         void    *symval;
99         switch( type )
100         {
101         // Standard 32 Bit Relocation (S+A)
102         case R_386_32:
103                 if( !GetSymbol(Sym, &symval, NULL) )
104                         return 1;
105                 DEBUGS(" elf_doRelocate: R_386_32 *0x%x += %p('%s')",
106                                 ptr, symval, Sym);
107                 *ptr = (intptr_t)symval + addend;
108                 break;
109                 
110         // 32 Bit Relocation wrt. Offset (S+A-P)
111         case R_386_PC32:
112                 DEBUGS(" elf_doRelocate: '%s'", Sym);
113                 if( !GetSymbol(Sym, &symval, NULL) )    return 1;
114                 DEBUGS(" elf_doRelocate: R_386_PC32 *0x%x = 0x%x + 0x%p - 0x%x",
115                         ptr, *ptr, symval, (intptr_t)ptr );
116                 *ptr = (intptr_t)symval + addend - (intptr_t)ptr;
117                 //*ptr = val + addend - ((Uint)ptr - iBaseDiff);
118                 break;
119
120         // Absolute Value of a symbol (S)
121         case R_386_GLOB_DAT:
122         case R_386_JMP_SLOT:
123                 DEBUGS(" elf_doRelocate: '%s'", Sym);
124                 if( !GetSymbol(Sym, &symval, NULL) )    return 1;
125                 DEBUGS(" elf_doRelocate: %s *0x%x = %p", csaR_NAMES[type], ptr, symval);
126                 *ptr = (intptr_t)symval;
127                 break;
128
129         // Base Address (B+A)
130         case R_386_RELATIVE:
131                 DEBUGS(" elf_doRelocate: R_386_RELATIVE *0x%x = 0x%x + 0x%x", ptr, iBaseDiff, addend);
132                 *ptr = iBaseDiff + addend;
133                 break;
134
135         case R_386_COPY: {
136                 size_t  size;
137                 if( !GetSymbol(Sym, &symval, &size) )   return 1;
138                 DEBUGS(" elf_doRelocate_386: R_386_COPY (%p, %p, %i)", ptr, symval, size);
139                 memcpy(ptr, symval, size);
140                 break; }
141
142         default:
143                 SysDebug("elf_doRelocate_386: Unknown relocation %i", type);
144                 return 2;
145         }
146         return 0;
147 }
148
149 int elf_doRelocate_arm(uint32_t r_info, uint32_t *ptr, Elf32_Addr addend, int type, int bRela, const char *Sym, intptr_t iBaseDiff)
150 {
151         uint32_t        val;
152         switch(type)
153         {
154         // (S + A) | T
155         case R_ARM_ABS32:
156                 DEBUGS(" elf_doRelocate_arm: R_ARM_ABS32 %p (%s + %x)", ptr, Sym, addend);
157                 if( !GetSymbol(Sym, (void**)&val, NULL) )       return 1;
158                 *ptr = val + addend;
159                 break;
160         case R_ARM_GLOB_DAT:
161                 DEBUGS(" elf_doRelocate_arm: R_ARM_GLOB_DAT %p (%s + %x)", ptr, Sym, addend);
162                 if( !GetSymbol(Sym, (void**)&val, NULL) )       return 1;
163                 *ptr = val + addend;
164                 break;
165         case R_ARM_JUMP_SLOT:
166                 if(!bRela)      addend = 0;
167                 DEBUGS(" elf_doRelocate_arm: R_ARM_JUMP_SLOT %p (%s + %x)", ptr, Sym, addend);
168                 if( !GetSymbol(Sym, (void**)&val, NULL) )       return 1;
169                 *ptr = val + addend;
170                 break;
171         // Copy
172         case R_ARM_COPY: {
173                 size_t  size;
174                 void    *src;
175                 if( !GetSymbol(Sym, &src, &size) )      return 1;
176                 DEBUGS(" elf_doRelocate_arm: R_ARM_COPY (%p, %p, %i)", ptr, src, size);
177                 memcpy(ptr, src, size);
178                 break; }
179         // Delta between link and runtime locations + A
180         case R_ARM_RELATIVE:
181                 DEBUGS(" elf_doRelocate_arm: R_ARM_RELATIVE %p (0x%x + 0x%x)", ptr, iBaseDiff, addend);
182                 if(Sym[0] != '\0') {
183                         // TODO: Get delta for a symbol
184                         SysDebug("elf_doRelocate_arm: TODO - Implment R_ARM_RELATIVE for symbols");
185                         return 2;
186                 }
187                 else {
188                         *ptr = iBaseDiff + addend;
189                 }
190                 break;
191         default:
192                 SysDebug("elf_doRelocate_arm: Unknown Relocation, %i", type);
193                 return 2;
194         }
195         return 0;
196 }
197
198 int elf_doRelocate_unk(uint32_t r_info, uint32_t *ptr, Elf32_Addr addend, int type, int bRela, const char *Sym, intptr_t iBaseDiff)
199 {
200         return 1;
201 }
202
203 void *Elf32Relocate(void *Base, char **envp, const char *Filename)
204 {
205         Elf32_Ehdr      *hdr = Base;
206         Elf32_Phdr      *phtab;
207          int    i, j;   // Counters
208         char    *libPath;
209         intptr_t        iRealBase = -1;
210         intptr_t        iBaseDiff;
211          int    iSegmentCount;
212 //       int    iSymCount;
213         Elf32_Rel       *rel = NULL;
214         Elf32_Rela      *rela = NULL;
215         void    *plt = NULL;
216          int    relSz=0, relEntSz=8;
217          int    relaSz=0, relaEntSz=8;
218          int    pltSz=0, pltType=0;
219         Elf32_Dyn       *dynamicTab = NULL;     // Dynamic Table Pointer
220         char    *dynstrtab = NULL;      // .dynamic String Table
221         Elf32_Sym       *dynsymtab;
222         int     (*do_relocate)(uint32_t t_info, uint32_t *ptr, Elf32_Addr addend, int Type, int bRela, const char *Sym, intptr_t iBaseDiff);
223         
224         DEBUGS("ElfRelocate: (Base=0x%x)", Base);
225         
226         // Check magic header
227         
228         
229         // Parse Program Header to get Dynamic Table
230         phtab = (void*)( (uintptr_t)Base + hdr->phoff );
231         iSegmentCount = hdr->phentcount;
232         for(i=0;i<iSegmentCount;i++)
233         {
234                 // Determine linked base address
235                 if(phtab[i].Type == PT_LOAD && iRealBase > phtab[i].VAddr)
236                         iRealBase = phtab[i].VAddr;
237                 
238                 // Find Dynamic Section
239                 if(phtab[i].Type == PT_DYNAMIC) {
240                         if(dynamicTab) {
241                                 DEBUGS(" WARNING - elf_relocate: Multiple PT_DYNAMIC segments");
242                                 continue;
243                         }
244                         dynamicTab = (void *) (intptr_t) phtab[i].VAddr;
245                         j = i;  // Save Dynamic Table ID
246                 }
247         }
248         
249         // Page Align real base
250         iRealBase &= ~0xFFF;
251         DEBUGS(" elf_relocate: True Base = 0x%x, Compiled Base = 0x%x", Base, iRealBase);
252         
253         // Adjust "Real" Base
254         iBaseDiff = (intptr_t)Base - iRealBase;
255
256 //      hdr->entrypoint += iBaseDiff;   // Adjust Entrypoint
257         
258         // Check if a PT_DYNAMIC segement was found
259         if(!dynamicTab) {
260                 SysDebug(" elf_relocate: No PT_DYNAMIC segment in image %p, returning", Base);
261                 return (void *)(intptr_t)(hdr->entrypoint + iBaseDiff);
262         }
263
264         // Allow writing to read-only segments, just in case they need to be relocated
265         // - Will be reversed at the end of the function
266         for( i = 0; i < iSegmentCount; i ++ )
267         {
268                 if(phtab[i].Type == PT_LOAD && !(phtab[i].Flags & PF_W) ) {
269                         uintptr_t       addr = phtab[i].VAddr + iBaseDiff;
270                         uintptr_t       end = addr + phtab[i].MemSize;
271                         for( ; addr < end; addr += PAGE_SIZE )
272                                 _SysSetMemFlags(addr, 0, 1);    // Unset RO
273                 }
274         }
275
276         // Adjust Dynamic Table
277         dynamicTab = (void *)( (intptr_t)dynamicTab + iBaseDiff );
278         
279         // === Get Symbol table and String Table ===
280         dynsymtab = NULL;
281         for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++)
282         {
283                 switch(dynamicTab[j].d_tag)
284                 {
285                 // --- Symbol Table ---
286                 case DT_SYMTAB:
287                         DEBUGS(" elf_relocate: DYNAMIC Symbol Table 0x%x (0x%x)",
288                                 dynamicTab[j].d_val, dynamicTab[j].d_val + iBaseDiff);
289                         dynsymtab = (void*)((intptr_t)dynamicTab[j].d_val + iBaseDiff);
290                         //if(iBaseDiff != 0)    dynamicTab[j].d_val += iBaseDiff;
291                         break;
292                 // --- String Table ---
293                 case DT_STRTAB:
294                         DEBUGS(" elf_relocate: DYNAMIC String Table 0x%x (0x%x)",
295                                 dynamicTab[j].d_val, dynamicTab[j].d_val + iBaseDiff);
296                         dynstrtab = (void*)((intptr_t)dynamicTab[j].d_val + iBaseDiff);
297                         //if(iBaseDiff != 0)    dynamicTab[j].d_val += iBaseDiff;
298                         break;
299                 // --- Hash Table --
300                 case DT_HASH:
301                         //if(iBaseDiff != 0)    dynamicTab[j].d_val += iBaseDiff;
302 //                      iSymCount = ((Elf32_Word*)(intptr_t)dynamicTab[j].d_val)[1];
303                         break;
304                 }
305         }
306
307         if(dynsymtab == NULL) {
308                 SysDebug("ld-acess.so - WARNING: No Dynamic Symbol table in %p, returning", hdr);
309                 return (void *)(intptr_t) (hdr->entrypoint + iBaseDiff);
310         }
311
312         // === Add to loaded list (can be imported now) ===
313         AddLoaded( Filename, Base );
314
315         // === Parse Relocation Data ===
316         DEBUGS(" elf_relocate: dynamicTab = 0x%x", dynamicTab);
317         for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++)
318         {
319                 switch(dynamicTab[j].d_tag)
320                 {
321                 // --- Shared Library Name ---
322                 case DT_SONAME:
323                         DEBUGS(" elf_relocate: .so Name '%s'", dynstrtab+dynamicTab[j].d_val);
324                         break;
325                 // --- Needed Library ---
326                 case DT_NEEDED:
327                         libPath = dynstrtab + dynamicTab[j].d_val;
328                         DEBUGS(" dynstrtab = %p, d_val = 0x%x", dynstrtab, dynamicTab[j].d_val);
329                         DEBUGS(" Required Library '%s'", libPath);
330                         if(LoadLibrary(libPath, NULL, envp) == 0) {
331                                 #if DEBUG
332                                 DEBUGS(" elf_relocate: Unable to load '%s'", libPath);
333                                 #else
334                                 SysDebug("Unable to load required library '%s'", libPath);
335                                 #endif
336                                 return 0;
337                         }
338                         DEBUGS(" Lib loaded");
339                         break;
340                 // --- PLT/GOT ---
341 //              case DT_PLTGOT: pltgot = (void*)(iBaseDiff + dynamicTab[j].d_val);      break;
342                 case DT_JMPREL: plt = (void*)(iBaseDiff + dynamicTab[j].d_val); break;
343                 case DT_PLTREL: pltType = dynamicTab[j].d_val;  break;
344                 case DT_PLTRELSZ:       pltSz = dynamicTab[j].d_val;    break;
345                 
346                 // --- Relocation ---
347                 case DT_REL:    rel = (void*)(iBaseDiff + dynamicTab[j].d_val); break;
348                 case DT_RELSZ:  relSz = dynamicTab[j].d_val;    break;
349                 case DT_RELENT: relEntSz = dynamicTab[j].d_val; break;
350                 case DT_RELA:   rela = (void*)(iBaseDiff + dynamicTab[j].d_val);        break;
351                 case DT_RELASZ: relaSz = dynamicTab[j].d_val;   break;
352                 case DT_RELAENT:        relaEntSz = dynamicTab[j].d_val;        break;
353                 
354                 // --- Symbol Table ---
355                 case DT_SYMTAB:
356                 // --- Hash Table ---
357                 case DT_HASH:
358                 // --- String Table ---
359                 case DT_STRTAB:
360                         break;
361                 
362                 // --- Unknown ---
363                 default:
364                         if(dynamicTab[j].d_tag > DT_JMPREL)     continue;
365                         //DEBUGS(" elf_relocate: %i-%i = %s,0x%x",
366                         //      i,j, csaDT_NAMES[dynamicTab[j].d_tag],dynamicTab[j].d_val);
367                         break;
368                 }
369         }
370         
371         DEBUGS(" elf_relocate: Beginning Relocation");
372
373          int    fail = 0;
374
375         switch(hdr->machine)
376         {
377         case EM_386:
378                 do_relocate = elf_doRelocate_386;
379                 break;
380         case EM_ARM:
381                 do_relocate = elf_doRelocate_arm;
382                 break;
383         default:
384                 SysDebug("Elf32Relocate: Unknown machine type %i", hdr->machine);
385                 do_relocate = elf_doRelocate_unk;
386                 fail = 1;
387                 break;
388         }
389         
390         DEBUGS("do_relocate = %p (%p or %p)", do_relocate, &elf_doRelocate_386, &elf_doRelocate_arm);
391
392         #define _doRelocate(r_info, ptr, bRela, addend) \
393                 do_relocate(r_info, ptr, addend, ELF32_R_TYPE(r_info), bRela, \
394                         dynstrtab + dynsymtab[ELF32_R_SYM(r_info)].nameOfs, iBaseDiff);
395
396         // Parse Relocation Entries
397         if(rel && relSz)
398         {
399                 Elf32_Word      *ptr;
400                 DEBUGS(" elf_relocate: rel=0x%x, relSz=0x%x, relEntSz=0x%x", rel, relSz, relEntSz);
401                 j = relSz / relEntSz;
402                 for( i = 0; i < j; i++ )
403                 {
404                         //DEBUGS("  Rel %i: 0x%x+0x%x", i, iBaseDiff, rel[i].r_offset);
405                         ptr = (void*)(iBaseDiff + rel[i].r_offset);
406                         fail |= _doRelocate(rel[i].r_info, ptr, 0, *ptr);
407                 }
408         }
409         // Parse Relocation Entries
410         if(rela && relaSz)
411         {
412                 Elf32_Word      *ptr;
413                 DEBUGS(" elf_relocate: rela=0x%x, relaSz=0x%x, relaEntSz=0x%x", rela, relaSz, relaEntSz);
414                 j = relaSz / relaEntSz;
415                 for( i = 0; i < j; i++ )
416                 {
417                         ptr = (void*)(iBaseDiff + rela[i].r_offset);
418                         fail |= _doRelocate(rel[i].r_info, ptr, 1, rela[i].r_addend);
419                 }
420         }
421         
422         // === Process PLT (Procedure Linkage Table) ===
423         if(plt && pltSz)
424         {
425                 Elf32_Word      *ptr;
426                 DEBUGS(" elf_relocate: Relocate PLT, plt=0x%x", plt);
427                 if(pltType == DT_REL)
428                 {
429                         Elf32_Rel       *pltRel = plt;
430                         j = pltSz / sizeof(Elf32_Rel);
431                         DEBUGS(" elf_relocate: PLT Reloc Type = Rel, %i entries", j);
432                         for(i=0;i<j;i++)
433                         {
434                                 ptr = (void*)(iBaseDiff + pltRel[i].r_offset);
435                                 fail |= _doRelocate(pltRel[i].r_info, ptr, 0, *ptr);
436                         }
437                 }
438                 else
439                 {
440                         Elf32_Rela      *pltRela = plt;
441                         j = pltSz / sizeof(Elf32_Rela);
442                         DEBUGS(" elf_relocate: PLT Reloc Type = Rela, %i entries", j);
443                         for(i=0;i<j;i++)
444                         {
445                                 ptr = (void*)(iRealBase + pltRela[i].r_offset);
446                                 fail |= _doRelocate(pltRela[i].r_info, ptr, 1, pltRela[i].r_addend);
447                         }
448                 }
449         }
450
451         // Re-set readonly
452         for( i = 0; i < iSegmentCount; i ++ )
453         {
454                 // If load and not writable
455                 if(phtab[i].Type == PT_LOAD && !(phtab[i].Flags & PF_W) ) {
456                         uintptr_t       addr = phtab[i].VAddr + iBaseDiff;
457                         uintptr_t       end = addr + phtab[i].MemSize;
458                         for( ; addr < end; addr += PAGE_SIZE )
459                                 _SysSetMemFlags(addr, 1, 1);    // Unset RO
460                 }
461         }
462
463         if( fail ) {
464                 DEBUGS("ElfRelocate: Failure");
465                 return NULL;
466         }       
467
468         #undef _doRelocate
469
470         DEBUGS("ElfRelocate: RETURN 0x%x to %p", hdr->entrypoint + iBaseDiff, __builtin_return_address(0));
471         return (void*)(intptr_t)( hdr->entrypoint + iBaseDiff );
472 }
473
474 int Elf32GetSymbol(void *Base, const char *Name, void **ret, size_t *Size)
475 {
476         Elf32_Ehdr      *hdr = Base;
477         Elf32_Sym       *symtab = NULL;
478          int    nbuckets = 0;
479         Elf32_Word      *pBuckets = NULL;
480         Elf32_Word      *pChains;
481         uint32_t        iNameHash;
482         const char      *dynstrtab = NULL;
483         uintptr_t       iBaseDiff = -1;
484         Elf32_Phdr      *phtab;
485         Elf32_Dyn       *dynTab = NULL;
486          int    i;
487
488         // Locate the tables
489         phtab = (void*)( (uintptr_t)Base + hdr->phoff );
490         for( i = 0; i < hdr->phentcount; i ++ )
491         {
492                 if(phtab[i].Type == PT_LOAD && iBaseDiff > phtab[i].VAddr)
493                         iBaseDiff = phtab[i].VAddr;
494                 if( phtab[i].Type == PT_DYNAMIC ) {
495                         dynTab = (void*)(intptr_t)phtab[i].VAddr;
496                 }
497         }
498         if( !dynTab ) {
499                 SysDebug("ERROR - Unable to find DYNAMIC segment in %p", Base);
500                 return 0;
501         }
502         iBaseDiff = (intptr_t)Base - iBaseDiff; // Make iBaseDiff actually the diff
503         dynTab = (void*)( (intptr_t)dynTab + iBaseDiff );
504         for( i = 0; dynTab[i].d_tag != DT_NULL; i++)
505         {
506                 switch(dynTab[i].d_tag)
507                 {
508                 // --- Symbol Table ---
509                 case DT_SYMTAB:
510                         symtab = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);        // Rebased in Relocate
511                         break;
512                 case DT_STRTAB:
513                         dynstrtab = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);
514                         break;
515                 // --- Hash Table --
516                 case DT_HASH:
517                         pBuckets = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);
518                         break;
519                 }
520         }
521         
522         if( !symtab ) {
523                 SysDebug("ERRO - No DT_SYMTAB in %p", Base);
524                 return 0;
525         }
526         if( !pBuckets ) {
527                 SysDebug("ERRO - No DT_HASH in %p", Base);
528                 return 0;
529         }
530         if( !dynstrtab ) {
531                 SysDebug("ERRO - No DT_STRTAB in %p", Base);
532                 return 0;
533         }
534
535         // ... ok... maybe they haven't been relocated
536         if( (uintptr_t)symtab < (uintptr_t)Base )
537         {
538                 symtab    = (void*)( (uintptr_t)symtab    + iBaseDiff );
539                 pBuckets  = (void*)( (uintptr_t)pBuckets  + iBaseDiff );
540                 dynstrtab = (void*)( (uintptr_t)dynstrtab + iBaseDiff );
541                 SysDebug("Executable not yet relocated");
542         }
543
544         nbuckets = pBuckets[0];
545 //      iSymCount = pBuckets[1];
546         pBuckets = &pBuckets[2];
547         pChains = &pBuckets[ nbuckets ];
548
549         // Get hash
550         iNameHash = ElfHashString(Name);
551         iNameHash %= nbuckets;
552
553         // Walk Chain
554         i = pBuckets[ iNameHash ];
555         if(symtab[i].shndx != SHN_UNDEF && strcmp(dynstrtab + symtab[i].nameOfs, Name) == 0) {
556                 *ret = (void*)( (uintptr_t) symtab[ i ].value + iBaseDiff );
557                 if(Size)        *Size = symtab[i].size;
558                 return 1;
559         }
560         
561         while(pChains[i] != STN_UNDEF)
562         {
563                 i = pChains[i];
564                 if(symtab[i].shndx != SHN_UNDEF && strcmp(dynstrtab + symtab[ i ].nameOfs, Name) == 0) {
565                         *ret = (void*)( (uintptr_t)symtab[ i ].value + iBaseDiff );
566                         if(Size)        *Size = symtab[i].size;
567                         return 1;
568                 }
569         }
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