Renamed ld-acess.so_src directory to ld-acess_src
[tpg/acess2.git] / AcessNative / ld-acess_src / elf.c
diff --git a/AcessNative/ld-acess_src/elf.c b/AcessNative/ld-acess_src/elf.c
new file mode 100644 (file)
index 0000000..3ab5a50
--- /dev/null
@@ -0,0 +1,529 @@
+/*\r
+ * Acess v0.1\r
+ * ELF Executable Loader Code\r
+ */\r
+#define DEBUG  0\r
+#include <stdlib.h>\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <unistd.h>\r
+#include "common.h"\r
+#include "elf.h"\r
+\r
+#define DEBUG_WARN     1\r
+\r
+#define MKPTR(_type,_val)      ((_type*)(uintptr_t)(_val))\r
+#define PTRMK(_type,_val)      MKPTR(_type,_val)\r
+#define PTR(_val)      ((void*)(uintptr_t)(_val))\r
+\r
+#if DEBUG\r
+# define ENTER(...)\r
+# define LOG(s, ...)   printf("%s: " s, __func__, __VA_ARGS__)\r
+# define LOGS(s)       printf("%s: " s, __func__)\r
+# define LEAVE(...)\r
+#else\r
+# define ENTER(...)\r
+# define LOG(...)\r
+# define LOGS(...)\r
+# define LEAVE(...)\r
+#endif\r
+\r
+// === PROTOTYPES ===\r
+void   *Elf_Load(FILE *FP);\r
+uintptr_t      Elf_Relocate(void *Base);\r
+ int   Elf_GetSymbol(void *Base, char *Name, uintptr_t *ret);\r
+ int   Elf_Int_DoRelocate(uint32_t r_info, uint32_t *ptr, uint32_t addend, Elf32_Sym *symtab, void *Base);\r
+uint32_t       Elf_Int_HashString(char *str);\r
+\r
+// === CODE ===\r
+void *Elf_Load(FILE *FP)\r
+{\r
+       Elf32_Ehdr      hdr;\r
+       Elf32_Phdr      *phtab;\r
+        int    i, j;\r
+        int    iPageCount;\r
+       uint32_t        max, base = -1;\r
+       uint32_t        addr;\r
+       uint32_t        baseDiff = 0;\r
+       \r
+       ENTER("pFP", FP);\r
+       \r
+       // Read ELF Header\r
+       fread(&hdr, sizeof(hdr), 1, FP);\r
+       \r
+       // Check the file type\r
+       if(hdr.ident[0] != 0x7F || hdr.ident[1] != 'E' || hdr.ident[2] != 'L' || hdr.ident[3] != 'F') {\r
+               Warning("Non-ELF File was passed to the ELF loader\n");\r
+               LEAVE('n');\r
+               return NULL;\r
+       }\r
+       \r
+       // Check for a program header\r
+       if(hdr.phoff == 0) {\r
+               #if DEBUG_WARN\r
+               Warning("ELF File does not contain a program header\n");\r
+               #endif\r
+               LEAVE('n');\r
+               return NULL;\r
+       }\r
+       \r
+       // Read Program Header Table\r
+       phtab = malloc( sizeof(Elf32_Phdr) * hdr.phentcount );\r
+       if( !phtab ) {\r
+               LEAVE('n');\r
+               return NULL;\r
+       }\r
+       LOG("hdr.phoff = 0x%08x\n", hdr.phoff);\r
+       fseek(FP, hdr.phoff, SEEK_SET);\r
+       fread(phtab, sizeof(Elf32_Phdr), hdr.phentcount, FP);\r
+       \r
+       // Count Pages\r
+       iPageCount = 0;\r
+       LOG("hdr.phentcount = %i\n", hdr.phentcount);\r
+       for( i = 0; i < hdr.phentcount; i++ )\r
+       {\r
+               // Ignore Non-LOAD types\r
+               if(phtab[i].Type != PT_LOAD)\r
+                       continue;\r
+               iPageCount += ((phtab[i].VAddr&0xFFF) + phtab[i].MemSize + 0xFFF) >> 12;\r
+               LOG("phtab[%i] = {VAddr:0x%x, MemSize:0x%x}\n", i, phtab[i].VAddr, phtab[i].MemSize);\r
+       }\r
+       \r
+       LOG("iPageCount = %i\n", iPageCount);\r
+       \r
+       // Allocate Information Structure\r
+       //ret = malloc( sizeof(tBinary) + sizeof(tBinaryPage)*iPageCount );\r
+       // Fill Info Struct\r
+       //ret->Entry = hdr.entrypoint;\r
+       //ret->Base = -1;               // Set Base to maximum value\r
+       //ret->NumPages = iPageCount;\r
+       //ret->Interpreter = NULL;\r
+\r
+       // Prescan for base and size\r
+       for( i = 0; i < hdr.phentcount; i ++)\r
+       {\r
+               if( phtab[i].Type != PT_LOAD )\r
+                       continue;\r
+               if( phtab[i].VAddr < base )\r
+                       base = phtab[i].VAddr;\r
+               if( phtab[i].VAddr > max )\r
+                       max = phtab[i].VAddr;\r
+       }\r
+\r
+       LOG("base = %08x, max = %08x\n", base, max);\r
+\r
+       if( base == 0 ) {\r
+               // Find a nice space (31 address bits allowed)\r
+               base = FindFreeRange( max, 31 );\r
+               LOG("new base = %08x\n", base);\r
+               if( base == 0 ) return NULL;\r
+               baseDiff = base;\r
+       }\r
+       \r
+       // Load Pages\r
+       j = 0;\r
+       for( i = 0; i < hdr.phentcount; i++ )\r
+       {\r
+               //LOG("phtab[%i].Type = 0x%x", i, phtab[i].Type);\r
+               LOG("phtab[%i] = {\n", i);\r
+               LOG(" .Type = 0x%08x\n", phtab[i].Type);\r
+               LOG(" .Offset = 0x%08x\n", phtab[i].Offset);\r
+               LOG(" .VAddr = 0x%08x\n", phtab[i].VAddr);\r
+               LOG(" .PAddr = 0x%08x\n", phtab[i].PAddr);\r
+               LOG(" .FileSize = 0x%08x\n", phtab[i].FileSize);\r
+               LOG(" .MemSize = 0x%08x\n", phtab[i].MemSize);\r
+               LOG(" .Flags = 0x%08x\n", phtab[i].Flags);\r
+               LOG(" .Align = 0x%08x\n", phtab[i].Align);\r
+               LOGS(" }\n");\r
+               // Get Interpreter Name\r
+               if( phtab[i].Type == PT_INTERP )\r
+               {\r
+                       char *tmp;\r
+                       //if(ret->Interpreter)  continue;\r
+                       tmp = malloc(phtab[i].FileSize);\r
+                       fseek(FP, phtab[i].Offset, SEEK_SET);\r
+                       fread(tmp, phtab[i].FileSize, 1, FP);\r
+                       //ret->Interpreter = Binary_RegInterp(tmp);\r
+                       LOG("Interpreter '%s'\n", tmp);\r
+                       free(tmp);\r
+                       continue;\r
+               }\r
+               // Ignore non-LOAD types\r
+               if(phtab[i].Type != PT_LOAD)    continue;\r
+               \r
+               LOG("phtab[%i] = {VAddr:0x%x,Offset:0x%x,FileSize:0x%x}\n",\r
+                       i, phtab[i].VAddr+baseDiff, phtab[i].Offset, phtab[i].FileSize);\r
+               \r
+               addr = phtab[i].VAddr + baseDiff;\r
+\r
+               if( AllocateMemory( addr, phtab[i].MemSize ) ) {\r
+                       return NULL;\r
+               }\r
+               \r
+               fseek(FP, phtab[i].Offset, SEEK_SET);\r
+               fread( PTRMK(void, addr), phtab[i].FileSize, 1, FP );\r
+               memset( PTRMK(char, addr) + phtab[i].FileSize, 0, phtab[i].MemSize - phtab[i].FileSize );\r
+       }\r
+       \r
+       // Clean Up\r
+       free(phtab);\r
+       // Return\r
+       LEAVE('p', base);\r
+       return PTRMK(void, base);\r
+}\r
+\r
+// --- ELF RELOCATION ---\r
+/**\r
+ * \brief Relocates a loaded ELF Executable\r
+ */\r
+uintptr_t Elf_Relocate(void *Base)\r
+{\r
+       Elf32_Ehdr      *hdr = Base;\r
+       Elf32_Phdr      *phtab;\r
+        int    i, j;   // Counters\r
+       char    *libPath;\r
+       uint32_t        iRealBase = -1;\r
+       uintptr_t       iBaseDiff;\r
+        int    iSegmentCount;\r
+        int    iSymCount = 0;\r
+       Elf32_Rel       *rel = NULL;\r
+       Elf32_Rela      *rela = NULL;\r
+       uint32_t        *pltgot = NULL;\r
+       void    *plt = NULL;\r
+       uint32_t        *ptr;\r
+        int    relSz=0, relEntSz=8;\r
+        int    relaSz=0, relaEntSz=8;\r
+        int    pltSz=0, pltType=0;\r
+       Elf32_Dyn       *dynamicTab = NULL;     // Dynamic Table Pointer\r
+       char    *dynstrtab = NULL;      // .dynamic String Table\r
+       Elf32_Sym       *dynsymtab = NULL;\r
+        int    bFailed = 0;\r
+       \r
+       ENTER("pBase", Base);\r
+       LOG("Base = %p\n", Base);\r
+       \r
+       // Parse Program Header to get Dynamic Table\r
+       phtab = Base + hdr->phoff;\r
+       iSegmentCount = hdr->phentcount;\r
+       for(i = 0; i < iSegmentCount; i ++ )\r
+       {\r
+               // Determine linked base address\r
+               if(phtab[i].Type == PT_LOAD && iRealBase > phtab[i].VAddr)\r
+                       iRealBase = phtab[i].VAddr;\r
+               \r
+               // Find Dynamic Section\r
+               if(phtab[i].Type == PT_DYNAMIC) {\r
+                       if(dynamicTab) {\r
+                               Warning("Elf_Relocate - Multiple PT_DYNAMIC segments\n");\r
+                               continue;\r
+                       }\r
+                       dynamicTab = MKPTR(void, phtab[i].VAddr);\r
+                       j = i;  // Save Dynamic Table ID\r
+                       break;\r
+               }\r
+       }\r
+       \r
+       // Check if a PT_DYNAMIC segement was found\r
+       if(!dynamicTab) {\r
+               Warning("Elf_Relocate: No PT_DYNAMIC segment in image, returning\n");\r
+               LEAVE('x', hdr->entrypoint);\r
+               return hdr->entrypoint;\r
+       }\r
+       \r
+       // Page Align real base\r
+       iRealBase &= ~0xFFF;\r
+       \r
+       LOG("dynamicTab = %p\n", dynamicTab);\r
+       // Adjust "Real" Base\r
+       iBaseDiff = (uintptr_t)Base - iRealBase;\r
+       LOG("iBaseDiff = %p\n", (void*)iBaseDiff);\r
+       // Adjust Dynamic Table\r
+       dynamicTab = PTR( (uintptr_t)dynamicTab + iBaseDiff);\r
+       LOG("dynamicTab = %p\n", dynamicTab);\r
+\r
+       hdr->entrypoint += iBaseDiff;\r
+       \r
+       // === Get Symbol table and String Table ===\r
+       for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++)\r
+       {\r
+               switch(dynamicTab[j].d_tag)\r
+               {\r
+               // --- Symbol Table ---\r
+               case DT_SYMTAB:\r
+                       dynamicTab[j].d_val += iBaseDiff;\r
+                       dynsymtab = PTRMK(void, dynamicTab[j].d_val);\r
+                       hdr->misc.SymTable = dynamicTab[j].d_val;       // Saved in unused bytes of ident\r
+                       break;\r
+               \r
+               // --- String Table ---\r
+               case DT_STRTAB:\r
+                       dynamicTab[j].d_val += iBaseDiff;\r
+                       dynstrtab = PTRMK(void, dynamicTab[j].d_val);\r
+                       break;\r
+               \r
+               // --- Hash Table --\r
+               case DT_HASH:\r
+                       dynamicTab[j].d_val += iBaseDiff;\r
+                       iSymCount = (PTRMK(uint32_t, dynamicTab[j].d_val))[1];\r
+                       hdr->misc.HashTable = dynamicTab[j].d_val;      // Saved in unused bytes of ident\r
+                       break;\r
+               }\r
+       }\r
+\r
+\r
+       // Alter Symbols to true base\r
+       for(i = 0; i < iSymCount; i ++)\r
+       {\r
+               dynsymtab[i].nameOfs += (uintptr_t)dynstrtab;\r
+               if( dynsymtab[i].shndx == SHN_UNDEF )\r
+               {\r
+                       LOG("Sym '%s' = UNDEF\n", MKPTR(char,dynsymtab[i].name));\r
+               }\r
+               else\r
+               {\r
+                       dynsymtab[i].value += iBaseDiff;\r
+                       LOG("Sym '%s' = 0x%x (relocated)\n", MKPTR(char,dynsymtab[i].name), dynsymtab[i].value);\r
+               }\r
+       }\r
+       \r
+       // === Add to loaded list (can be imported now) ===\r
+       Binary_SetReadyToUse( Base );\r
+\r
+       // === Parse Relocation Data ===\r
+       for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++)\r
+       {\r
+               switch(dynamicTab[j].d_tag)\r
+               {\r
+               // --- Shared Library Name ---\r
+               case DT_SONAME:\r
+                       LOG(".so Name '%s'\n", dynstrtab + dynamicTab[j].d_val);\r
+                       break;\r
+               // --- Needed Library ---\r
+               case DT_NEEDED:\r
+                       libPath = dynstrtab + dynamicTab[j].d_val;\r
+                       Binary_LoadLibrary(libPath);\r
+                       break;\r
+               // --- PLT/GOT ---\r
+               case DT_PLTGOT: pltgot = (void*)(iBaseDiff+dynamicTab[j].d_val);        break;\r
+               case DT_JMPREL: plt = (void*)(iBaseDiff+dynamicTab[j].d_val);   break;\r
+               case DT_PLTREL: pltType = dynamicTab[j].d_val;  break;\r
+               case DT_PLTRELSZ:       pltSz = dynamicTab[j].d_val;    break;\r
+               \r
+               // --- Relocation ---\r
+               case DT_REL:    rel = (void*)(iBaseDiff + dynamicTab[j].d_val); break;\r
+               case DT_RELSZ:  relSz = dynamicTab[j].d_val;    break;\r
+               case DT_RELENT: relEntSz = dynamicTab[j].d_val; break;\r
+               \r
+               case DT_RELA:   rela = (void*)(iBaseDiff + dynamicTab[j].d_val);        break;\r
+               case DT_RELASZ: relaSz = dynamicTab[j].d_val;   break;\r
+               case DT_RELAENT:        relaEntSz = dynamicTab[j].d_val;        break;\r
+               }\r
+       }\r
+       \r
+       // Parse Relocation Entries\r
+       if(rel && relSz)\r
+       {\r
+               j = relSz / relEntSz;\r
+               for( i = 0; i < j; i++ )\r
+               {\r
+                       ptr = (void*)(iBaseDiff + rel[i].r_offset);\r
+                       if( !Elf_Int_DoRelocate(rel[i].r_info, ptr, *ptr, dynsymtab, Base) ) {\r
+                               bFailed = 1;\r
+                       }\r
+               }\r
+       }\r
+       // Parse Relocation Entries\r
+       if(rela && relaSz)\r
+       {\r
+               j = relaSz / relaEntSz;\r
+               for( i = 0; i < j; i++ )\r
+               {\r
+                       ptr = (void*)(iBaseDiff + rela[i].r_offset);\r
+                       if( !Elf_Int_DoRelocate(rel[i].r_info, ptr, rela[i].r_addend, dynsymtab, Base) ) {\r
+                               bFailed = 1;\r
+                       }\r
+               }\r
+       }\r
+       \r
+       // === Process PLT (Procedure Linkage Table) ===\r
+       if(plt && pltSz)\r
+       {\r
+               if(pltType == DT_REL)\r
+               {\r
+                       Elf32_Rel       *pltRel = plt;\r
+                       j = pltSz / sizeof(Elf32_Rel);\r
+                       LOG("PLT Rel - plt = %p, pltSz = %i (%i ents)\n", plt, pltSz, j);\r
+                       for(i = 0; i < j; i++)\r
+                       {\r
+                               ptr = (void*)(iBaseDiff + pltRel[i].r_offset);\r
+                               if( !Elf_Int_DoRelocate(pltRel[i].r_info, ptr, *ptr, dynsymtab, Base) ) {\r
+                                       bFailed = 1;\r
+                               }\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       Elf32_Rela      *pltRela = plt;\r
+                       j = pltSz / sizeof(Elf32_Rela);\r
+                       LOG("PLT RelA - plt = %p, pltSz = %i (%i ents)\n", plt, pltSz, j);\r
+                       for(i=0;i<j;i++)\r
+                       {\r
+                               ptr = (void*)(iBaseDiff + pltRela[i].r_offset);\r
+                               if( !Elf_Int_DoRelocate(pltRela[i].r_info, ptr, pltRela[i].r_addend, dynsymtab, Base) ) {\r
+                                       bFailed = 1;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       if(bFailed) {\r
+               LEAVE('i', 0);\r
+               return 0;\r
+       }\r
+       \r
+       LEAVE('x', hdr->entrypoint);\r
+       return hdr->entrypoint;\r
+}\r
+\r
+/**\r
+ * \fn void Elf_Int_DoRelocate(uint32_t r_info, uint32_t *ptr, uint32_t addend, Elf32_Sym *symtab, void *base)\r
+ * \brief Performs a relocation\r
+ * \param r_info       Field from relocation entry\r
+ * \param ptr  Pointer to location of relocation\r
+ * \param addend       Value to add to symbol\r
+ * \param symtab       Symbol Table\r
+ * \param base Base of loaded binary\r
+ */\r
+int Elf_Int_DoRelocate(uint32_t r_info, uint32_t *ptr, uint32_t addend, Elf32_Sym *symtab, void *base)\r
+{\r
+       uintptr_t       val;\r
+        int    type = ELF32_R_TYPE(r_info);\r
+        int    sym = ELF32_R_SYM(r_info);\r
+       char    *sSymName = PTRMK(char, symtab[sym].name);\r
+       \r
+       //LogF("Elf_Int_DoRelocate: (r_info=0x%x, ptr=0x%x, addend=0x%x, .., base=0x%x)\n",\r
+       //      r_info, ptr, addend, base);\r
+       \r
+       switch( type )\r
+       {\r
+       // Standard 32 Bit Relocation (S+A)\r
+       case R_386_32:\r
+               if( !Elf_GetSymbol( base, sSymName, &val ) && !Binary_GetSymbol( sSymName, &val ) ) {\r
+                       Warning("Unable to find symbol '%s'", sSymName);\r
+                       return 0;\r
+               }\r
+               LOG("%08x R_386_32 *%p += %p('%s')\n", r_info, ptr, (void*)val, sSymName);\r
+               *ptr = val + addend;\r
+               break;\r
+               \r
+       // 32 Bit Relocation wrt. Offset (S+A-P)\r
+       case R_386_PC32:\r
+               if( !Elf_GetSymbol( base, sSymName, &val ) && !Binary_GetSymbol( sSymName, &val ) ) {\r
+                       Warning("Unable to find symbol '%s'", sSymName);\r
+                       return 0;\r
+               }\r
+               LOG("%08x R_386_PC32 *%p = 0x%x + %p('%s') - %p\n", r_info, ptr, *ptr, (void*)val, sSymName, ptr );\r
+               // TODO: Check if it needs the true value of ptr or the compiled value\r
+               // NOTE: Testing using true value\r
+               *ptr = val + addend - (uintptr_t)ptr;\r
+               break;\r
+\r
+       // Absolute Value of a symbol (S)\r
+       case R_386_GLOB_DAT:\r
+               if( !Elf_GetSymbol( base, sSymName, &val ) && !Binary_GetSymbol( sSymName, &val ) ) {\r
+                       Warning("Unable to find symbol '%s'", sSymName);\r
+                       return 0; \r
+               }\r
+               LOG("%08x R_386_GLOB_DAT *%p = 0x%x(%s)\n", r_info, ptr, (unsigned int)val, sSymName);\r
+               *ptr = val;\r
+               break;\r
+       \r
+       // Absolute Value of a symbol (S)\r
+       case R_386_JMP_SLOT:\r
+               if( !Elf_GetSymbol( base, sSymName, &val ) && !Binary_GetSymbol( sSymName, &val ) ) {\r
+                       Warning("Unable to find symbol '%s'", sSymName);\r
+                       return 0;\r
+               }\r
+               LOG("%08x R_386_JMP_SLOT *%p = 0x%x (%s)\n", r_info, ptr, (unsigned int)val, sSymName);\r
+               *ptr = val;\r
+               break;\r
+\r
+       // Base Address (B+A)\r
+       case R_386_RELATIVE:\r
+               LOG("%08x R_386_RELATIVE *%p = %p + 0x%x\n", r_info, ptr, base, addend);\r
+               *ptr = (uintptr_t)base + addend;\r
+               break;\r
+               \r
+       default:\r
+               LOG("Rel %p: 0x%x,%i\n", ptr, sym, type);\r
+               break;\r
+       }\r
+       return 1;\r
+}\r
+\r
+/**\r
+ * \fn int Elf_GetSymbol(void *Base, char *name, uintptr_t *ret)\r
+ * \brief Get a symbol from the loaded binary\r
+ */\r
+int Elf_GetSymbol(void *Base, char *Name, uintptr_t *ret)\r
+{\r
+       Elf32_Ehdr      *hdr = (void*)Base;\r
+       Elf32_Sym       *symtab;\r
+        int    nbuckets = 0;\r
+        int    iSymCount = 0;\r
+        int    i;\r
+       uint32_t        *pBuckets;\r
+       uint32_t        *pChains;\r
+       uint32_t        iNameHash;\r
+\r
+       if(!Base)       return 0;\r
+\r
+       pBuckets = PTR(hdr->misc.HashTable);\r
+       symtab = PTR(hdr->misc.SymTable);\r
+       \r
+       nbuckets = pBuckets[0];\r
+       iSymCount = pBuckets[1];\r
+       pBuckets = &pBuckets[2];\r
+       pChains = &pBuckets[ nbuckets ];\r
+       \r
+       // Get hash\r
+       iNameHash = Elf_Int_HashString(Name);\r
+       iNameHash %= nbuckets;\r
+\r
+       // Check Bucket\r
+       i = pBuckets[ iNameHash ];\r
+       if(symtab[i].shndx != SHN_UNDEF && strcmp(MKPTR(char,symtab[i].name), Name) == 0) {\r
+               if(ret) *ret = symtab[ i ].value;\r
+               return 1;\r
+       }\r
+       \r
+       // Walk Chain\r
+       while(pChains[i] != STN_UNDEF)\r
+       {\r
+               i = pChains[i];\r
+               if(symtab[i].shndx != SHN_UNDEF && strcmp(MKPTR(char,symtab[i].name), Name) == 0) {\r
+                       if(ret) *ret = symtab[ i ].value;\r
+                       return 1;\r
+               }\r
+       }\r
+       return 0;\r
+}\r
+\r
+/**\r
+ * \fn uint32_t Elf_Int_HashString(char *str)\r
+ * \brief Hash a string in the ELF format\r
+ * \param str  String to hash\r
+ * \return Hash value\r
+ */\r
+uint32_t Elf_Int_HashString(char *str)\r
+{\r
+       uint32_t        h = 0, g;\r
+       while(*str)\r
+       {\r
+               h = (h << 4) + *str++;\r
+               if( (g = h & 0xf0000000) )\r
+                       h ^= g >> 24;\r
+               h &= ~g;\r
+       }\r
+       return h;\r
+}\r

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