Box86源码剖析
Box86
前文回顾
源码阅读
CalcLoadAddr(elf_header)
AllocElfMemory(my_context, elf_header, 1)
LoadElfMemory(f, my_context, elf_header)
CalcStackSize(my_context)
NewX86Emu(...);
SetupInitialStack(emu);
SetEAX(emu, my_context->argc);
pthread_atfork(NULL, NULL, my_child_fork);
AddSymbols();
分支处理
LoadNeededLibs(...)
RelocateElf(...);
atexit(endBox86);
重点主操作 `Run(emu, 0);`
总结
Box86
前文回顾
前面,我们通过阅读box86源代码,已经看到了将执行程序读取到内存中并将关键信息做提取的地方了,如下图:
源码阅读
接下来,继续沿着这个方向进行阅读研究。(速度有些缓慢,中间遇到部分不懂的地方也需要搞清楚的。)
CalcLoadAddr(elf_header)
CalcLoadAddr(elf_header) 这个操作的作用,前面已经研究过了,是提取 ELF 可执行程序的头信息并将主要的数据段根据长度参数申请内存。
int CalcLoadAddr(elfheader_t* head) { head->memsz = 0; head->paddr = head->vaddr = ~(uintptr_t)0; head->align = 1; for (int i=0; i<head->numPHEntries; ++i) if(head->PHEntries[i].p_type == PT_LOAD) { if(head->paddr > (uintptr_t)head->PHEntries[i].p_paddr) head->paddr = (uintptr_t)head->PHEntries[i].p_paddr; if(head->vaddr > (uintptr_t)head->PHEntries[i].p_vaddr) head->vaddr = (uintptr_t)head->PHEntries[i].p_vaddr; } if(head->vaddr==~(uintptr_t)0 || head->paddr==~(uintptr_t)0) { printf_log(LOG_NONE, "Error: v/p Addr for Elf Load not set\n"); return 1; } head->stacksz = 1024*1024; //1M stack size default? head->stackalign = 4; // default align for stack for (int i=0; i<head->numPHEntries; ++i) { if(head->PHEntries[i].p_type == PT_LOAD) { uintptr_t phend = head->PHEntries[i].p_vaddr - head->vaddr + head->PHEntries[i].p_memsz; if(phend > head->memsz) head->memsz = phend; if(head->PHEntries[i].p_align > head->align) head->align = head->PHEntries[i].p_align; } if(head->PHEntries[i].p_type == PT_GNU_STACK) { if(head->stacksz < head->PHEntries[i].p_memsz) head->stacksz = head->PHEntries[i].p_memsz; if(head->stackalign < head->PHEntries[i].p_align) head->stackalign = head->PHEntries[i].p_align; } if(head->PHEntries[i].p_type == PT_TLS) { head->tlssize = head->PHEntries[i].p_memsz; head->tlsalign = head->PHEntries[i].p_align; // force alignement... if(head->tlsalign>1) while(head->tlssize&(head->tlsalign-1)) head->tlssize++; } } printf_log(LOG_DEBUG, "Elf Addr(v/p)=%p/%p Memsize=0x%x (align=0x%x)\n", (void*)head->vaddr, (void*)head->paddr, head->memsz, head->align); printf_log(LOG_DEBUG, "Elf Stack Memsize=%u (align=%u)\n", head->stacksz, head->stackalign); printf_log(LOG_DEBUG, "Elf TLS Memsize=%u (align=%u)\n", head->tlssize, head->tlsalign); return 0; }
AllocElfMemory(my_context, elf_header, 1)
AllocElfMemory(my_context, elf_header, 1) 根据 elf_header 中的信息去申请需要的内存区大小,用于后面存放对于的数据。
int AllocElfMemory(box86context_t* context, elfheader_t* head, int mainbin) { uintptr_t offs = 0; if(mainbin && head->vaddr==0) { char* load_addr = getenv("BOX86_LOAD_ADDR"); if(load_addr) if(sscanf(load_addr, "0x%x", &offs)!=1) offs = 0; } if(!offs) offs = head->vaddr; if(head->vaddr) { head->multiblock_n = 0; // count PHEntrie with LOAD for (int i=0; i<head->numPHEntries; ++i) if(head->PHEntries[i].p_type == PT_LOAD && head->PHEntries[i].p_flags) ++head->multiblock_n; head->multiblock_size = (uint32_t*)calloc(head->multiblock_n, sizeof(uint32_t)); head->multiblock_offs = (uintptr_t*)calloc(head->multiblock_n, sizeof(uintptr_t)); head->multiblock = (void**)calloc(head->multiblock_n, sizeof(void*)); // and now, create all individual blocks head->memory = (char*)0xffffffff; int n = 0; for (int i=0; i<head->numPHEntries; ++i) if(head->PHEntries[i].p_type == PT_LOAD && head->PHEntries[i].p_flags) { Elf32_Phdr * e = &head->PHEntries[i]; uintptr_t bstart = e->p_vaddr; uint32_t bsize = e->p_memsz; uintptr_t balign = e->p_align; if (balign) balign = balign-1; else balign = 1; if(balign<4095) balign = 4095; uintptr_t bend = (bstart + bsize + balign)&(~balign); bstart &= ~balign; int ok = 0; for (int j=0; !ok && j<n; ++j) { uintptr_t start = head->multiblock_offs[j]; uintptr_t end = head->multiblock_offs[j] + head->multiblock_size[j]; start &= ~balign; if(((bstart>=start) && (bstart<=end)) || ((bend>=start) && (bend<=end)) || ((bstart<start) && (bend>end))) { // merge ok = 1; if(bstart<start) head->multiblock_offs[j] = bstart; head->multiblock_size[j] = ((bend>end)?bend:end) - head->multiblock_offs[j]; --head->multiblock_n; } } if(!ok) { head->multiblock_offs[n] = bstart; head->multiblock_size[n] = bend - head->multiblock_offs[n]; ++n; } } for (int i=0; i<head->multiblock_n; ++i) { printf_log(LOG_DEBUG, "Allocating 0x%x memory @%p for Elf \"%s\"\n", head->multiblock_size[i], (void*)head->multiblock_offs[i], head->name); void* p = mmap((void*)head->multiblock_offs[i], head->multiblock_size[i] , PROT_READ | PROT_WRITE | PROT_EXEC , MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED , -1, 0); if(p==MAP_FAILED) { printf_log(LOG_NONE, "Cannot create memory map (@%p 0x%x/0x%x) for elf \"%s\"\n", (void*)head->multiblock_offs[i], head->multiblock_size[i], head->align, head->name); return 1; } head->multiblock[i] = p; if(p<(void*)head->memory) head->memory = (char*)p; } } else { // vaddr is 0, load everything has a One block printf_log(LOG_DEBUG, "Allocating 0x%x memory @%p for Elf \"%s\"\n", head->memsz, (void*)offs, head->name); void* p = mmap((void*)offs, head->memsz , PROT_READ | PROT_WRITE | PROT_EXEC , MAP_PRIVATE | MAP_ANONYMOUS | (((offs)?MAP_FIXED:0)) , -1, 0); if(p==MAP_FAILED) { printf_log(LOG_NONE, "Cannot create memory map (@%p 0x%x/0x%x) for elf \"%s\"\n", (void*)offs, head->memsz, head->align, head->name); return 1; } head->memory = p; memset(p, 0, head->memsz); head->delta = (intptr_t)p - (intptr_t)head->vaddr; printf_log(LOG_DEBUG, "Got %p (delta=%p)\n", p, (void*)head->delta); head->multiblock_n = 1; head->multiblock_size = (uint32_t*)calloc(head->multiblock_n, sizeof(uint32_t)); head->multiblock_offs = (uintptr_t*)calloc(head->multiblock_n, sizeof(uintptr_t)); head->multiblock = (void**)calloc(head->multiblock_n, sizeof(void*)); head->multiblock_size[0] = head->memsz; head->multiblock_offs[0] = (uintptr_t)p; head->multiblock[0] = p; } head->tlsbase = AddTLSPartition(context, head->tlssize); return 0; }
LoadElfMemory(f, my_context, elf_header)
LoadElfMemory(f, my_context, elf_header) 将可执行文件中的二进制数据按照头信息的参数读取到 my_context的对应位置去。方便后面处理时去读取和调用。
int LoadElfMemory(FILE* f, box86context_t* context, elfheader_t* head) { for (int i=0; i<head->numPHEntries; ++i) { if(head->PHEntries[i].p_type == PT_LOAD) { Elf32_Phdr * e = &head->PHEntries[i]; char* dest = (char*)e->p_paddr + head->delta; printf_log(LOG_DEBUG, "Loading block #%i @%p (0x%x/0x%x)\n", i, dest, e->p_filesz, e->p_memsz); fseeko64(f, e->p_offset, SEEK_SET); if(e->p_filesz) { if(fread(dest, e->p_filesz, 1, f)!=1) { printf_log(LOG_NONE, "Fail to read PT_LOAD part #%d (size=%d)\n", i, e->p_filesz); return 1; } #ifdef DYNAREC if(e->p_flags & PF_X) { dynarec_log(LOG_DEBUG, "Add ELF eXecutable Memory %p:%p\n", dest, (void*)e->p_memsz); addDBFromAddressRange((uintptr_t)dest, e->p_memsz, 0); } #endif } // zero'd difference between filesz and memsz /*if(e->p_filesz != e->p_memsz) memset(dest+e->p_filesz, 0, e->p_memsz - e->p_filesz);*/ //block is already 0'd at creation } if(head->PHEntries[i].p_type == PT_TLS) { Elf32_Phdr * e = &head->PHEntries[i]; char* dest = (char*)(context->tlsdata+context->tlssize+head->tlsbase); printf_log(LOG_DEBUG, "Loading TLS block #%i @%p (0x%x/0x%x)\n", i, dest, e->p_filesz, e->p_memsz); if(e->p_filesz) { fseeko64(f, e->p_offset, SEEK_SET); if(fread(dest, e->p_filesz, 1, f)!=1) { printf_log(LOG_NONE, "Fail to read PT_TLS part #%d (size=%d)\n", i, e->p_filesz); return 1; } } // zero'd difference between filesz and memsz if(e->p_filesz != e->p_memsz) memset(dest+e->p_filesz, 0, e->p_memsz - e->p_filesz); } } return 0; }
CalcStackSize(my_context)
CalcStackSize(box86context_t *context) 初始化 my_context 中的栈空间。将 context->elfs[i]中的栈空间都设置成 8G 大小,并且4字节对齐。
int CalcStackSize(box86context_t *context) { printf_log(LOG_DEBUG, "Calc stack size, based on %d elf(s)\n", context->elfsize); context->stacksz = 8*1024*1024; context->stackalign=4; for (int i=0; i<context->elfsize; ++i) CalcStack(context->elfs[i], &context->stacksz, &context->stackalign); if (posix_memalign((void**)&context->stack, context->stackalign, context->stacksz)) { printf_log(LOG_NONE, "Cannot allocate aligned memory (0x%x/0x%x) for stack\n", context->stacksz, context->stackalign); return 1; } memset(context->stack, 0, context->stacksz); printf_log(LOG_DEBUG, "Stack is @%p size=0x%x align=0x%x\n", context->stack, context->stacksz, context->stackalign); return 0; }
NewX86Emu(…);
NewX86Emu 这就是最重要的一步。这里会初始化一个X86模拟器。
x86emu_t *NewX86Emu(box86context_t *context, uintptr_t start, uintptr_t stack, int stacksize, int ownstack) { printf_log(LOG_DEBUG, "Allocate a new X86 Emu, with EIP=%p and Stack=%p/0x%X\n", (void*)start, (void*)stack, stacksize); x86emu_t *emu = (x86emu_t*)calloc(1, sizeof(x86emu_t)); internalX86Setup(emu, context, start, stack, stacksize, ownstack); return emu; }
SetupInitialStack(emu);
将环境参数及部分初始化信息放入前面新创建的 X86模拟器对象栈空间中。
void SetupInitialStack(x86emu_t *emu) { // start with 0 Push(emu, 0); // push program executed PushString(emu, emu->context->argv[0]); uintptr_t p_arg0 = R_ESP; // push envs uintptr_t p_envv[emu->context->envc]; for (int i=emu->context->envc-1; i>=0; --i) { PushString(emu, emu->context->envv[i]); p_envv[i] = R_ESP; } // push args, also, free the argv[] string and point to the one in the main stack uintptr_t p_argv[emu->context->argc]; for (int i=emu->context->argc-1; i>=0; --i) { PushString(emu, emu->context->argv[i]); p_argv[i] = R_ESP; free(emu->context->argv[i]); emu->context->argv[i] = (char*)p_argv[i]; } // align uintptr_t tmp = (R_ESP)&~(emu->context->stackalign-1); memset((void*)tmp, 0, R_ESP-tmp); R_ESP=tmp; // push some AuxVector stuffs PushString(emu, "i686"); uintptr_t p_386 = R_ESP; uintptr_t p_random = real_getauxval(25); if(!p_random) { for (int i=0; i<4; ++i) Push(emu, random()); p_random = R_ESP; } // align tmp = (R_ESP)&~(emu->context->stackalign-1); memset((void*)tmp, 0, R_ESP-tmp); R_ESP=tmp; // push the AuxVector themselves Push(emu, 0); Push(emu, 0); //AT_NULL(0)=0 Push(emu, p_386); Push(emu, 15); //AT_PLATFORM(15)=p_386* Push(emu, 0); Push(emu, 66); //AT_HWCAP2(26)=0 // Push HWCAP: // FPU: 1<<0 ; VME: 1<<1 ; DE : 1<<2 ; PSE: 1<<3 ; TSC: 1<<4 // MSR: 1<<5 : PAE: 1<<6 : MCE: 1<<7 ; CX8: 1<<8 : APIC:1<<9 // SEP: 1<<11: MTRR:1<<12: PGE: 1<<13: MCA: 1<<14; CMOV:1<<15; FCMOV: 1<<16 // MMX: 1<<23:OSFXR:1<<24: XMM: 1<<25:XMM2: 1<<26;AMD3D:1<<31 Push(emu, (1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) | (1<<8) | (1<<15) | (1<<16) | (1<<23) | (1<<25) | (1<<26)); Push(emu, 16); //AT_HWCAP(16)=... Push(emu, p_arg0); Push(emu, 31); //AT_EXECFN(31)=p_arg0 Push(emu, p_random); Push(emu, 25); //AT_RANDOM(25)=p_random Push(emu, real_getauxval(23)); Push(emu, 23); //AT_SECURE(23)=0 Push(emu, real_getauxval(14)); Push(emu, 14); //AT_EGID(14) Push(emu, real_getauxval(13)); Push(emu, 13); //AT_GID(13) Push(emu, real_getauxval(12)); Push(emu, 12); //AT_EUID(12) Push(emu, real_getauxval(11)); Push(emu, 11); //AT_UID(11) Push(emu, R_EIP); Push(emu, 9); //AT_ENTRY(9)=entrypoint Push(emu, 0/*emu->context->vsyscall*/); Push(emu, 32); //AT_SYSINFO(32)=vsyscall if(!emu->context->auxval_start) // store auxval start if needed emu->context->auxval_start = (uintptr_t*)R_ESP; // TODO: continue // push nil / envs / nil / args / argc Push(emu, 0); for (int i=emu->context->envc-1; i>=0; --i) Push(emu, p_envv[i]); Push(emu, 0); for (int i=emu->context->argc-1; i>=0; --i) Push(emu, p_argv[i]); Push(emu, emu->context->argc); }
SetEAX(emu, my_context->argc);
这里的 SetEAX(emu, my_context->argc); 和 SetEBX(emu, (uint32_t)my_context->argv); 的作用类似于我们启动运行 main 函数传递参数,将执行参数事前放到对于的寄存器中。
SetupInitialStack(emu); // starting here, the argv[] don't need free anymore SetupX86Emu(emu); SetEAX(emu, my_context->argc); SetEBX(emu, (uint32_t)my_context->argv);
pthread_atfork(NULL, NULL, my_child_fork);
创建一个子线程,实际上在源代码中这一条指令其实是作为注释存在的,没有作用,真正有用的是下面一行:
thread_set_emu(emu); static pthread_key_t thread_key; static pthread_once_t thread_key_once = PTHREAD_ONCE_INIT; static void thread_key_alloc() { //创建子线程内部私有的全局变量:thread_key ,线程结束的时候会自动调用第二个参数对应的函数去释放全局变量对象。 pthread_key_create(&thread_key, emuthread_destroy); } void thread_set_emu(x86emu_t* emu) { // create the key pthread_once(&thread_key_once, thread_key_alloc); //当前程序仅执行一次的县城创建操作。 emuthread_t *et = (emuthread_t*)pthread_getspecific(thread_key); //获得线程的私有空间 if(!et) { et = (emuthread_t*)calloc(1, sizeof(emuthread_t)); } else { if(et->emu != emu) FreeX86Emu(&et->emu); } et->emu = emu; et->emu->type = EMUTYPE_MAIN; pthread_setspecific(thread_key, et); } void setupTraceInit(box86context_t* context) //如果需要追踪其中的log,需要设置 BOX86_TRACE_INIT 环境变量 { #ifdef HAVE_TRACE char* p = getenv("BOX86_TRACE_INIT"); if(p) { setbuf(stdout, NULL); uintptr_t trace_start=0, trace_end=0; if (strcmp(p, "1")==0) SetTraceEmu(0, 0); else if (strchr(p,'-')) { if(sscanf(p, "%d-%d", &trace_start, &trace_end)!=2) { if(sscanf(p, "0x%X-0x%X", &trace_start, &trace_end)!=2) sscanf(p, "%x-%x", &trace_start, &trace_end); } if(trace_start) SetTraceEmu(trace_start, trace_end); } else { if (GetSymbolStartEnd(GetMapSymbol(my_context->maplib), p, &trace_start, &trace_end)) { SetTraceEmu(trace_start, trace_end); printf_log(LOG_INFO, "TRACE on %s only (%p-%p)\n", p, (void*)trace_start, (void*)trace_end); } else { printf_log(LOG_NONE, "Warning, symbol to Traced (\"%s\") not found, disabling trace\n", p); SetTraceEmu(0, 100); // disabling trace, mostly } } } else { p = getenv("BOX86_TRACE"); if(p) if (strcmp(p, "0")) SetTraceEmu(0, 1); } #endif }
AddSymbols();
这里将 elf_header 对应的链接库提取存储下来。
void AddSymbols(lib_t *maplib, kh_mapsymbols_t* mapsymbols, kh_mapsymbols_t* weaksymbols, kh_mapsymbols_t* localsymbols, elfheader_t* h) { printf_log(LOG_DUMP, "Will look for Symbol to add in SymTable(%d)\n", h->numSymTab); for (int i=0; i<h->numSymTab; ++i) { const char * symname = h->StrTab+h->SymTab[i].st_name; int bind = ELF32_ST_BIND(h->SymTab[i].st_info); int type = ELF32_ST_TYPE(h->SymTab[i].st_info); int vis = h->SymTab[i].st_other&0x3; if((type==STT_OBJECT || type==STT_FUNC || type==STT_COMMON || type==STT_TLS || type==STT_NOTYPE) && (vis==STV_DEFAULT || vis==STV_PROTECTED) && (h->SymTab[i].st_shndx!=0)) { if(bind==10/*STB_GNU_UNIQUE*/ && FindGlobalSymbol(maplib, symname)) continue; uintptr_t offs = (type==STT_TLS)?h->SymTab[i].st_value:(h->SymTab[i].st_value + h->delta); uint32_t sz = h->SymTab[i].st_size; printf_log(LOG_DUMP, "Adding Symbol(bind=%s) \"%s\" with offset=%p sz=%d\n", (bind==STB_LOCAL)?"LOCAL":((bind==STB_WEAK)?"WEAK":"GLOBAL"), symname, (void*)offs, sz); if(bind==STB_LOCAL) AddSymbol(localsymbols, symname, offs, sz); else // add in local and global map if(bind==STB_WEAK) { AddSymbol(weaksymbols, symname, offs, sz); } else { AddSymbol(mapsymbols, symname, offs, sz); } } } printf_log(LOG_DUMP, "Will look for Symbol to add in DynSym (%d)\n", h->numDynSym); for (int i=0; i<h->numDynSym; ++i) { const char * symname = h->DynStr+h->DynSym[i].st_name; int bind = ELF32_ST_BIND(h->DynSym[i].st_info); int type = ELF32_ST_TYPE(h->DynSym[i].st_info); int vis = h->DynSym[i].st_other&0x3; //st_shndx==65521 means ABS value if((type==STT_OBJECT || type==STT_FUNC || type==STT_COMMON || type==STT_TLS || type==STT_NOTYPE) && (vis==STV_DEFAULT || vis==STV_PROTECTED) && (h->DynSym[i].st_shndx!=0 && h->DynSym[i].st_shndx<=65521)) { if(bind==10/*STB_GNU_UNIQUE*/ && FindGlobalSymbol(maplib, symname)) continue; uintptr_t offs = (type==STT_TLS)?h->DynSym[i].st_value:(h->DynSym[i].st_value + h->delta); uint32_t sz = h->DynSym[i].st_size; printf_log(LOG_DUMP, "Adding Symbol(bind=%s) \"%s\" with offset=%p sz=%d\n", (bind==STB_LOCAL)?"LOCAL":((bind==STB_WEAK)?"WEAK":"GLOBAL"), symname, (void*)offs, sz); if(bind==STB_LOCAL) AddSymbol(localsymbols, symname, offs, sz); else // add in local and global map if(bind==STB_WEAK) { AddSymbol(weaksymbols, symname, offs, sz); } else { AddSymbol(mapsymbols, symname, offs, sz); } } } }
分支处理
这里如果使用wine,就需要将对应的wine环境导入(如果不存在设置的wine环境参数则使用默认的)。
ld_preload.size 如果有值,则说明有链接的动态库,这里就需要将对应的动态库找到并导入到my_context和 emu对象中。
if(wine_preloaded) { uintptr_t wineinfo = FindSymbol(GetMapSymbol(my_context->maplib), "wine_main_preload_info"); if(!wineinfo) wineinfo = FindSymbol(GetWeakSymbol(my_context->maplib), "wine_main_preload_info"); if(!wineinfo) wineinfo = FindSymbol(GetLocalSymbol(my_context->maplib), "wine_main_preload_info"); if(!wineinfo) {printf_log(LOG_NONE, "Warning, Symbol wine_main_preload_info not found\n");} else { *(void**)wineinfo = get_wine_prereserve(); printf_log(LOG_DEBUG, "WINE wine_main_preload_info found and updated\n"); } } // pre-load lib if needed if(ld_preload.size) { for (int i=0; i<ld_preload.size; ++i) { if(AddNeededLib(NULL, NULL, 0, ld_preload.paths[i], my_context, emu)) { printf_log(LOG_INFO, "Warning, cannot pre-load lib: \"%s\"\n", ld_preload.paths[i]); } } }
LoadNeededLibs(…)
LoadNeededLibs(...) 导入需要的库内容,前面的操作刚刚完成将需要的库信息导入到 my_context 中,这里就找到对应的文件读取导入。
if(LoadNeededLibs(elf_header, my_context->maplib, &my_context->neededlibs, 0, my_context, emu)) { printf_log(LOG_NONE, "Error: loading needed libs in elf %s\n", my_context->argv[0]); FreeBox86Context(&my_context); return -1; } int LoadNeededLibs(elfheader_t* h, lib_t *maplib, needed_libs_t* neededlibs, int local, box86context_t *box86, x86emu_t* emu) { DumpDynamicRPath(h); // update RPATH first for (int i=0; i<h->numDynamic; ++i) if(h->Dynamic[i].d_tag==DT_RPATH || h->Dynamic[i].d_tag==DT_RUNPATH) { char *rpathref = h->DynStrTab+h->delta+h->Dynamic[i].d_un.d_val; char* rpath = rpathref; while(strstr(rpath, "$ORIGIN")) { char* origin = strdup(h->path); char* p = strrchr(origin, '/'); if(p) *p = '\0'; // remove file name to have only full path, without last '/' char* tmp = (char*)calloc(1, strlen(rpath)-strlen("$ORIGIN")+strlen(origin)+1); p = strstr(rpath, "$ORIGIN"); memcpy(tmp, rpath, p-rpath); strcat(tmp, origin); strcat(tmp, p+strlen("$ORIGIN")); if(rpath!=rpathref) free(rpath); rpath = tmp; } if(strchr(rpath, '$')) { printf_log(LOG_INFO, "BOX86: Warning, RPATH with $ variable not supported yet (%s)\n", rpath); } else { printf_log(LOG_DEBUG, "Prepending path \"%s\" to BOX86_LD_LIBRARY_PATH\n", rpath); PrependPath(rpath, &box86->box86_ld_lib, 1); } if(rpath!=rpathref) free(rpath); } if(!h->neededlibs && neededlibs) h->neededlibs = neededlibs; DumpDynamicNeeded(h); for (int i=0; i<h->numDynamic; ++i) if(h->Dynamic[i].d_tag==DT_NEEDED) { char *needed = h->DynStrTab+h->delta+h->Dynamic[i].d_un.d_val; // TODO: Add LD_LIBRARY_PATH and RPATH Handling if(AddNeededLib(maplib, neededlibs, local, needed, box86, emu)) { printf_log(LOG_INFO, "Error loading needed lib: \"%s\"\n", needed); if(!allow_missing_libs) return 1; //error... } } return 0; }
RelocateElf(…);
RelocateElf(...)
int RelocateElf(lib_t *maplib, lib_t *local_maplib, elfheader_t* head) { if(head->rel) { int cnt = head->relsz / head->relent; DumpRelTable(head, cnt, (Elf32_Rel *)(head->rel + head->delta), "Rel"); printf_log(LOG_DEBUG, "Applying %d Relocation(s) for %s\n", cnt, head->name); if(RelocateElfREL(maplib, local_maplib, head, cnt, (Elf32_Rel *)(head->rel + head->delta))) return -1; } if(head->rela) { int cnt = head->relasz / head->relaent; DumpRelATable(head, cnt, (Elf32_Rela *)(head->rela + head->delta), "RelA"); printf_log(LOG_DEBUG, "Applying %d Relocation(s) with Addend for %s\n", cnt, head->name); if(RelocateElfRELA(maplib, local_maplib, head, cnt, (Elf32_Rela *)(head->rela + head->delta))) return -1; } return 0; }
atexit(endBox86);
使用 atexit()注册在main退出后执行的函数 endBox86
void endBox86() { if(!my_context) return; x86emu_t* emu = thread_get_emu(); //atexit first printf_log(LOG_DEBUG, "Calling atexit registered functions\n"); CallAllCleanup(emu); // than call all the Fini (some "smart" ordering of the fini may be needed, but for now, callign in this order should be good enough) printf_log(LOG_DEBUG, "Calling fini for all loaded elfs\n"); for (int i=0; i<my_context->elfsize; ++i) RunElfFini(my_context->elfs[i], emu); // all done, free context FreeBox86Context(&my_context); if(libGL) { free(libGL); libGL = NULL; } }
重点主操作 Run(emu, 0);
接下来就到了 box86 最最重要的主操作部分,入口是 Run(emu, 0); ,前面完成了一系列的环境参数获取、执行程序解析等等步骤,都是为了在这个函数操作内真正让X86可执行程序能够运行起来。这里由于代码量十分庞大,就不在这里粘贴全部代码了,这个函数会占用box86源代码中的一个文件内容,src/emu/x86run.c,大家感兴趣可以去这里细细阅读。
Run()的主要操作其实就是模拟 X86可执行程序的机器码解码过程,然后找到对应的操作用代码来模拟实现,这样在真正主机上编译完成后就会去真正执行当前环境下的对应操作了。(个人理解,如果有不同意见欢迎讨论)
总结
对于 BOX86 源码阅读,经过了各种系统环境的获取、参数识别、ELF文件解析、头文件数据获取、链接库内容导入等等一系列前期操作后,最终在 main() 入口函数中找到了真正去执行可执行程序翻译到运行的代码入口 Run()。并且初步判断是利用事先编写好的X86机器码操作流程进行识别从而将X86执行程序的指令翻译成当前主机环境下的指令去执行。(如果是这样的话,那么对于动态指令翻译来说,也就是模拟了一个X86指令执行的过程,而真正去执行的地方是调用当前主机的指令去执行。)