mirror of
https://github.com/EnderIce2/Fennix.git
synced 2025-07-02 10:59:15 +00:00
userspace: Rewrite everything
Everything. Signed-off-by: EnderIce2 <enderice2@protonmail.com>
This commit is contained in:
40
Userspace/libc/interpreter/Makefile
Normal file
40
Userspace/libc/interpreter/Makefile
Normal file
@ -0,0 +1,40 @@
|
||||
default:
|
||||
$(error Do not run this Makefile directly!)
|
||||
|
||||
OBJECT_NAME := ld.so
|
||||
|
||||
OUTPUT_DIR=$(WORKSPACE_DIR)/out/lib/
|
||||
SYSROOT = --sysroot=$(WORKSPACE_DIR)/out/
|
||||
|
||||
S_SOURCES = $(shell find ./ -type f -name '*.S')
|
||||
C_SOURCES = $(shell find ./ -type f -name '*.c')
|
||||
CXX_SOURCES = $(shell find ./ -type f -name '*.cpp')
|
||||
OBJ = ${S_SOURCES:.S=.o} ${C_SOURCES:.c=.o} ${CXX_SOURCES:.cpp=.o}
|
||||
|
||||
CFLAGS := -fvisibility=hidden -fPIC -I$(WORKSPACE_DIR)/out/include -DLIBC_GIT_COMMIT='"$(shell git rev-parse HEAD)"'
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fverbose-asm
|
||||
endif
|
||||
|
||||
build: $(OBJECT_NAME)
|
||||
|
||||
$(OBJECT_NAME): $(OBJ)
|
||||
$(info Linking $@)
|
||||
$(CC) -nostdlib -shared -fPIC -fPIE -fno-plt -Wl,-soname,$(OBJECT_NAME) $(SYSROOT) $(OBJ) -o $(OBJECT_NAME)
|
||||
cp $(OBJECT_NAME) $(OUTPUT_DIR)$(OBJECT_NAME)
|
||||
|
||||
%.o: %.c
|
||||
$(info Compiling $<)
|
||||
$(CC) $(CFLAGS) -std=c17 -c $< -o $@
|
||||
|
||||
%.o: %.cpp
|
||||
$(info Compiling $<)
|
||||
$(CC) $(CFLAGS) -std=c++20 -c $< -o $@
|
||||
|
||||
%.o: %.S
|
||||
$(info Compiling $<)
|
||||
$(AS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) $(OBJECT_NAME)
|
117
Userspace/libc/interpreter/alloc.c
Normal file
117
Userspace/libc/interpreter/alloc.c
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
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 <fennix/syscalls.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "elf.h"
|
||||
#include "misc.h"
|
||||
|
||||
typedef struct MemoryBlock
|
||||
{
|
||||
struct MemoryBlock *next;
|
||||
void *slots;
|
||||
size_t slot_size;
|
||||
size_t slots_per_block;
|
||||
uint8_t *bitmap;
|
||||
} MemoryBlock;
|
||||
|
||||
MemoryBlock *memory_pool = NULL;
|
||||
#define PAGE_SIZE 0x1000
|
||||
|
||||
void *request_page(size_t size)
|
||||
{
|
||||
size_t aligned_size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
|
||||
void *addr = (void *)call_mmap(NULL, aligned_size, __SYS_PROT_READ | __SYS_PROT_WRITE, __SYS_MAP_ANONYMOUS | __SYS_MAP_PRIVATE, -1, 0);
|
||||
if ((intptr_t)addr < 0)
|
||||
return NULL;
|
||||
return addr;
|
||||
}
|
||||
|
||||
void free_page(void *addr, size_t size)
|
||||
{
|
||||
size_t aligned_size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
|
||||
call_munmap(addr, aligned_size);
|
||||
}
|
||||
|
||||
MemoryBlock *allocate_block(size_t slot_size)
|
||||
{
|
||||
size_t block_size = PAGE_SIZE;
|
||||
size_t slots_per_block = block_size / slot_size;
|
||||
size_t bitmap_size = (slots_per_block + 7) / 8;
|
||||
|
||||
MemoryBlock *block = request_page(block_size);
|
||||
if (!block)
|
||||
return NULL;
|
||||
|
||||
block->slots = (void *)((uintptr_t)block + sizeof(MemoryBlock) + bitmap_size);
|
||||
block->slot_size = slot_size;
|
||||
block->slots_per_block = slots_per_block;
|
||||
block->bitmap = (uint8_t *)((uintptr_t)block + sizeof(MemoryBlock));
|
||||
memset(block->bitmap, 0, bitmap_size);
|
||||
block->next = NULL;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
void *mini_malloc(size_t size)
|
||||
{
|
||||
MemoryBlock *block = memory_pool;
|
||||
while (block)
|
||||
{
|
||||
for (size_t i = 0; i < block->slots_per_block; i++)
|
||||
{
|
||||
size_t byte = i / 8, bit = i % 8;
|
||||
if (!(block->bitmap[byte] & (1 << bit)))
|
||||
{
|
||||
block->bitmap[byte] |= (1 << bit);
|
||||
return (void *)((uintptr_t)block->slots + i * size);
|
||||
}
|
||||
}
|
||||
block = block->next;
|
||||
}
|
||||
|
||||
block = allocate_block(size);
|
||||
if (!block)
|
||||
return NULL;
|
||||
|
||||
block->next = memory_pool;
|
||||
memory_pool = block;
|
||||
|
||||
block->bitmap[0] |= 1;
|
||||
return block->slots;
|
||||
}
|
||||
|
||||
void mini_free(void *ptr)
|
||||
{
|
||||
MemoryBlock *block = memory_pool;
|
||||
while (block)
|
||||
{
|
||||
if ((uintptr_t)ptr >= (uintptr_t)block->slots &&
|
||||
(uintptr_t)ptr < (uintptr_t)block->slots + block->slots_per_block * block->slot_size)
|
||||
{
|
||||
size_t index = ((uintptr_t)ptr - (uintptr_t)block->slots) / block->slot_size;
|
||||
size_t byte = index / 8, bit = index % 8;
|
||||
block->bitmap[byte] &= ~(1 << bit);
|
||||
return;
|
||||
}
|
||||
block = block->next;
|
||||
}
|
||||
}
|
1013
Userspace/libc/interpreter/elf.h
Normal file
1013
Userspace/libc/interpreter/elf.h
Normal file
File diff suppressed because it is too large
Load Diff
131
Userspace/libc/interpreter/helper.c
Normal file
131
Userspace/libc/interpreter/helper.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
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 <fennix/syscalls.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "elf.h"
|
||||
#include "misc.h"
|
||||
|
||||
void *memset(void *s, int c, size_t n)
|
||||
{
|
||||
uint8_t *p = s;
|
||||
while (n--)
|
||||
*p++ = c;
|
||||
return s;
|
||||
}
|
||||
|
||||
void *memcpy(void *dest, const void *src, size_t n)
|
||||
{
|
||||
uint8_t *d = dest;
|
||||
const uint8_t *s = src;
|
||||
while (n--)
|
||||
*d++ = *s++;
|
||||
return dest;
|
||||
}
|
||||
|
||||
size_t strlen(const char *s)
|
||||
{
|
||||
const char *p = s;
|
||||
while (*p)
|
||||
p++;
|
||||
return p - s;
|
||||
}
|
||||
|
||||
char *strcpy(char *dest, const char *src)
|
||||
{
|
||||
char *d = dest;
|
||||
while ((*d++ = *src++))
|
||||
;
|
||||
return dest;
|
||||
}
|
||||
|
||||
int strcmp(const char *l, const char *r)
|
||||
{
|
||||
while (*l && *l == *r)
|
||||
{
|
||||
l++;
|
||||
r++;
|
||||
}
|
||||
return *l - *r;
|
||||
}
|
||||
|
||||
char *strcat(char *dest, const char *src)
|
||||
{
|
||||
char *d = dest;
|
||||
while (*d)
|
||||
d++;
|
||||
while ((*d++ = *src++))
|
||||
;
|
||||
return dest;
|
||||
}
|
||||
|
||||
unsigned long elf_hash(const unsigned char *name)
|
||||
{
|
||||
unsigned long hash = 0, high;
|
||||
while (*name)
|
||||
{
|
||||
hash = (hash << 4) + *name++;
|
||||
if ((high = hash & 0xF0000000))
|
||||
hash ^= high >> 24;
|
||||
hash &= ~high;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
uint32_t gnu_hash(const char *name)
|
||||
{
|
||||
uint32_t hash = 5381;
|
||||
for (; *name; name++)
|
||||
{
|
||||
hash = (hash << 5) + hash + (unsigned char)(*name); // hash * 33 + c
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
Elf64_Sym *find_symbol(const char *name, uint32_t *hash_table, Elf64_Sym *symtab, const char *strtab)
|
||||
{
|
||||
/* Symbol Hash Table
|
||||
|-------------------|
|
||||
| nbucket |
|
||||
|-------------------|
|
||||
| nchain |
|
||||
|-------------------|
|
||||
| bucket[0] |
|
||||
| . . . |
|
||||
|bucket[nbucket - 1]|
|
||||
|-------------------|
|
||||
| chain[0] |
|
||||
| . . . |
|
||||
| chain[nchain - 1] |
|
||||
|-------------------|
|
||||
*/
|
||||
unsigned long h = elf_hash(name); // or gnu_hash(name)
|
||||
unsigned long bucket = h % hash_table[0]; // hash_table[0] = nbucket
|
||||
|
||||
for (unsigned long i = hash_table[2 + bucket];
|
||||
i != STN_UNDEF;
|
||||
i = hash_table[2 + hash_table[0] + i])
|
||||
{
|
||||
if (!strcmp(&strtab[symtab[i].st_name], name))
|
||||
return &symtab[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
975
Userspace/libc/interpreter/load.c
Normal file
975
Userspace/libc/interpreter/load.c
Normal file
@ -0,0 +1,975 @@
|
||||
/*
|
||||
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 <fennix/syscalls.h>
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <limits.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()
|
||||
{
|
||||
__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 */
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int _dl_preload()
|
||||
{
|
||||
call_api_version(0);
|
||||
|
||||
/* 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];
|
||||
strcpy(fullLibPath, "/lib/");
|
||||
strcat(fullLibPath, libPath);
|
||||
/* TODO: more checks and also check environment variables */
|
||||
if (call_access(fullLibPath, __SYS_F_OK) != 0)
|
||||
{
|
||||
printf("dl: Can't access %s\n", fullLibPath);
|
||||
return;
|
||||
}
|
||||
|
||||
int fd = call_open(fullLibPath, __SYS_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 */
|
||||
call_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 = call_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 |= __SYS_PROT_EXEC;
|
||||
if (phdr.p_flags & PF_W)
|
||||
mmapProt |= __SYS_PROT_WRITE;
|
||||
if (phdr.p_flags & PF_R)
|
||||
mmapProt |= __SYS_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 = call_mmap(base + sectionOffset,
|
||||
sectionSize, mmapProt,
|
||||
__SYS_MAP_ANONYMOUS | __SYS_MAP_PRIVATE | __SYS_MAP_FIXED,
|
||||
-1, 0);
|
||||
sectionOffset = phdr.p_vaddr - ALIGN_DOWN(phdr.p_vaddr, phdr.p_align);
|
||||
|
||||
if (phdr.p_filesz > 0)
|
||||
{
|
||||
ssize_t read = call_pread(fd, 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 |= __SYS_PROT_EXEC;
|
||||
if (phdr.p_flags & PF_W)
|
||||
mmapProt |= __SYS_PROT_WRITE;
|
||||
if (phdr.p_flags & PF_R)
|
||||
mmapProt |= __SYS_PROT_READ;
|
||||
|
||||
dynamicTable = (Elf_Dyn *)call_mmap(0, ALIGN_UP(phdr.p_memsz, phdr.p_align),
|
||||
mmapProt, __SYS_MAP_ANONYMOUS | __SYS_MAP_PRIVATE | __SYS_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 = call_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, __LP64__ ? "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;
|
||||
call_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 *)call_mmap(0,
|
||||
ALIGN_UP(strlen(Path) + 1, 0x1000 /* TODO: get page size from kernel */),
|
||||
__SYS_PROT_READ,
|
||||
__SYS_MAP_ANONYMOUS | __SYS_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)
|
||||
{
|
||||
call_munmap((uintptr_t)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;
|
||||
}
|
||||
case R_DTPMOD64:
|
||||
{
|
||||
printf("dl: i don't know what to do with DTPMOD64\n");
|
||||
reloc = Info->BaseAddress;
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 (call_access(path, __SYS_F_OK) < 0)
|
||||
{
|
||||
printf("dl: Can't access file %s\n", path);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
int fd = call_open(path, __SYS_O_RDONLY, 0644);
|
||||
int status = LoadElf(fd, path, &info);
|
||||
if (status < 0)
|
||||
{
|
||||
printf("%s: Can't load ELF file\n", path);
|
||||
call_close(fd);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = HandleRelocations(info);
|
||||
if (status < 0)
|
||||
{
|
||||
printf("%s: Can't relocate ELF file\n", path);
|
||||
call_close(fd);
|
||||
return status;
|
||||
}
|
||||
|
||||
call_close(fd);
|
||||
Elf_Addr entry = info->BaseAddress + info->Header.e_entry;
|
||||
return ((int (*)(int, char *[], char *[]))entry)(argc, argv, envp);
|
||||
}
|
51
Userspace/libc/interpreter/main.c
Normal file
51
Userspace/libc/interpreter/main.c
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int printf(const char *, ...);
|
||||
int _dl_main(int, char *[], char *[]);
|
||||
|
||||
void print_help()
|
||||
{
|
||||
printf("Usage: ld.so [options] <program>\n");
|
||||
printf("Options:\n");
|
||||
printf(" --help Display this help message\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[], char *envp[])
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
printf("Error: No program specified.\n");
|
||||
print_help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
if (strcmp(argv[i], "--help") == 0)
|
||||
{
|
||||
print_help();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int status = _dl_main(argc, argv, envp);
|
||||
return status;
|
||||
}
|
50
Userspace/libc/interpreter/misc.h
Normal file
50
Userspace/libc/interpreter/misc.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef __FENNIX_DL_HELPER_H__
|
||||
#define __FENNIX_DL_HELPER_H__
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define ALIGN_UP(x, align) ((__typeof__(x))(((uintptr_t)(x) + ((align) - 1)) & (~((align) - 1))))
|
||||
#define ALIGN_DOWN(x, align) ((__typeof__(x))((x) & (~((align) - 1))))
|
||||
|
||||
#ifndef __FENNIX_DL_ELF_H__
|
||||
#error "Please include elf.h before misc.h"
|
||||
#endif
|
||||
|
||||
void *memset(void *s, int c, size_t n);
|
||||
void *memcpy(void *dest, const void *src, size_t n);
|
||||
size_t strlen(const char *s);
|
||||
char *strcpy(char *dest, const char *src);
|
||||
int strcmp(const char *l, const char *r);
|
||||
char *strcat(char *dest, const char *src);
|
||||
|
||||
unsigned long elf_hash(const unsigned char *name);
|
||||
uint32_t gnu_hash(const char *name);
|
||||
Elf64_Sym *find_symbol(const char *name, uint32_t *hash_table, Elf64_Sym *symtab, const char *strtab);
|
||||
|
||||
void __init_print_buffer();
|
||||
void __fini_print_buffer();
|
||||
int printf(const char *format, ...);
|
||||
|
||||
void *mini_malloc(size_t size);
|
||||
void mini_free(void *ptr);
|
||||
|
||||
#endif // !__FENNIX_DL_HELPER_H__
|
1460
Userspace/libc/interpreter/nanoprintf.h
Normal file
1460
Userspace/libc/interpreter/nanoprintf.h
Normal file
File diff suppressed because it is too large
Load Diff
82
Userspace/libc/interpreter/print.c
Normal file
82
Userspace/libc/interpreter/print.c
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
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 <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <fennix/syscalls.h>
|
||||
|
||||
#include "elf.h"
|
||||
#include "misc.h"
|
||||
#define NANOPRINTF_IMPLEMENTATION 1
|
||||
#include "nanoprintf.h"
|
||||
|
||||
char *print_buffer;
|
||||
size_t print_buffer_size;
|
||||
size_t print_buffer_offset;
|
||||
void flush_buffer()
|
||||
{
|
||||
if (print_buffer_offset > 0)
|
||||
{
|
||||
call_write(1, print_buffer, print_buffer_offset);
|
||||
print_buffer_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void print_wrapper(int c, void *)
|
||||
{
|
||||
if (print_buffer_offset >= print_buffer_size - 1)
|
||||
flush_buffer();
|
||||
print_buffer[print_buffer_offset++] = (char)c;
|
||||
}
|
||||
|
||||
void __init_print_buffer()
|
||||
{
|
||||
print_buffer = (char *)call_mmap(0,
|
||||
0x1000,
|
||||
__SYS_PROT_READ | __SYS_PROT_WRITE,
|
||||
__SYS_MAP_PRIVATE | __SYS_MAP_ANONYMOUS,
|
||||
-1, 0);
|
||||
print_buffer_size = 0x1000;
|
||||
print_buffer_offset = 0;
|
||||
}
|
||||
|
||||
void __fini_print_buffer()
|
||||
{
|
||||
flush_buffer();
|
||||
if (print_buffer != NULL)
|
||||
call_munmap(print_buffer, 0x1000);
|
||||
print_buffer = NULL;
|
||||
}
|
||||
|
||||
int printf(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int ret = npf_vpprintf(print_wrapper, NULL, format, args);
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int puts(const char *s)
|
||||
{
|
||||
int len = strlen(s);
|
||||
memcpy(print_buffer + print_buffer_offset, s, len);
|
||||
print_buffer_offset += len;
|
||||
print_buffer[print_buffer_offset++] = '\0';
|
||||
flush_buffer();
|
||||
return len + 1;
|
||||
}
|
139
Userspace/libc/interpreter/start.c
Normal file
139
Userspace/libc/interpreter/start.c
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
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 <fennix/syscalls.h>
|
||||
|
||||
// const char __interp[] __attribute__((section(".interp"))) = "/boot/fennix.elf";
|
||||
|
||||
#ifndef LIBC_GIT_COMMIT
|
||||
#define LIBC_GIT_COMMIT "0000000000000000000000000000000000000000"
|
||||
#endif
|
||||
|
||||
#define HEX_DIGIT(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : ((c) - 'a' + 10))
|
||||
#define CONVERT_TO_BYTE(h, l) ((HEX_DIGIT(h) << 4) | HEX_DIGIT(l))
|
||||
#define HASH_BYTES(hex) \
|
||||
{CONVERT_TO_BYTE(hex[0], hex[1]), \
|
||||
CONVERT_TO_BYTE(hex[2], hex[3]), \
|
||||
CONVERT_TO_BYTE(hex[4], hex[5]), \
|
||||
CONVERT_TO_BYTE(hex[6], hex[7]), \
|
||||
CONVERT_TO_BYTE(hex[8], hex[9]), \
|
||||
CONVERT_TO_BYTE(hex[10], hex[11]), \
|
||||
CONVERT_TO_BYTE(hex[12], hex[13]), \
|
||||
CONVERT_TO_BYTE(hex[14], hex[15]), \
|
||||
CONVERT_TO_BYTE(hex[16], hex[17]), \
|
||||
CONVERT_TO_BYTE(hex[18], hex[19]), \
|
||||
CONVERT_TO_BYTE(hex[20], hex[21]), \
|
||||
CONVERT_TO_BYTE(hex[22], hex[23]), \
|
||||
CONVERT_TO_BYTE(hex[24], hex[25]), \
|
||||
CONVERT_TO_BYTE(hex[26], hex[27]), \
|
||||
CONVERT_TO_BYTE(hex[28], hex[29]), \
|
||||
CONVERT_TO_BYTE(hex[30], hex[31]), \
|
||||
CONVERT_TO_BYTE(hex[32], hex[33]), \
|
||||
CONVERT_TO_BYTE(hex[34], hex[35]), \
|
||||
CONVERT_TO_BYTE(hex[36], hex[37]), \
|
||||
CONVERT_TO_BYTE(hex[38], hex[39])}
|
||||
|
||||
/* These are declared in GNU ld */
|
||||
enum
|
||||
{
|
||||
NT_FNX_ABI_TAG = 1,
|
||||
NT_FNX_VERSION = 2,
|
||||
NT_FNX_BUILD_ID = 3,
|
||||
NT_FNX_ARCH = 4
|
||||
};
|
||||
|
||||
typedef struct Elf_Nhdr
|
||||
{
|
||||
__UINT32_TYPE__ n_namesz;
|
||||
__UINT32_TYPE__ n_descsz;
|
||||
__UINT32_TYPE__ n_type;
|
||||
char n_name[];
|
||||
} __attribute__((packed)) Elf_Nhdr;
|
||||
|
||||
const struct
|
||||
{
|
||||
Elf_Nhdr header;
|
||||
char name[4];
|
||||
__UINT32_TYPE__ desc[4];
|
||||
} __abi_tag __attribute__((aligned(4), section(".note.ABI-tag"))) = {
|
||||
.header = {
|
||||
.n_namesz = 4, /* "FNX" + '\0' */
|
||||
.n_descsz = sizeof(__UINT32_TYPE__) * 4, /* Description Size */
|
||||
.n_type = NT_FNX_ABI_TAG, /* Type */
|
||||
},
|
||||
.name = "FNX",
|
||||
.desc = {0, 0, 0, 0},
|
||||
};
|
||||
|
||||
const struct
|
||||
{
|
||||
Elf_Nhdr header;
|
||||
char name[4];
|
||||
__UINT8_TYPE__ desc[20];
|
||||
} __build_id __attribute__((aligned(4), section(".note.build-id"))) = {
|
||||
.header = {
|
||||
.n_namesz = 4, /* "FNX" + '\0' */
|
||||
.n_descsz = sizeof(__UINT8_TYPE__) * 20, /* Description Size */
|
||||
.n_type = NT_FNX_BUILD_ID, /* Type */
|
||||
},
|
||||
.name = "FNX",
|
||||
.desc = HASH_BYTES(LIBC_GIT_COMMIT),
|
||||
};
|
||||
|
||||
void __init_print_buffer();
|
||||
void __fini_print_buffer();
|
||||
|
||||
__attribute__((naked, used, no_stack_protector)) void _start()
|
||||
{
|
||||
__asm__(
|
||||
"xorq %rbp, %rbp\n" /* Clear rbp */
|
||||
|
||||
"push %rdi\n"
|
||||
"push %rsi\n"
|
||||
"push %rdx\n"
|
||||
"push %rcx\n"
|
||||
"push %r8\n"
|
||||
"push %r9\n"
|
||||
|
||||
"call __init_print_buffer\n" /* Call __init_print_buffer */
|
||||
"call _dl_preload\n" /* Call _dl_preload */
|
||||
"movl %eax, %edi\n" /* Move return value to edi */
|
||||
"cmp $0, %edi\n" /* Check if return value is 0 */
|
||||
"jne _exit\n" /* If not, jump to _exit */
|
||||
|
||||
"pop %r9\n"
|
||||
"pop %r8\n"
|
||||
"pop %rcx\n"
|
||||
"pop %rdx\n"
|
||||
"pop %rsi\n"
|
||||
"pop %rdi\n"
|
||||
|
||||
"call main\n" /* Call _dl_main */
|
||||
"movl %eax, %edi\n" /* Move return value to edi */
|
||||
"call _exit\n"); /* Call _exit */
|
||||
}
|
||||
|
||||
__attribute__((no_stack_protector)) _Noreturn void _exit(int status)
|
||||
{
|
||||
__fini_print_buffer();
|
||||
call_exit(status);
|
||||
/* At this point, the program *SHOULD* have exited. */
|
||||
__asm__("ud2\n");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
__attribute__((no_stack_protector)) _Noreturn void _Exit(int status) { _exit(status); }
|
Reference in New Issue
Block a user