借助this问题的帮助,加载程序现在可以将静态编译的hello世界映射到内存中,并跳转到该内存区域中的某个位置。我现在面临的问题是我似乎没有跳到正确的地址,或者我以错误的方式(或错误的功能?)调用了函数。
下面是要尝试的代码;我在glibc中找不到加载器在哪里调用程序的入口点以验证我在做正确的事情。我尝试过:
入口点是0x400a30,__ libc_start_main下面有一些说明。其中有SIGSEGV,SIGABRT或印刷品:
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.
和几百条垃圾线。
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;
}
答案 0 :(得分:3)
我现在面临的问题是我似乎没有跳转到正确的地址,或者我以错误的方式调用了函数
您的问题不是以上两个问题(尽管“打错方式”不太远)。
对于静态链接的可执行文件,Elf64_Ehdr.e_entry
是要调用的正确地址(它指向_start
),而_start
不带任何参数。
问题在于_start
的工作是
argc
,argv
和envp
,最后main(argc, argv, envp)
。然后的问题是:_start
如何完成第2步?
答案:有一个协议,Linux内核实现和_start
使用来完成步骤2。
尤其是,内核将argv[0]
,argv[1]
,... envp[0]
,envp[1]
等的实际(字符串)值复制到堆栈中,然后指向这些字符串的指针。还有一种叫做辅助向量的东西。
_start
期望在堆栈上查找所有这些信息,并且在找不到该信息时会表现异常。我相信这是您当前问题的根本原因。