Usermode/ld-acess - Overhaul cleanup of ELF loader
[tpg/acess2.git] / Usermode / Libraries / ld-acess.so_src / elf_impl.c
1
2 #ifndef ELFTYPE
3 # error "ELFTYPE must be defined to either ELf32 or Elf64 to use this file"
4 #endif
5
6 #define __PREF(t,s)     t##_##s
7 #define _PREF(t,s)      __PREF(t,s)
8 #define PREF(sym)       _PREF(ELFTYPE,sym)
9
10 typedef struct
11 {
12         void    *Base;
13         intptr_t        iBaseDiff;
14         const char      *strtab;
15         const PREF(Sym) *symtab;
16 } PREF(RelocInfo);
17
18 typedef int PREF(RelocFcn)(const PREF(RelocInfo)* Info, PREF(Xword) t_info, PREF(Xword)* ptr, PREF(Addr) addend, bool bRela);
19
20 // - Extern
21 static PREF(RelocFcn)*  PREF(GetRelocFcn)(unsigned Machine);
22 // - Local
23 static PREF(RelocFcn) PREF(doRelocate_unk);
24 static void PREF(int_GetBaseDyntab)(void* Base, const PREF(Phdr)* phtab, unsigned phentcount, intptr_t* iBaseDiff_p, PREF(Dyn)** dynamicTab_p);
25 static int PREF(GetSymbolVars)(void *Base, const PREF(Sym)** symtab, const PREF(Word)** pBuckets, const char **dynstrtab, uintptr_t* piBaseDiff);
26 static int PREF(GetSymbolInfo)(void *Base, const char *Name, void **Addr, size_t *Size, int* Section, int *Binding, int *Type);
27
28 // - Relocate
29 void *PREF(Relocate)(void *Base, char **envp, const char *Filename)
30 {
31         TRACE("(Base=0x%x)", Base);
32         const PREF(Ehdr)        *hdr = Base;
33         
34         // Check magic header
35         // TODO: Validate header?
36         
37         
38         // Parse Program Header to get Dynamic Table
39         // - Determine the linked base of the executable
40         const PREF(Phdr)        *phtab = (void*)( (uintptr_t)Base + hdr->e_phoff );
41         
42         intptr_t        iBaseDiff = 0;
43         PREF(Dyn)*      dynamicTab = NULL;
44         
45         PREF(int_GetBaseDyntab)(Base, phtab, hdr->e_phnum, &iBaseDiff, &dynamicTab);
46         
47         // Check if a PT_DYNAMIC segement was found
48         if(!dynamicTab)
49         {
50                 SysDebug(" elf_relocate: No PT_DYNAMIC segment in image %p, returning", Base);
51                 return (void *)(intptr_t)(hdr->e_entry + iBaseDiff);
52         }
53
54         // Allow writing to read-only segments, just in case they need to be relocated
55         // - Will be reversed at the end of the function
56         for( unsigned i = 0; i < hdr->e_phnum; i ++ )
57         {
58                 if(phtab[i].p_type == PT_LOAD && !(phtab[i].p_flags & PF_W) )
59                 {
60                         uintptr_t       addr = phtab[i].p_vaddr + iBaseDiff;
61                         uintptr_t       end = addr + phtab[i].p_memsz;
62                         for( ; addr < end; addr += PAGE_SIZE )
63                                 _SysSetMemFlags(addr, 0, 1);    // Unset RO
64                 }
65         }
66         
67         // === Get Symbol table and String Table ===
68         char    *dynstrtab = NULL;      // .dynamic String Table
69         PREF(Sym)       *dynsymtab = NULL;
70         PREF(Word)      *hashtable = NULL;
71         unsigned        iSymCount = 0;
72         for( unsigned j = 0; dynamicTab[j].d_tag != DT_NULL; j++)
73         {
74                 const PREF(Dyn) *dt = &dynamicTab[j];
75                 switch(dt->d_tag)
76                 {
77                 // --- Symbol Table ---
78                 case DT_SYMTAB:
79                         TRACE("DYNAMIC Symbol Table 0x%x (0x%x)", dt->d_val, dt->d_val + iBaseDiff);
80                         dynsymtab = (void*)((intptr_t)dt->d_val + iBaseDiff);
81                         break;
82                 // --- String Table ---
83                 case DT_STRTAB:
84                         TRACE("DYNAMIC String Table 0x%x (0x%x)", dt->d_val, dt->d_val + iBaseDiff);
85                         dynstrtab = (void*)((intptr_t)dt->d_val + iBaseDiff);
86                         break;
87                 // --- Hash Table --
88                 case DT_HASH:
89                         TRACE("DYNAMIC Hash table %p (%p)", dt->d_val, dt->d_val + iBaseDiff);
90                         hashtable = (void*)((intptr_t)dt->d_val + iBaseDiff);
91                         iSymCount = hashtable[1];
92                         break;
93                 }
94         }
95
96         if(dynsymtab == NULL) {
97                 SysDebug("ld-acess.so - WARNING: No Dynamic Symbol table in %p, returning", hdr);
98                 return (void *)(intptr_t) (hdr->e_entry + iBaseDiff);
99         }
100         
101         // Apply base offset to locally defined symbols
102         // - #0 is defined as ("" SHN_UNDEF), so skip it
103         for( unsigned i = 1; i < iSymCount; i ++ )
104         {
105                 PREF(Sym)       *sym = &dynsymtab[i];
106                 const char *name = dynstrtab + sym->st_name;
107                 (void)name;
108                 if( sym->st_shndx == SHN_UNDEF )
109                 {
110                         TRACE("Sym %i'%s' deferred (SHN_UNDEF)", i, name);
111                 }
112                 else if( sym->st_shndx == SHN_ABS )
113                 {
114                         // Leave as is
115                         TRACE("Sym %i'%s' untouched", i, name);
116                 }
117                 else
118                 {
119                         void *newval;
120                         size_t  newsize;
121                         if( ELF32_ST_BIND(sym->st_info) != STB_WEAK )
122                         {
123                                 TRACE("Sym %i'%s' = %p (local)", i, name, sym->st_value + iBaseDiff);
124                                 sym->st_value += iBaseDiff;
125                         }
126                         // If GetSymbol doesn't return a strong/global symbol value
127                         else if( GetSymbol(name, &newval, &newsize, Base) != 1 )
128                         {
129                                 TRACE("Sym %i'%s' = %p (Local weak)", i, name, sym->st_value + iBaseDiff);
130                                 sym->st_value += iBaseDiff;
131                         }
132                         else
133                         {
134                                 TRACE("Sym %i'%s' = %p+0x%x (Extern weak)", i, name, newval, newsize);
135                                 sym->st_value = (uintptr_t)newval;
136                                 sym->st_size = newsize;
137                         }
138                 }
139         }
140
141         // === Add to loaded list (can be imported now) ===
142         AddLoaded( Filename, Base );
143
144         // === Parse Relocation Data ===
145         PREF(Rel)       *rel = NULL;
146         PREF(Rela)      *rela = NULL;
147         void    *plt = NULL;
148          int    relSz=0, relEntSz=8;
149          int    relaSz=0, relaEntSz=8;
150          int    pltSz=0, pltType=0;
151         TRACE("dynamicTab = 0x%x", dynamicTab);
152         for( int j = 0; dynamicTab[j].d_tag != DT_NULL; j++)
153         {
154                 const PREF(Dyn) *dt = &dynamicTab[j];
155                 switch(dt->d_tag)
156                 {
157                 // --- Shared Library Name ---
158                 case DT_SONAME:
159                         TRACE(".so Name '%s'", dynstrtab + dt->d_val);
160                         break;
161                 // --- Needed Library ---
162                 case DT_NEEDED: {
163                         //assert(dt->d_val < sizeof_dynstrtab); // Disabled, no sizeof_dynstrtab
164                         const char      *libPath = dynstrtab + dt->d_val;
165                         TRACE(" Required Library '%s'", libPath);
166                         if(LoadLibrary(libPath, NULL, envp) == 0) {
167                                 SysDebug("Unable to load required library '%s'", libPath);
168                                 return 0;
169                         }
170                         TRACE(" Lib loaded");
171                         break; }
172                 // --- PLT/GOT ---
173 //              case DT_PLTGOT: pltgot = (void*)(iBaseDiff + dt->d_val);        break;
174                 case DT_JMPREL: plt = (void*)(iBaseDiff + dt->d_val);   break;
175                 case DT_PLTREL: pltType = dt->d_val;    break;
176                 case DT_PLTRELSZ:       pltSz = dt->d_val;      break;
177                 
178                 // --- Relocation ---
179                 case DT_REL:    rel = (void*)(iBaseDiff + dt->d_val);   break;
180                 case DT_RELSZ:  relSz = dt->d_val;      break;
181                 case DT_RELENT: relEntSz = dt->d_val;   break;
182                 case DT_RELA:   rela = (void*)(iBaseDiff + dt->d_val);  break;
183                 case DT_RELASZ: relaSz = dt->d_val;     break;
184                 case DT_RELAENT:        relaEntSz = dt->d_val;  break;
185                 
186                 // --- Symbol Table ---
187                 case DT_SYMTAB:
188                 // --- Hash Table ---
189                 case DT_HASH:
190                 // --- String Table ---
191                 case DT_STRTAB:
192                         break;
193                 
194                 // --- Unknown ---
195                 default:
196                         if(dt->d_tag > DT_JMPREL)       continue;
197                         //DEBUGS(" elf_relocate: %i-%i = %s,0x%x",
198                         //      i,j, csaDT_NAMES[dynamicTab[j].d_tag],dynamicTab[j].d_val);
199                         break;
200                 }
201         }
202         
203         // Resolve symbols (second pass)
204         // - #0 is defined as ("" SHN_UNDEF), so skip it
205          int    fail = 0;
206         for( int i = 1; i < iSymCount; i ++ )
207         {
208                 PREF(Sym)       *sym = &dynsymtab[i];
209                 const char *name = dynstrtab + sym->st_name;
210                 if( sym->st_shndx == SHN_UNDEF )
211                 {
212                         void *newval;
213                         size_t  newsize;
214                         if( !GetSymbol(name, &newval, &newsize, Base) ) {
215                                 if( ELF32_ST_BIND(sym->st_info) != STB_WEAK ) {
216                                         // Not a weak binding, set fail and move on
217                                         WARNING("%s: Can't find required symbol '%s' for '%s'", __func__, name, Filename);
218                                         fail = 1;
219                                         continue ;
220                                 }
221                                 // Leave the symbol value as-is
222                         }
223                         else {
224                                 TRACE("Sym %i'%s' bound to %p+0x%x", i, name, newval, newsize);
225                                 sym->st_value = (intptr_t)newval;
226                                 sym->st_size = newsize;
227                         }
228                 }
229                 else if( sym->st_shndx == SHN_ABS )
230                 {
231                         // Leave as is
232                 }
233                 else
234                 {
235                         // Handled previously
236                         // TODO: What about weak locally-defined symbols?
237                         //assert( ELF32_ST_BIND(sym->st_info) != STB_WEAK );
238                 }
239         }
240         if( fail ) {
241                 WARNING("Relocation of '%s' failed", Filename);
242                 return NULL;
243         }
244         
245         TRACE("Beginning Relocation on '%s'", Filename);
246
247
248         PREF(RelocFcn)* do_relocate = PREF(GetRelocFcn)(hdr->e_machine);
249         if( do_relocate == 0 )
250         {
251                 SysDebug("%s: Unknown machine type %i", __func__, hdr->e_machine);
252                 do_relocate = PREF(doRelocate_unk);
253                 fail = 1;
254         }
255         
256         TRACE("do_relocate = %p", do_relocate);
257
258         #define _doRelocate(r_info, ptr, bRela, addend) \
259                 do_relocate(&reloc_info, r_info, ptr, addend, bRela);
260
261         PREF(RelocInfo) reloc_info = {
262                 .Base = Base,
263                 .iBaseDiff = iBaseDiff,
264                 .strtab = dynstrtab,
265                 .symtab = dynsymtab
266         };
267
268         // Parse Relocation Entries
269         if(rel && relSz)
270         {
271                 TRACE("rel=0x%x, relSz=0x%x, relEntSz=0x%x", rel, relSz, relEntSz);
272                 int max = relSz / relEntSz;
273                 for( int i = 0; i < max; i++ )
274                 {
275                         PREF(Xword) *ptr = (void*)(iBaseDiff + rel[i].r_offset);
276                         fail |= _doRelocate(rel[i].r_info, ptr, 0, *ptr);
277                 }
278         }
279         // Parse Relocation Entries
280         if(rela && relaSz)
281         {
282                 TRACE("rela=0x%x, relaSz=0x%x, relaEntSz=0x%x", rela, relaSz, relaEntSz);
283                 int count = relaSz / relaEntSz;
284                 for( int i = 0; i < count; i++ )
285                 {
286                         void *ptr = (void*)(iBaseDiff + rela[i].r_offset);
287                         fail |= _doRelocate(rela[i].r_info, ptr, 1, rela[i].r_addend);
288                 }
289         }
290         
291         // === Process PLT (Procedure Linkage Table) ===
292         if(plt && pltSz)
293         {
294                 TRACE("Relocate PLT, plt=0x%x", plt);
295                 if(pltType == DT_REL)
296                 {
297                         PREF(Rel)       *pltRel = plt;
298                         int count = pltSz / sizeof(*pltRel);
299                         TRACE("PLT Reloc Type = Rel, %i entries", count);
300                         for(int i = 0; i < count; i ++)
301                         {
302                                 PREF(Xword) *ptr = (void*)(iBaseDiff + pltRel[i].r_offset);
303                                 fail |= _doRelocate(pltRel[i].r_info, ptr, 0, *ptr);
304                         }
305                 }
306                 else
307                 {
308                         PREF(Rela)      *pltRela = plt;
309                         int count = pltSz / sizeof(*pltRela);
310                         TRACE("PLT Reloc Type = Rela, %i entries", count);
311                         for(int i=0;i<count;i++)
312                         {
313                                 void *ptr = (void*)(iBaseDiff + pltRela[i].r_offset);
314                                 fail |= _doRelocate(pltRela[i].r_info, ptr, 1, pltRela[i].r_addend);
315                         }
316                 }
317         }
318
319         // Re-set readonly
320         for( int i = 0; i < hdr->e_phnum; i ++ )
321         {
322                 // If load and not writable
323                 if(phtab[i].p_type == PT_LOAD && !(phtab[i].p_flags & PF_W) )
324                 {
325                         uintptr_t       addr = phtab[i].p_vaddr + iBaseDiff;
326                         uintptr_t       end = addr + phtab[i].p_memsz;
327                         for( ; addr < end; addr += PAGE_SIZE )
328                                 _SysSetMemFlags(addr, 1, 1);    // Unset RO
329                 }
330         }
331
332         if( fail ) {
333                 TRACE("ElfRelocate: Failure");
334                 return NULL;
335         }       
336
337         #undef _doRelocate
338
339         TRACE("RETURN 0x%x to %p", hdr->e_entry + iBaseDiff, __builtin_return_address(0));
340         return (void*)(intptr_t)( hdr->e_entry + iBaseDiff );
341 }
342
343 void PREF(int_GetBaseDyntab)(void* Base, const PREF(Phdr)* phtab, unsigned phentcount, intptr_t* iBaseDiff_p, PREF(Dyn)** dynamicTab_p)
344 {
345         uintptr_t       iRealBase = UINTPTR_MAX;
346         assert(dynamicTab_p);
347         
348         for(unsigned i = 0; i < phentcount; i ++)
349         {
350                 switch(phtab[i].p_type)
351                 {
352                 case PT_LOAD:
353                         // Determine linked base address
354                         if( iRealBase > phtab[i].p_vaddr)
355                                 iRealBase = phtab[i].p_vaddr;
356                         break;
357                 case PT_DYNAMIC:
358                         // Find Dynamic Section
359                         if(!*dynamicTab_p) {
360                                 *dynamicTab_p = (void *) (intptr_t) phtab[i].p_vaddr;
361                         }
362                         else {
363                                 WARNING("elf_relocate: Multiple PT_DYNAMIC segments");
364                         }
365                         break;
366                 }
367         }
368         
369         // Page Align real base
370         iRealBase &= ~0xFFF;
371         
372         // Adjust "Real" Base
373         const intptr_t  iBaseDiff = (intptr_t)Base - iRealBase;
374         *iBaseDiff_p = iBaseDiff;
375
376         // Adjust Dynamic Table
377         if( *dynamicTab_p )
378         {
379                 *dynamicTab_p = (void *)( (intptr_t)*dynamicTab_p + iBaseDiff );
380         }
381
382         TRACE("True Base = 0x%x, Compiled Base = 0x%x, Difference = 0x%x", Base, iRealBase, *iBaseDiff_p);
383 }
384
385 int PREF(doRelocate_unk)(const PREF(RelocInfo)* Info, PREF(Xword) r_info, PREF(Xword)* ptr, PREF(Addr) addend, bool bRela)
386 {
387         return 1;
388 }
389
390 int PREF(GetSymbolVars)(void *Base, const PREF(Sym)** symtab, const PREF(Word)** pBuckets, const char **dynstrtab, uintptr_t* piBaseDiff)
391 {
392         const PREF(Ehdr)*       hdr = Base;
393         PREF(Dyn)*      dynTab = NULL;
394         intptr_t        iBaseDiff = -1;
395         
396         PREF(int_GetBaseDyntab)(Base, (void*)( (uintptr_t)Base + hdr->e_phoff ), hdr->e_phnum, &iBaseDiff, &dynTab);
397         if( !dynTab ) {
398                 SysDebug("ERROR - Unable to find DYNAMIC segment in %p", Base);
399                 return 1;
400         }
401         
402         for( int i = 0; dynTab[i].d_tag != DT_NULL; i++)
403         {
404                 switch(dynTab[i].d_tag)
405                 {
406                 // --- Symbol Table ---
407                 case DT_SYMTAB:
408                         *symtab = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);       // Rebased in Relocate
409                         break;
410                 case DT_STRTAB:
411                         *dynstrtab = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);
412                         break;
413                 // --- Hash Table --
414                 case DT_HASH:
415                         *pBuckets = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);
416                         break;
417                 }
418         }
419         
420         if( !*symtab ) {
421                 SysDebug("ERRO - No DT_SYMTAB in %p", Base);
422                 return 1;
423         }
424         if( !*pBuckets ) {
425                 SysDebug("ERRO - No DT_HASH in %p", Base);
426                 return 1;
427         }
428         if( !*dynstrtab ) {
429                 SysDebug("ERRO - No DT_STRTAB in %p", Base);
430                 return 1;
431         }
432
433         // ... ok... maybe they haven't been relocated
434         if( (uintptr_t)*symtab < (uintptr_t)Base )
435         {
436                 SysDebug("Executable not yet relocated (symtab,pBuckets,dynstrtab = %p,%p,%p + 0x%x)",
437                         *symtab,*pBuckets,*dynstrtab, iBaseDiff);
438                 *symtab    = (void*)( (uintptr_t)*symtab    + iBaseDiff );
439                 *pBuckets  = (void*)( (uintptr_t)*pBuckets  + iBaseDiff );
440                 *dynstrtab = (void*)( (uintptr_t)*dynstrtab + iBaseDiff );
441         }
442         *piBaseDiff = iBaseDiff;
443         return 0;
444 }
445
446 int PREF(GetSymbolInfo)(void *Base, const char *Name, void **Addr, size_t *Size, int* Section, int *Binding, int *Type)
447 {
448         // Locate the tables
449         uintptr_t       iBaseDiff = -1;
450         const PREF(Sym)*        symtab = NULL;
451         const PREF(Word)*       pBuckets = NULL;
452         const char      *dynstrtab = NULL;
453         if( PREF(GetSymbolVars)(Base, &symtab, &pBuckets, &dynstrtab, &iBaseDiff) )
454                 return 1;
455
456         unsigned nbuckets = pBuckets[0];
457 //      int iSymCount = pBuckets[1];
458         pBuckets = &pBuckets[2];
459         
460         const PREF(Word)*       pChains = &pBuckets[ nbuckets ];
461         assert(pChains);
462
463         // Get hash
464         unsigned iNameHash = ElfHashString(Name);
465         iNameHash %= nbuckets;
466
467         // Walk Chain
468         unsigned idx = pBuckets[ iNameHash ];
469         do {
470                 const PREF(Sym)*        sym = &symtab[idx];
471                 assert(sym);
472                 if( strcmp(dynstrtab + sym->st_name, Name) == 0 )
473                 {
474                         TRACE("*sym = {value:0x%x,size:0x%x,info:0x%x,other:0x%x,shndx:%i}",
475                                 sym->st_value, sym->st_size, sym->st_info,
476                                 sym->st_other, sym->st_shndx);
477                         if(Addr)        *Addr = (void*)(intptr_t)( sym->st_value );
478                         if(Size)        *Size = sym->st_size;
479                         if(Binding)     *Binding = ELF32_ST_BIND(sym->st_info);
480                         if(Type)        *Type = ELF32_ST_TYPE(sym->st_info);
481                         if(Section)     *Section = sym->st_shndx;
482                         return 0;
483                 }
484         } while( (idx = pChains[idx]) != STN_UNDEF && idx != pBuckets[iNameHash] );
485         
486         TRACE("No symbol");
487         return 1;
488 }
489
490 int PREF(GetSymbol)(void *Base, const char *Name, void **ret, size_t *Size)
491 {
492          int    section, binding;
493         TRACE("%s(%p,%s,...)", __func__, Base, Name);
494         if( PREF(GetSymbolInfo)(Base, Name, ret, Size, &section, &binding, NULL) )
495                 return 0;
496         if( section == SHN_UNDEF ) {
497                 TRACE("%s: Undefined %p", __func__, *ret, (Size?*Size:0), section);
498                 return 0;
499         }
500         if( binding == STB_WEAK ) {
501                 TRACE("%s: Weak, return %p+0x%x,section=%i", __func__, *ret, (Size?*Size:0), section);
502                 return 2;
503         }
504         TRACE("%s: Found %p+0x%x,section=%i", __func__, *ret, (Size?*Size:0), section);
505         return 1;
506 }

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