1045 lines
23 KiB
C

/*
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 <https://www.gnu.org/licenses/>.
*/
#include <bits/libc.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <inttypes.h>
#include <stddef.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#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 <fennix/syscalls.h>
#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, ":");
}
mini_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);
}