Thanks to the help in this question, the loader can now map a statically compiled hello world into memory and jump somewhere in that memory region. The problem I'm facing now is I seem not to jump to the right address or I'm calling the function in the wrong way (or wrong function?).
Below is the code to try; I can't find in glibc where the loader calls the entry point of the program to verify I'm doing the right thing. I tried:
- Calling _start(void)
- Calling _start(int, char**, char**)
- Calling __libc_start_main with the last parameters set to NULL (found here)
- Calling directly main
- Calling some addresses in (found with gdb in rdi: 400B4D, r8: 4018E0, rcx: 401840)
The entry point is 0x400a30, some instructions below __libc_start_main. With some of them it SIGSEGV, SIGABRT or prints:
hello, world! haswell xeon_phi ../csu/libc-start.c FATAL: kernel too old
__ehdr_start.e_phentsize == sizeof *GL(dl_phdr) unexpected reloc type in static binary FATAL: cannot determine kernel version
__libc_start_main /dev/full /dev/null cannot set %fs base address for thread-local storage : %s%s%s:%u: %s%sAssertion `%s' failed.
%n Unexpected error.
and some hundred of junk lines.
int main(int argc, char* argv[argc+1]) {
FILE *fp = fopen(argv[1], "r");
if (!fp) {
fprintf(stderr, "cannot open file %s", argv[1]);
return 1;
}
fseek(fp, 0L, SEEK_END);
size_t sz = ftell(fp) + 1;
rewind(fp);
char *region = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
if (region == MAP_FAILED) {
fprintf(stderr, "could not mmap");
return 1;
}
Elf64_Ehdr elf64Ehdr;
memmove(&elf64Ehdr, region, sizeof(elf64Ehdr));
size_t offset;
Elf64_Phdr elf64Phdr;
for (uint16_t i = 0; i != elf64Ehdr.e_phnum; ++i) {
offset = elf64Ehdr.e_phoff + i * elf64Ehdr.e_phentsize;
memmove(&elf64Phdr, region + offset, sizeof(elf64Phdr));
switch (elf64Phdr.p_type) {
case PT_NULL:
break;
case PT_LOAD:
if (load(&elf64Phdr, region + elf64Phdr.p_offset)) {
exit(EXIT_FAILURE);
}
break;
default:
break;
}
}
printf("jumping to: 0x%x\n", elf64Ehdr.e_entry);
char *argv1[] = {"", NULL};
int ret = ((int (*)(int, char **, char **)) elf64Ehdr.e_entry)(1, argv1, argv1);
return ret;
}
int load(const Elf64_Phdr *phdr, const void *elf_bytes_for_phdr) {
const size_t pagesize = getpagesize();
const size_t unaligned_bytes = phdr->p_vaddr % pagesize;
void *base_addr = phdr->p_vaddr - unaligned_bytes;
size_t total_bytes = phdr->p_memsz + unaligned_bytes;
void *region = mmap(base_addr, total_bytes,
phdr->p_flags | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (region != MAP_FAILED) {
memset(region, 0, unaligned_bytes);
return memcpy(region + unaligned_bytes, elf_bytes_for_phdr, phdr->p_filesz) != region + unaligned_bytes &&
! mprotect(region, total_bytes, phdr->p_flags);
}
return 1;
}