mirror of
https://github.com/EnderIce2/Fennix.git
synced 2025-05-25 22:14:34 +00:00
1045 lines
23 KiB
C
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);
|
|
}
|