2 * Acess2 Dynamic Linker
3 * - By John Hodge (thePowersGang)
6 * - ELF32/ELF64 relocation
8 * TODO: Have GetSymbol() return a symbol "strength" on success. Allows STB_WEAK to be overriden by STB_GLOBAL
10 #ifndef KERNEL_VERSION
15 # define PAGE_SIZE 4096
28 # define DEBUG_OUT(...) SysDebug(__VA_ARGS__)
30 # define DEBUG_OUT(...) do{}while(0) //((void)(__VA_ARGS__))
33 #define WARNING(f,...) SysDebug("WARN: "f ,## __VA_ARGS__) // Malformed file
34 #define NOTICE(f,...) SysDebug("NOTICE: "f ,## __VA_ARGS__) // Missing relocation
35 //#define TRACE(f,...) DEBUG_OUT("TRACE:%s:%i "f, __func__, __LINE__ ,## __VA_ARGS__) // Debugging trace
36 #define TRACE(f,...) DEBUG_OUT("TRACE:%s "f, __func__,## __VA_ARGS__) // Debugging trace
39 # define SUPPORT_ELF64
44 //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"};
45 //static const char *csaR_NAMES[] = {"R_386_NONE", "R_386_32", "R_386_PC32", "R_386_GOT32", "R_386_PLT32", "R_386_COPY", "R_386_GLOB_DAT", "R_386_JMP_SLOT", "R_386_RELATIVE", "R_386_GOTOFF", "R_386_GOTPC", "R_386_LAST"};
52 void *ElfRelocate(void *Base, char **envp, const char *Filename);
53 int ElfGetSymbol(void *Base, const char *Name, void **Ret, size_t *Size);
54 void *Elf32_Relocate(void *Base, char **envp, const char *Filename);
55 int Elf32_GetSymbol(void *Base, const char *Name, void **Ret, size_t *Size);
57 void *Elf64_Relocate(void *Base, char **envp, const char *Filename);
58 int Elf64_GetSymbol(void *Base, const char *Name, void **Ret, size_t *Size);
60 uint32_t ElfHashString(const char *name);
64 * \fn int ElfRelocate(void *Base, char **envp, const char *Filename)
65 * \brief Relocates a loaded ELF Executable
67 void *ElfRelocate(void *Base, char **envp, const char *Filename)
69 Elf32_Ehdr *hdr = Base;
71 switch(hdr->e_ident[4])
74 return Elf32_Relocate(Base, envp, Filename);
77 return Elf64_Relocate(Base, envp, Filename);
80 SysDebug("ld-acess - ElfRelocate: Unknown file class %i", hdr->e_ident[4]);
86 * \fn int ElfGetSymbol(Uint Base, const char *name, void **ret)
88 int ElfGetSymbol(void *Base, const char *Name, void **ret, size_t *Size)
90 Elf32_Ehdr *hdr = Base;
92 switch(hdr->e_ident[4])
95 return Elf32_GetSymbol(Base, Name, ret, Size);
98 return Elf64_GetSymbol(Base, Name, ret, Size);
101 SysDebug("ld-acess - ElfRelocate: Unknown file class %i", hdr->e_ident[4]);
106 // --------------------------------------------------------------------
108 // --------------------------------------------------------------------
109 #define ELFTYPE Elf32
110 #include "elf_impl.c"
113 Elf32_RelocFcn elf_doRelocate_386;
114 Elf32_RelocFcn elf_doRelocate_arm;
116 int elf_doRelocate_386(const Elf32_RelocInfo *Info, Elf32_Word r_info, Elf32_Word* ptr, Elf32_Addr addend, bool bRela)
118 const Elf32_Sym *sym = &Info->symtab[ ELF32_R_SYM(r_info) ];
119 void *symval = (void*)(intptr_t)sym->st_value;
120 size_t size = sym->st_size;
121 TRACE("%i '%s'", ELF32_R_TYPE(r_info), Info->strtab + sym->st_name);
122 switch( ELF32_R_TYPE(r_info) )
124 // Standard 32 Bit Relocation (S+A)
126 TRACE("R_386_32 *0x%x = %p + 0x%x", ptr, symval, addend);
127 *ptr = (intptr_t)symval + addend;
130 // 32 Bit Relocation wrt. Offset (S+A-P)
132 TRACE("R_386_PC32 *0x%x = 0x%x + 0x%p - 0x%x", ptr, *ptr, symval, (intptr_t)ptr );
133 *ptr = (intptr_t)symval + addend - (intptr_t)ptr;
134 //*ptr = val + addend - ((Uint)ptr - iBaseDiff);
137 // Absolute Value of a symbol (S)
139 TRACE("R_386_GLOB_DAT *0x%x = %p", ptr, symval); if(0)
141 TRACE("R_386_JMP_SLOT *0x%x = %p", ptr, symval);
142 *ptr = (intptr_t)symval;
145 // Base Address (B+A)
147 TRACE("R_386_RELATIVE *0x%x = 0x%x + 0x%x", ptr, Info->iBaseDiff, addend);
148 *ptr = Info->iBaseDiff + addend;
152 void *old_symval = symval;
153 GetSymbol(Info->strtab + sym->st_name, &symval, &size, Info->Base);
154 if( symval == old_symval )
156 if( ELF32_ST_BIND(sym->st_info) != STB_WEAK )
158 WARNING("sym={val:%p,size:0x%x,info:0x%x,other:0x%x,shndx:%i}",
159 sym->st_value, sym->st_size, sym->st_info, sym->st_other, sym->st_shndx);
160 WARNING("Can't find required external symbol '%s' for R_386_COPY", Info->strtab + sym->st_name);
163 // Don't bother doing the memcpy
164 TRACE("R_386_COPY (%p, %p, %i)", ptr, symval, size);
168 TRACE("R_386_COPY (%p, %p, %i)", ptr, symval, size);
169 memcpy(ptr, symval, size);
174 WARNING("Unknown relocation %i", ELF32_ST_TYPE(r_info));
180 int elf_doRelocate_arm(const Elf32_RelocInfo *Info, Elf32_Word r_info, Elf32_Word* ptr, Elf32_Addr addend, bool bRela)
182 const Elf32_Sym *sym = &Info->symtab[ ELF32_R_SYM(r_info) ];
183 void *symval = (void*)(intptr_t)sym->st_value;
184 size_t size = sym->st_size;
185 TRACE("%i '%s'", ELF32_R_TYPE(r_info), Info->strtab + sym->st_name);
186 uintptr_t val = (uintptr_t)symval;
187 switch( ELF32_R_TYPE(r_info) )
191 TRACE("R_ARM_ABS32 %p (%p + %x)", ptr, symval, addend);
195 TRACE("R_ARM_GLOB_DAT %p (%p + %x)", ptr, symval, addend);
198 case R_ARM_JUMP_SLOT:
199 if(!bRela) addend = 0;
200 TRACE("R_ARM_JUMP_SLOT %p (%p + %x)", ptr, symval, addend);
205 TRACE("R_ARM_COPY (%p, %p, %i)", ptr, symval, size);
206 memcpy(ptr, symval, size);
208 // Delta between link and runtime locations + A
210 TRACE("R_ARM_RELATIVE %p (0x%x + 0x%x)", ptr, Info->iBaseDiff, addend);
211 if(ELF32_R_SYM(r_info) != 0) {
212 // TODO: Get delta for a symbol
213 WARNING("TODO - Implment R_ARM_RELATIVE for symbols");
217 *ptr = Info->iBaseDiff + addend;
221 WARNING("Unknown Relocation, %i", ELF32_R_TYPE(r_info));
227 Elf32_RelocFcn* Elf32_GetRelocFcn(unsigned Machine)
231 case EM_386: return elf_doRelocate_386;
232 case EM_ARM: return elf_doRelocate_arm;
233 default: return NULL;
238 // --------------------------------------------------------------------
240 // --------------------------------------------------------------------
243 #define ELFTYPE Elf64
244 #include "elf_impl.c"
247 Elf64_RelocFcn elf_doRelocate_x86_64;
249 int elf_doRelocate_x86_64(const Elf64_RelocInfo *Info, Elf64_Xword r_info, Elf64_Xword* ptr, Elf64_Addr addend, bool bRela)
251 const Elf64_Sym *sym = &Info->symtab[ ELF64_R_SYM(r_info) ];
252 void *symval = (void*)(intptr_t)sym->st_value;
253 size_t size = sym->st_size;
254 TRACE("%i '%s'", ELF64_R_TYPE(r_info), Info->strtab + sym->st_name);
255 switch( ELF64_R_TYPE(r_info) )
260 TRACE("R_X86_64_64 *0x%x = %p + 0x%x", ptr, symval, addend);
261 *ptr = (intptr_t)symval + addend;
263 // Absolute Value of a symbol (S)
264 case R_X86_64_GLOB_DAT:
265 TRACE("R_X86_64_GLOB_DAT *0x%x = %p", ptr, symval); if(0)
266 case R_X86_64_JUMP_SLOT:
267 TRACE("R_X86_64_JUMP_SLOT *0x%x = %p", ptr, symval);
268 *ptr = (intptr_t)symval;
271 // Base Address (B+A)
272 case R_X86_64_RELATIVE:
273 TRACE("R_X86_64_RELATIVE *0x%x = 0x%x + 0x%x", ptr, Info->iBaseDiff, addend);
274 *ptr = Info->iBaseDiff + addend;
277 case R_X86_64_COPY: {
278 void *old_symval = symval;
279 GetSymbol(Info->strtab + sym->st_name, &symval, &size, Info->Base);
280 if( symval == old_symval )
282 if( ELF64_ST_BIND(sym->st_info) != STB_WEAK )
284 WARNING("sym={val:%p,size:0x%x,info:0x%x,other:0x%x,shndx:%i}",
285 sym->st_value, sym->st_size, sym->st_info, sym->st_other, sym->st_shndx);
286 WARNING("Can't find required external symbol '%s' for R_X86_64_COPY", Info->strtab + sym->st_name);
289 // Don't bother doing the memcpy
290 TRACE("R_X86_64_COPY (%p, %p, %i)", ptr, symval, size);
294 TRACE("R_X86_64_COPY (%p, %p, %i)", ptr, symval, size);
295 memcpy(ptr, symval, size);
299 WARNING("Unknown Relocation, %i", ELF64_R_TYPE(r_info));
305 Elf64_RelocFcn* Elf64_GetRelocFcn(unsigned Machine)
309 case EM_X86_64: return elf_doRelocate_x86_64;
310 default: return NULL;
314 #endif // SUPPORT_ELF64
319 typedef int (*t_elf64_doreloc)(void *Base, const char *strtab, Elf64_Sym *symtab, Elf64_Xword r_info, void *ptr, Elf64_Sxword addend);
321 int _Elf64DoReloc_X86_64(void *Base, const char *strtab, Elf64_Sym *symtab, Elf64_Xword r_info, void *ptr, Elf64_Sxword addend)
323 int sym = ELF64_R_SYM(r_info);
324 int type = ELF64_R_TYPE(r_info);
325 const char *symname = strtab + symtab[sym].st_name;
327 //DEBUGS("_Elf64DoReloc: %s", symname);
333 if( !GetSymbol(symname, &symval, NULL, NULL) ) return 1;
334 *(uint64_t*)ptr = (uintptr_t)symval + addend;
336 case R_X86_64_COPY: {
338 if( !GetSymbol(symname, &symval, &size, NULL) ) return 1;
339 memcpy(ptr, symval, size);
341 case R_X86_64_GLOB_DAT:
342 if( !GetSymbol(symname, &symval, NULL, NULL) ) return 1;
343 *(uint64_t*)ptr = (uintptr_t)symval;
345 case R_X86_64_JUMP_SLOT:
346 if( !GetSymbol(symname, &symval, NULL, NULL) ) return 1;
347 *(uint64_t*)ptr = (uintptr_t)symval;
349 case R_X86_64_RELATIVE:
350 *(uint64_t*)ptr = (uintptr_t)Base + addend;
353 SysDebug("ld-acess - _Elf64DoReloc: Unknown relocation type %i", type);
356 //DEBUGS("_Elf64DoReloc: - Good");
360 void *Elf64Relocate(void *Base, char **envp, const char *Filename)
363 Elf64_Ehdr *hdr = Base;
365 Elf64_Dyn *dyntab = NULL;
366 Elf64_Addr compiledBase = -1, baseDiff;
367 Elf64_Sym *symtab = NULL;
369 Elf64_Word *hashtab = NULL;
370 Elf64_Rel *rel = NULL;
372 Elf64_Rela *rela = NULL;
375 int plt_size = 0, plt_type = 0;
378 TRACE(" e_ident = '%.16s'", hdr->e_ident);
379 TRACE(" e_type = 0x%x", hdr->e_type);
380 TRACE(" e_machine = 0x%x", hdr->e_machine);
381 TRACE(" e_version = 0x%x", hdr->e_version);
382 TRACE(" e_entry = %p", hdr->e_entry);
383 TRACE(" e_phoff = 0x%llx", hdr->e_phoff);
384 TRACE(" e_shoff = 0x%llx", hdr->e_shoff);
385 TRACE(" e_flags = 0x%x", hdr->e_flags);
386 TRACE(" e_ehsize = 0x%x", hdr->e_ehsize);
387 TRACE(" e_phentsize = 0x%x", hdr->e_phentsize);
388 TRACE(" e_phnum = %i", hdr->e_phnum);
390 // Scan for the dynamic table (and find the compiled base)
391 phtab = (void*)((uintptr_t)Base + (uintptr_t)hdr->e_phoff);
392 for( i = 0; i < hdr->e_phnum; i ++ )
394 if(phtab[i].p_type == PT_DYNAMIC)
395 dyntab = (void *)(intptr_t)phtab[i].p_vaddr;
396 if(phtab[i].p_type == PT_LOAD && compiledBase > phtab[i].p_vaddr)
397 compiledBase = phtab[i].p_vaddr;
400 baseDiff = (uintptr_t)Base - compiledBase;
402 TRACE("baseDiff = %p", baseDiff);
405 SysDebug(" Elf64Relocate: No PT_DYNAMIC segment in image %p, returning", Base);
406 return (void *)(uintptr_t)(hdr->e_entry + baseDiff);
409 dyntab = (void *)(uintptr_t)((uintptr_t)dyntab + baseDiff);
411 // Parse the dynamic table (first pass)
412 // - Search for String, Symbol and Hash tables
413 for(i = 0; dyntab[i].d_tag != DT_NULL; i ++)
415 switch(dyntab[i].d_tag)
418 dyntab[i].d_un.d_ptr += baseDiff;
419 symtab = (void *)(uintptr_t)dyntab[i].d_un.d_ptr;
422 dyntab[i].d_un.d_ptr += baseDiff;
423 strtab = (void *)(uintptr_t)dyntab[i].d_un.d_ptr;
426 dyntab[i].d_un.d_ptr += baseDiff;
427 hashtab = (void *)(uintptr_t)dyntab[i].d_un.d_ptr;
432 if( !symtab || !strtab || !hashtab ) {
433 SysDebug("ld-acess - Elf64Relocate: Missing Symbol, string or hash table");
437 // Ready for symbol use
438 AddLoaded( Filename, Base );
440 // Second pass on dynamic table
441 for(i = 0; dyntab[i].d_tag != DT_NULL; i ++)
443 TRACE("dyntab[%i].d_tag = %i", i, dyntab[i].d_tag);
444 switch(dyntab[i].d_tag)
446 case DT_SONAME: break;
449 char *libPath = strtab + dyntab[i].d_un.d_val;
450 TRACE("Elf64Relocate: libPath = '%s'", libPath);
451 if(LoadLibrary(libPath, NULL, envp) == 0) {
452 SysDebug("ld-acess - Elf64Relocate: Unable to load '%s'", libPath);
457 // Relocation entries
459 dyntab[i].d_un.d_ptr += baseDiff;
460 rel = (void *)(uintptr_t)dyntab[i].d_un.d_ptr;
463 rel_count = dyntab[i].d_un.d_val / sizeof(Elf64_Rel);
466 if( dyntab[i].d_un.d_val != sizeof(Elf64_Rel) ) {
467 SysDebug("ld-acess - Elf64Relocate: DT_RELENT(%i) != sizeof(Elf64_Rel)(%i)",
468 dyntab[i].d_un.d_val, sizeof(Elf64_Rel));
473 dyntab[i].d_un.d_ptr += baseDiff;
474 rela = (void *)(uintptr_t)dyntab[i].d_un.d_ptr;
477 rela_count = dyntab[i].d_un.d_val / sizeof(Elf64_Rela);
480 if( dyntab[i].d_un.d_val != sizeof(Elf64_Rela) ) {
481 SysDebug("ld-acess - Elf64Relocate: DT_RELAENT(%i) != sizeof(Elf64_Rela)(%i)",
482 dyntab[i].d_un.d_val, sizeof(Elf64_Rela));
487 dyntab[i].d_un.d_ptr += baseDiff;
488 pltrel = (void *)(uintptr_t)dyntab[i].d_un.d_ptr;
491 plt_type = dyntab[i].d_un.d_val;
494 plt_size = dyntab[i].d_un.d_val;
499 // TODO: Relocate symbols
501 // Relocation function
502 t_elf64_doreloc fpElf64DoReloc = &_Elf64DoReloc_X86_64;
503 #define _Elf64DoReloc(info, ptr, addend) fpElf64DoReloc(Base, strtab, symtab, info, ptr, addend)
508 TRACE("rel_count = %i", rel_count);
509 for( i = 0; i < rel_count; i ++ )
511 uint64_t *ptr = (void *)(uintptr_t)( rel[i].r_offset + baseDiff );
512 fail |= _Elf64DoReloc( rel[i].r_info, ptr, *ptr);
518 TRACE("rela_count = %i", rela_count);
519 for( i = 0; i < rela_count; i ++ )
521 uint64_t *ptr = (void *)(uintptr_t)( rela[i].r_offset + baseDiff );
522 fail |= _Elf64DoReloc( rela[i].r_info, ptr, rela[i].r_addend );
526 if( pltrel && plt_type )
528 if( plt_type == DT_REL ) {
529 Elf64_Rel *plt = pltrel;
530 int count = plt_size / sizeof(Elf64_Rel);
531 TRACE("plt rel count = %i", count);
532 for( i = 0; i < count; i ++ )
534 uint64_t *ptr = (void *)(uintptr_t)( plt[i].r_offset + baseDiff );
535 fail |= _Elf64DoReloc( plt[i].r_info, ptr, *ptr);
539 Elf64_Rela *plt = pltrel;
540 int count = plt_size / sizeof(Elf64_Rela);
541 TRACE("plt rela count = %i", count);
542 for( i = 0; i < count; i ++ )
544 uint64_t *ptr = (void *)(uintptr_t)( plt[i].r_offset + baseDiff );
545 fail |= _Elf64DoReloc( plt[i].r_info, ptr, plt[i].r_addend);
556 void *ret = (void *)(uintptr_t)(hdr->e_entry + baseDiff);
557 TRACE("Relocations done, return %p", ret);
562 int Elf64GetSymbol(void *Base, const char *Name, void **Ret, size_t *Size)
564 Elf64_Ehdr *hdr = Base;
567 // int iSymCount = 0;
569 Elf64_Word *pBuckets;
572 const char *dynstrtab;
573 uintptr_t iBaseDiff = -1;
579 // Catch the current executable
583 Elf64_Dyn *dynTab = NULL;
587 phtab = (void*)( (intptr_t)Base + (uintptr_t)hdr->e_phoff );
588 for( i = 0; i < hdr->e_phnum; i ++ )
590 if(phtab[i].p_type == PT_LOAD && iBaseDiff > phtab[i].p_vaddr)
591 iBaseDiff = phtab[i].p_vaddr;
592 if( phtab[i].p_type == PT_DYNAMIC ) {
593 dynTab = (void*)(intptr_t)phtab[i].p_vaddr;
597 SysDebug("ERROR - Unable to find DYNAMIC segment in %p", Base);
600 iBaseDiff = (intptr_t)Base - iBaseDiff; // Make iBaseDiff actually the diff
601 dynTab = (void*)( (intptr_t)dynTab + iBaseDiff );
603 for( j = 0; dynTab[j].d_tag != DT_NULL; j++)
605 switch(dynTab[j].d_tag)
607 // --- Symbol Table ---
609 symtab = (void*)(intptr_t) dynTab[j].d_un.d_val; // Rebased in Relocate
612 dynstrtab = (void*)(intptr_t) dynTab[j].d_un.d_val;
616 pBuckets = (void*)(intptr_t) dynTab[j].d_un.d_val;
622 nbuckets = pBuckets[0];
623 // iSymCount = pBuckets[1];
624 pBuckets = &pBuckets[2];
625 pChains = &pBuckets[ nbuckets ];
628 iNameHash = ElfHashString(Name);
629 iNameHash %= nbuckets;
632 i = pBuckets[ iNameHash ];
633 if(symtab[i].st_shndx != SHN_UNDEF && strcmp(dynstrtab + symtab[i].st_name, Name) == 0) {
634 *Ret = (void*)( (intptr_t)symtab[i].st_value + iBaseDiff );
635 if(Size) *Size = symtab[i].st_size;
636 TRACE("%s = %p", Name, *Ret);
640 while(pChains[i] != STN_UNDEF)
643 if(symtab[i].st_shndx != SHN_UNDEF && strcmp(dynstrtab + symtab[i].st_name, Name) == 0) {
644 *Ret = (void*)((intptr_t)symtab[i].st_value + iBaseDiff);
645 if(Size) *Size = symtab[i].st_size;
646 TRACE("%s = %p", Name, *Ret);
656 uint32_t ElfHashString(const char *name)
661 h = (h << 4) + *(uint8_t*)name++;
662 if( (g = h & 0xf0000000) )