/* This file is part of Fennix C Library. Fennix C Library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fennix C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Fennix C Library. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "elf.h" #include "misc.h" typedef struct ElfInfo { Elf_Ehdr Header; Elf_Phdr DynamicHeader; uintptr_t BaseAddress; Elf_Dyn *Dynamic; char *Path; struct { Elf_Addr PLTGOT, HASH, STRTAB, SYMTAB, RELA, REL, TEXTREL, JMPREL; Elf_Addr BIND_NOW, INIT, FINI, RPATH, SYMBOLIC, INIT_ARRAY; Elf_Addr FINI_ARRAY, PREINIT_ARRAY, RUNPATH, FLAGS; } DynamicTable; struct { #ifdef __LP64__ _Static_assert(sizeof(Elf64_Xword) == sizeof(size_t), "Elf64_Xword and size_t are not the same size"); #else _Static_assert(sizeof(Elf32_Word) == sizeof(size_t), "Elf32_Word and size_t are not the same size"); #endif size_t PLTRELSZ, RELASZ, RELAENT, STRSZ, SYMENT, RELSZ, RELENT, PLTREL; size_t INIT_ARRAYSZ, FINI_ARRAYSZ, PREINIT_ARRAYSZ; } DynamicSize; union { struct { uint8_t Relocated : 1; uint8_t IsLibrary : 1; uint8_t __padding : 6; }; uint8_t raw; } Flags; struct ElfInfo *prev; struct ElfInfo *next; } ElfInfo; ElfInfo *elf_list_head = NULL; ElfInfo *AllocateLib() { ElfInfo *new_node = mini_malloc(sizeof(ElfInfo)); if (!new_node) { printf("Failed to allocate memory for new library\n"); return NULL; } memset(new_node, 0, sizeof(ElfInfo)); if (!elf_list_head) { elf_list_head = new_node; return new_node; } ElfInfo *current = elf_list_head; while (current->next) current = current->next; current->next = new_node; new_node->prev = current; return new_node; } void FreeLib(ElfInfo *node) { if (!node) return; if (node->prev) node->prev->next = node->next; else elf_list_head = node->next; if (node->next) node->next->prev = node->prev; mini_free(node); } ElfInfo *SearchLib(char *Path) { ElfInfo *current = elf_list_head; while (current) { if (strcmp(current->Path, Path) == 0) return current; current = current->next; } return NULL; } __attribute__((naked, used, no_stack_protector)) void _dl_runtime_resolve() { #if defined(__amd64__) __asm__( "pop %r11\n" /* Pop lazy resolve arguments */ "pop %r10\n" "push %rdi\n" "push %rsi\n" "push %rdx\n" "push %rcx\n" "push %r8\n" "push %r9\n" "mov %r11, %rdi\n" /* Move the first argument to rdi */ "mov %r10, %rsi\n" /* Move the second argument to rsi (rel index) */ "call _dl_fixup\n" /* Call _dl_fixup */ "mov %rax, %r11\n" /* Move the return value to r11 */ "pop %r9\n" "pop %r8\n" "pop %rcx\n" "pop %rdx\n" "pop %rsi\n" "pop %rdi\n" "jmp *%r11\n"); /* Jump to the return value */ #elif defined(__i386__) #warning "i386 _dl_runtime_resolve not implemented" #elif defined(__aarch64__) #warning "aarch64 not implemented" #endif } int RelocateHelper(ElfInfo *Info, Elf_Rela *Rela, short IsRel, void **Relocated); __attribute__((noinline)) void *_dl_fixup(ElfInfo *Info, long RelIndex) { void *ret = NULL; if (Info->DynamicSize.PLTREL == DT_REL) RelocateHelper(Info, (Elf_Rela *)(Info->DynamicTable.JMPREL + RelIndex), 1, &ret); else if (Info->DynamicSize.PLTREL == DT_RELA) RelocateHelper(Info, &((Elf_Rela *)Info->DynamicTable.JMPREL)[RelIndex], 0, &ret); return ret; } #ifdef __fennix__ #include #endif int _dl_preload() { #ifdef __fennix__ call_api_version(0); #endif /* TODO: Do aditional checks for miscellaneous things */ /* Everything is ok, continue. */ return 0; } void HandleGOT(ElfInfo *Info, Elf_Addr Offset) { Elf_Addr *got = (Elf_Addr *)Offset; got[0] = (Elf_Addr)Info->Dynamic; got[1] = (Elf_Addr)Info; got[2] = (Elf_Addr)&_dl_runtime_resolve; } void AdjustDynamicTable(Elf_Dyn *elem, ElfInfo *Info) { switch (elem->d_tag) { case DT_PLTGOT: case DT_HASH: case DT_STRTAB: case DT_SYMTAB: case DT_RELA: case DT_INIT: case DT_FINI: case DT_REL: case DT_JMPREL: case DT_INIT_ARRAY: case DT_FINI_ARRAY: elem->d_un.d_ptr += Info->BaseAddress; break; default: break; } } void CreateInfoTables(Elf_Dyn *elem, ElfInfo *Info) { switch (elem->d_tag) { case DT_NEEDED: break; case DT_PLTRELSZ: Info->DynamicSize.PLTRELSZ = elem->d_un.d_val; break; case DT_PLTGOT: HandleGOT(Info, elem->d_un.d_ptr); Info->DynamicTable.PLTGOT = elem->d_un.d_ptr; break; case DT_HASH: Info->DynamicTable.HASH = elem->d_un.d_ptr; break; case DT_STRTAB: Info->DynamicTable.STRTAB = elem->d_un.d_ptr; break; case DT_SYMTAB: Info->DynamicTable.SYMTAB = elem->d_un.d_ptr; break; case DT_RELA: Info->DynamicTable.RELA = elem->d_un.d_ptr; break; case DT_RELASZ: Info->DynamicSize.RELASZ = elem->d_un.d_val; break; case DT_RELAENT: Info->DynamicSize.RELAENT = elem->d_un.d_val; break; case DT_STRSZ: Info->DynamicSize.STRSZ = elem->d_un.d_val; break; case DT_SYMENT: Info->DynamicSize.SYMENT = elem->d_un.d_val; break; case DT_INIT: Info->DynamicTable.INIT = elem->d_un.d_ptr; break; case DT_FINI: Info->DynamicTable.FINI = elem->d_un.d_ptr; break; case DT_RPATH: Info->DynamicTable.RPATH = elem->d_un.d_ptr; break; case DT_SYMBOLIC: Info->DynamicTable.SYMBOLIC = elem->d_un.d_ptr; break; case DT_REL: Info->DynamicTable.REL = elem->d_un.d_ptr; break; case DT_RELSZ: Info->DynamicSize.RELSZ = elem->d_un.d_val; break; case DT_RELENT: Info->DynamicSize.RELENT = elem->d_un.d_val; break; case DT_PLTREL: Info->DynamicSize.PLTREL = elem->d_un.d_val; break; // case DT_DEBUG: case DT_TEXTREL: Info->DynamicTable.TEXTREL = elem->d_un.d_ptr; break; case DT_JMPREL: Info->DynamicTable.JMPREL = elem->d_un.d_ptr; break; case DT_BIND_NOW: Info->DynamicTable.BIND_NOW = elem->d_un.d_ptr; break; case DT_INIT_ARRAY: Info->DynamicTable.INIT_ARRAY = elem->d_un.d_ptr; break; case DT_FINI_ARRAY: Info->DynamicTable.FINI_ARRAY = elem->d_un.d_ptr; break; case DT_INIT_ARRAYSZ: Info->DynamicSize.INIT_ARRAYSZ = elem->d_un.d_val; break; case DT_FINI_ARRAYSZ: Info->DynamicSize.FINI_ARRAYSZ = elem->d_un.d_val; break; case DT_RUNPATH: Info->DynamicTable.RUNPATH = elem->d_un.d_ptr; break; case DT_FLAGS: Info->DynamicTable.FLAGS = elem->d_un.d_ptr; break; // case DT_ENCODING: case DT_PREINIT_ARRAY: Info->DynamicTable.PREINIT_ARRAY = elem->d_un.d_ptr; break; case DT_PREINIT_ARRAYSZ: Info->DynamicSize.PREINIT_ARRAYSZ = elem->d_un.d_val; break; case DT_LOOS: case DT_SUNW_RTLDINF: case DT_HIOS: case DT_VALRNGLO: case DT_CHECKSUM: case DT_PLTPADSZ: case DT_MOVEENT: case DT_MOVESZ: case DT_FEATURE_1: case DT_POSFLAG_1: case DT_SYMINSZ: case DT_SYMINENT: // case DT_VALRNGHI: case DT_ADDRRNGLO: case DT_CONFIG: case DT_DEPAUDIT: case DT_AUDIT: case DT_PLTPAD: case DT_MOVETAB: case DT_SYMINFO: // case DT_ADDRRNGHI: case DT_RELACOUNT: case DT_RELCOUNT: case DT_FLAGS_1: case DT_VERDEF: case DT_VERDEFNUM: case DT_VERNEED: case DT_VERNEEDNUM: case DT_LOPROC: case DT_SPARC_REGISTER: case DT_AUXILIARY: case DT_USED: case DT_FILTER: // case DT_HIPROC: default: break; } } int LoadElf(int, char *, ElfInfo **); void ProcessNeededLibraries(Elf_Dyn *elem, ElfInfo *Info) { char *libPath = (char *)Info->DynamicTable.STRTAB + elem->d_un.d_val; ElfInfo *info = NULL; char fullLibPath[PATH_MAX]; int found = 0; char *ldLibPath = getenv("LD_LIBRARY_PATH"); if (ldLibPath) { char *pathCopy = strdup(ldLibPath); char *path = strtok(pathCopy, ":"); while (path) { strcpy(fullLibPath, path); if (fullLibPath[strlen(fullLibPath) - 1] != '/') strcat(fullLibPath, "/"); strcat(fullLibPath, libPath); if (sysdep(Access)(fullLibPath, F_OK) == 0) { found = 1; break; } path = strtok(NULL, ":"); } free(pathCopy); if (found) goto load_lib; } const char *standardPaths[] = { "/sys/lib/", "/usr/lib/", "/lib/", "/usr/local/lib/", "/usr/local/lib64/", "/usr/lib64/", "/lib64/"}; for (size_t i = 0; i < sizeof(standardPaths) / sizeof(standardPaths[0]); i++) { strcpy(fullLibPath, standardPaths[i]); strcat(fullLibPath, libPath); if (sysdep(Access)(fullLibPath, F_OK) == 0) { found = 1; break; } } if (!found) { printf("dl: Library %s not found in search paths\n", libPath); return; } load_lib: int fd = sysdep(Open)(fullLibPath, O_RDONLY, 0644); int status = LoadElf(fd, fullLibPath, &info); elem->d_un.d_ptr = (uintptr_t)info; /* if LoadElf fails, info will still be NULL */ sysdep(Close)(fd); if (status < 0) /* announce that LoadElf failed */ printf("dl: Can't load %s\n", fullLibPath); } void ProcessDynamicTable(ElfInfo *Info) { for (size_t i = 0;; i++) { Elf_Dyn *elem = &Info->Dynamic[i]; if (elem->d_tag == DT_NULL) break; AdjustDynamicTable(elem, Info); CreateInfoTables(elem, Info); } /* TODO: Optimize this, we don't have to recheck every element */ for (size_t i = 0;; i++) { Elf_Dyn *elem = &Info->Dynamic[i]; if (elem->d_tag == DT_NULL) break; if (elem->d_tag != DT_NEEDED) continue; ProcessNeededLibraries(elem, Info); } } uintptr_t GetASLR() { #ifdef DEBUG static uintptr_t __aslr_stub_next = 0; __aslr_stub_next += 0x1000000; return __aslr_stub_next; #else /* FIXME: implement real ASLR */ static uintptr_t __aslr_stub_next = 0; __aslr_stub_next += 0x1000000; return __aslr_stub_next; #endif } int LoadElfPhdrEXEC(int fd, ElfInfo *Info) { printf("dl: ET_EXEC not implemented yet\n"); return -ENOSYS; } uintptr_t DynamicCreateBase(int fd, ElfInfo *Info) { uintptr_t base = GetASLR(); if ((uintptr_t)base <= 0) { printf("dl: Can't get ASLR\n"); _Exit(-1); } Info->BaseAddress = base; return base; } int LoadElfPhdrDYN(int fd, ElfInfo *Info) { Elf_Ehdr header = Info->Header; Info->Dynamic = NULL; uintptr_t base = 0; Elf_Phdr phdr, lastLOAD; for (Elf_Half i = 0; i < header.e_phnum; i++) { ssize_t read = sysdep(PRead)(fd, &phdr, sizeof(Elf_Phdr), header.e_phoff + (header.e_phentsize * i)); if (read != sizeof(Elf_Phdr)) { printf("dl: Can't read program header %d\n", i); return (int)read; } switch (phdr.p_type) { case PT_LOAD: { if (phdr.p_memsz == 0) continue; if (base == 0) base = DynamicCreateBase(fd, Info); int mmapProt = 0; if (phdr.p_flags & PF_X) mmapProt |= PROT_EXEC; if (phdr.p_flags & PF_W) mmapProt |= PROT_WRITE; if (phdr.p_flags & PF_R) mmapProt |= PROT_READ; off_t sectionOffset = ALIGN_DOWN(phdr.p_vaddr, phdr.p_align); size_t sectionSize = ALIGN_UP(phdr.p_memsz + (phdr.p_vaddr - sectionOffset), phdr.p_align); uintptr_t section = (uintptr_t)sysdep(MemoryMap)((void *)(base + sectionOffset), sectionSize, mmapProt, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); sectionOffset = phdr.p_vaddr - ALIGN_DOWN(phdr.p_vaddr, phdr.p_align); if (phdr.p_filesz > 0) { ssize_t read = sysdep(PRead)(fd, (void *)(section + sectionOffset), phdr.p_filesz, phdr.p_offset); if (read != phdr.p_filesz) { printf("dl: Can't read segment %d in PT_LOAD\n", i); return (int)read; } } if (phdr.p_memsz - phdr.p_filesz > 0) { /* TODO: Do we really have to do this? Kernel already zeros the memory for us */ void *zero = (void *)(section + sectionOffset + phdr.p_filesz); memset(zero, 0, phdr.p_memsz - phdr.p_filesz); } lastLOAD = phdr; break; } case PT_DYNAMIC: { Elf_Dyn *dynamicTable = NULL; if (phdr.p_vaddr == lastLOAD.p_vaddr && phdr.p_memsz < lastLOAD.p_memsz) { /* The dynamic section is inside the last LOAD segment */ dynamicTable = (Elf_Dyn *)(base + phdr.p_vaddr); } else { int mmapProt = 0; if (phdr.p_flags & PF_X) mmapProt |= PROT_EXEC; if (phdr.p_flags & PF_W) mmapProt |= PROT_WRITE; if (phdr.p_flags & PF_R) mmapProt |= PROT_READ; dynamicTable = (Elf_Dyn *)sysdep(MemoryMap)(0, ALIGN_UP(phdr.p_memsz, phdr.p_align), mmapProt, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); if ((intptr_t)dynamicTable <= 0) { printf("dl: Can't allocate memory for PT_DYNAMIC\n"); return (int)(uintptr_t)dynamicTable; } read = sysdep(PRead)(fd, dynamicTable, phdr.p_memsz, phdr.p_offset); if (read != phdr.p_memsz) { printf("dl: Can't read PT_DYNAMIC\n"); return (int)read; } } Info->Dynamic = dynamicTable; Info->DynamicHeader = phdr; break; } case PT_INTERP: break; case PT_NOTE: break; case PT_SHLIB: break; case PT_PHDR: break; case PT_TLS: { printf("dl: PT_TLS not implemented yet\n"); break; } default: { printf("dl: Unimplemented program header type %d\n", phdr.p_type); break; } } } return 0; } int CheckElfEhdr(Elf_Ehdr *ehdr, char *Path) { if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || ehdr->e_ident[EI_MAG1] != ELFMAG1 || ehdr->e_ident[EI_MAG2] != ELFMAG2 || ehdr->e_ident[EI_MAG3] != ELFMAG3) { printf("dl: %s is not an ELF file\n", Path); return -EINVAL; } #ifdef __LP64__ const int elfClass = ELFCLASS64; #else const int elfClass = ELFCLASS32; #endif if (ehdr->e_ident[EI_CLASS] != elfClass) { printf("dl: %s is not a %s-bit ELF file\n", Path, elfClass == ELFCLASS64 ? "64" : "32"); return -EINVAL; } /* TODO: check LSB MSB */ if (ehdr->e_ident[EI_VERSION] != EV_CURRENT || ehdr->e_version != EV_CURRENT) { printf("dl: %s has an unsupported ELF version\n", Path); return -EINVAL; } if (ehdr->e_type != ET_DYN && ehdr->e_type != ET_EXEC) { printf("dl: %s is not a shared object or executable\n", Path); return -EINVAL; } return 0; } int LoadElf(int fd, char *Path, ElfInfo **Out) { ElfInfo *info = SearchLib(Path); if (info != NULL) { *Out = info; return 0; } Elf_Ehdr header; sysdep(PRead)(fd, &header, sizeof(Elf_Ehdr), 0); int status = CheckElfEhdr(&header, Path); if (status != 0) return status; info = AllocateLib(); info->Header = header; info->Path = (char *)sysdep(MemoryMap)(0, ALIGN_UP(strlen(Path) + 1, 0x1000 /* TODO: get page size from kernel */), PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if ((intptr_t)info->Path <= 0) { printf("dl: Can't allocate memory for path\n"); FreeLib(info); return (int)(uintptr_t)info->Path; } memcpy(info->Path, Path, strlen(Path) + 1); switch (header.e_type) { case ET_REL: printf("dl: ET_REL not implemented yet\n"); status = -ENOSYS; break; case ET_EXEC: status = LoadElfPhdrEXEC(fd, info); break; case ET_DYN: status = LoadElfPhdrDYN(fd, info); break; case ET_CORE: printf("dl: ET_CORE not implemented yet\n"); status = -ENOSYS; break; case ET_NONE: printf("dl: ET_NONE???\n"); status = -EINVAL; break; default: printf("dl: Unsupported ELF type %d\n", header.e_type); status = -EINVAL; break; } if (status < 0) { sysdep(MemoryUnmap)((void *)info->Path, ALIGN_UP(strlen(Path) + 1, 0x1000)); FreeLib(info); return status; } ProcessDynamicTable(info); *Out = info; return 0; } uintptr_t GetSymbolAddress(ElfInfo *Info, const char *SymbolName) { Elf64_Sym *sym = find_symbol(SymbolName, (uint32_t *)Info->DynamicTable.HASH, (Elf64_Sym *)Info->DynamicTable.SYMTAB, (const char *)Info->DynamicTable.STRTAB); if (sym == NULL) return (-ENOSYS); return Info->BaseAddress + sym->st_value; } int ResolveExternalSymbol(ElfInfo *Info, uintptr_t *symAddress, Elf_Sym *sym, const char *symName) { *symAddress = (-ENOSYS); for (size_t i = 0; *symAddress == (-ENOSYS); i++) { Elf_Dyn *dyn = &Info->Dynamic[i]; if (dyn->d_tag == DT_NULL) break; if (dyn->d_tag != DT_NEEDED) continue; ElfInfo *lib = (ElfInfo *)dyn->d_un.d_ptr; *symAddress = GetSymbolAddress(lib, symName); } if (*symAddress != (-ENOSYS)) return 0; printf("%s: Unresolved symbol: %s\n", Info->Path, symName); if (ELF_ST_BIND(sym->st_info) != STB_WEAK) return -EINVAL; *symAddress = 0; return 0; } int ApplyRelocation(ElfInfo *Info, uintptr_t *reloc, Elf_Rela *Rela, size_t reloSize) { switch (reloSize) { case 0: break; case sizeof(uint8_t): *reloc += *(uint8_t *)(Info->BaseAddress + Rela->r_offset); break; case sizeof(uint16_t): *reloc += *(uint16_t *)(Info->BaseAddress + Rela->r_offset); break; case sizeof(uint32_t): *reloc += *(uint32_t *)(Info->BaseAddress + Rela->r_offset); break; case sizeof(uint64_t): *reloc += *(uint64_t *)(Info->BaseAddress + Rela->r_offset); break; default: { printf("dl: Unsupported size for relocation\n"); return -EINVAL; } } return 0; } int CalculateRelocation(ElfInfo *Info, uintptr_t *reloc, Elf_Rela *Rela, size_t reloSize) { switch (reloSize) { case 0: break; case sizeof(uint8_t): *(uint8_t *)(Info->BaseAddress + Rela->r_offset) = *reloc; break; case sizeof(uint16_t): *(uint16_t *)(Info->BaseAddress + Rela->r_offset) = *reloc; break; case sizeof(uint32_t): *(uint32_t *)(Info->BaseAddress + Rela->r_offset) = *reloc; break; case sizeof(uint64_t): *(uint64_t *)(Info->BaseAddress + Rela->r_offset) = *reloc; break; default: { printf("dl: Unsupported size for relocation\n"); return -EINVAL; } } return 0; } int RelocateHelper(ElfInfo *Info, Elf_Rela *Rela, short IsRel, void **Relocated) { uintptr_t reloc = 0; uintptr_t symAddress = 0; size_t symSize = 0; uint32_t symIndex = ELF_R_SYM(Rela->r_info); if (symIndex) { Elf_Sym *sym = (Elf_Sym *)(Info->DynamicTable.SYMTAB + symIndex * Info->DynamicSize.SYMENT); const char *symName = (const char *)(Info->DynamicTable.STRTAB + sym->st_name); symSize = sym->st_size; if (!(ELF_R_TYPE(Rela->r_info) == R_COPY) && sym->st_shndx) symAddress = Info->BaseAddress + sym->st_value; else if (ResolveExternalSymbol(Info, &symAddress, sym, symName) < 0) return -EINVAL; } size_t reloSize = 0; int addAddend = 0; enum RelocationTypes relType = ELF_R_TYPE(Rela->r_info); switch (relType) { case R_NONE: break; case R_X86_64_64: { reloSize = 8; reloc = symAddress; addAddend = 1; break; } case R_COPY: { if (symAddress == 0) { printf("dl: Copy undefined weak symbol %d\n", ELF_R_SYM(Rela->r_info)); return -EINVAL; } memcpy((void *)(Info->BaseAddress + Rela->r_offset), (void *)symAddress, symSize); break; } case R_GLOB_DAT: case R_JMP_SLOT: { reloSize = 8; reloc = symAddress; break; } case R_RELATIVE: { reloSize = 8; reloc = Info->BaseAddress; addAddend = 1; break; } #if __LP64__ case R_DTPMOD64: { printf("dl: i don't know what to do with DTPMOD64\n"); reloc = Info->BaseAddress; break; } #if defined(__amd64__) case R_DTPOFF64: { printf("dl: i don't know what to do with DTPOFF64\n"); reloc = symAddress + Rela->r_addend; break; } case R_TPOFF64: { printf("dl: i don't know what to do with TPOFF64\n"); reloc = symAddress + Rela->r_addend; break; } #endif #endif // __LP64__ default: { printf("dl: Unsupported relocation type %d\n", relType); return -EINVAL; } } if (addAddend) { if (IsRel == 0) reloc += Rela->r_addend; else if (ApplyRelocation(Info, &reloc, Rela, reloSize) < 0) return -EINVAL; } CalculateRelocation(Info, &reloc, Rela, reloSize); if (Relocated != NULL) *Relocated = (void *)reloc; return 0; } int HandleRelocations(ElfInfo *Info); void SearchNeeded(ElfInfo *Info) { for (size_t i = 0;; i++) { Elf_Dyn *elem = &Info->Dynamic[i]; if (elem->d_tag == DT_NULL) break; if (elem->d_tag != DT_NEEDED) continue; HandleRelocations((ElfInfo *)elem->d_un.d_ptr); } } int HandleRelocations(ElfInfo *Info) { if (Info->Flags.Relocated) return 0; SearchNeeded(Info); if (Info->DynamicTable.REL != ((Elf_Addr)0) && Info->DynamicSize.RELENT != 0) { for (size_t i = 0; i < Info->DynamicSize.RELSZ / Info->DynamicSize.RELENT; i++) RelocateHelper(Info, (Elf_Rela *)(Info->DynamicTable.REL + i * Info->DynamicSize.RELENT), 1, NULL); } if (Info->DynamicTable.RELA != ((Elf_Addr)0) && Info->DynamicSize.RELAENT != 0) { for (size_t i = 0; i < Info->DynamicSize.RELASZ / Info->DynamicSize.RELAENT; i++) RelocateHelper(Info, (Elf_Rela *)(Info->DynamicTable.RELA + i * Info->DynamicSize.RELAENT), 0, NULL); } if (Info->DynamicTable.JMPREL == ((Elf_Addr)0) || Info->DynamicSize.PLTRELSZ == 0) return 0; if (Info->DynamicSize.PLTREL != DT_REL && Info->DynamicSize.PLTREL != DT_RELA) { printf("dl: Wrong PLT relocation type %d\n", Info->DynamicSize.PLTREL); return -EINVAL; } if (Info->DynamicTable.BIND_NOW != ((Elf_Addr)0)) { if (Info->DynamicSize.PLTREL == DT_REL) { for (size_t i = 0; i < Info->DynamicSize.PLTRELSZ / sizeof(Elf_Rel); i++) RelocateHelper(Info, (Elf_Rela *)&((Elf_Rel *)Info->DynamicTable.JMPREL)[i], 1, NULL); } else if (Info->DynamicSize.PLTREL == DT_RELA) { for (size_t i = 0; i < Info->DynamicSize.PLTRELSZ / sizeof(Elf_Rela); i++) RelocateHelper(Info, (Elf_Rela *)&((Elf_Rela *)Info->DynamicTable.JMPREL)[i], 0, NULL); } Info->Flags.Relocated = 1; return 0; } size_t relsize = Info->DynamicSize.PLTREL == DT_REL ? sizeof(Elf_Rel) : sizeof(Elf_Rela); for (size_t i = 0; i < Info->DynamicSize.PLTRELSZ / relsize; i++) { Elf64_Xword info = (Info->DynamicSize.PLTREL == DT_REL ? ((Elf_Rel *)Info->DynamicTable.JMPREL)[i].r_info : ((Elf_Rela *)Info->DynamicTable.JMPREL)[i].r_info); Elf_Addr offset = (Info->DynamicSize.PLTREL == DT_REL ? ((Elf_Rel *)Info->DynamicTable.JMPREL)[i].r_offset : ((Elf_Rela *)Info->DynamicTable.JMPREL)[i].r_offset); if (ELF_R_TYPE(info) != R_JMP_SLOT) { printf("dl: Wrong JMPREL type %d\n", ELF_R_TYPE(info)); return -EINVAL; } /* FIXME: HANDLE THIS RIGHT */ if (Info->DynamicSize.PLTREL == DT_REL) { Elf_Addr *slot = (Elf_Addr *)(Info->BaseAddress + offset); *slot += Info->BaseAddress; if (*slot == Info->BaseAddress) RelocateHelper(Info, (Elf_Rela *)&((Elf_Rel *)Info->DynamicTable.JMPREL)[i], 1, NULL); } else if (Info->DynamicSize.PLTREL == DT_RELA) { Elf64_Sxword addend = ((Elf_Rela *)Info->DynamicTable.JMPREL)[i].r_addend; Elf_Addr *slot = (Elf_Addr *)(Info->BaseAddress + offset); *slot += Info->BaseAddress + addend; if (*slot == Info->BaseAddress) RelocateHelper(Info, (Elf_Rela *)&((Elf_Rela *)Info->DynamicTable.JMPREL)[i], 0, NULL); } } Info->Flags.Relocated = 1; return 0; } int _dl_main(int argc, char *argv[], char *envp[]) { char *path = argv[0]; ElfInfo *info = NULL; if (sysdep(Access)(path, F_OK) < 0) { printf("dl: Can't access file %s\n", path); return -EACCES; } int fd = sysdep(Open)(path, O_RDONLY, 0644); int status = LoadElf(fd, path, &info); if (status < 0) { printf("%s: Can't load ELF file\n", path); sysdep(Close)(fd); return status; } status = HandleRelocations(info); if (status < 0) { printf("%s: Can't relocate ELF file\n", path); sysdep(Close)(fd); return status; } sysdep(Close)(fd); Elf_Addr entry = info->BaseAddress + info->Header.e_entry; return ((int (*)(int, char *[], char *[]))entry)(argc, argv, envp); }