PA2程序的执行(第二课)——PA2-2程序的装载PA2-3调试器符号表解析2017年10月20日计算机系统基础ProgrammingAssignment前情提要PA1ALU+FPU基本运算功能PA2-1模拟指令执行2017/10/20ProgrammingAssignment2-22根据EIP取指令(操作码)取源操作数执行数据操作mov,add,sub,…写目的操作数CPU更新EIP先把指令依次在内存中排好,给EIP赋一个初始值,指向第一条指令,CPU就可以循环执行每一条指令了instr_fetch()opcode_entry[xxx]operand_read()instr_execute_xop()operand_write()cpu.
eip+=len前情提要在实现的过程中出现了许多稀奇古怪的bug基本的心理发展过程第一阶段:不可能是我的错!
一定是框架代码、编译器、操作系统、虚拟机、CPU……里有bug!
第二阶段:嗯……似乎这里有一点小问题,但是不至于吧~第三阶段:当初这代码怎么能跑起来的!
!
!
!
Debugging是码农们一生都要面对的问题基本过程重现错误(成功一半):再跑一次、构造新的有针对性的测试用例……分离和定位root-cause:单步执行、断点……查看和分析:assert、printf……总结:不容易犯错的编码方式、构造对测试友好的代码……踩遍所有的坑,成就伟大程序员2017/10/20ProgrammingAssignment2-23补充说明关于git记录过大的问题参见课程群中《关于文件过大》(sandhill.
pdf)一文的说明2017/10/20ProgrammingAssignment2-24PA2-2程序的装载&PA2-3调试器ELF文件的装载符号表的解析PA2-2程序的装载&PA2-3调试器ELF文件的装载符号表的解析原先的NEMU是怎么装载程序的2017/10/20ProgrammingAssignment2-27CPU内存例如:testcase/bin/addNEMUeip磁盘上存着的测试用例binarycopy1.
把磁盘上存着的测试用例binary原封不动拷贝到NEMU的模拟内存里(从0x30000开始的地方)2.
把CPU的eip初始化为0x300003.
模拟CPU通过执行exec()开始执行测试用例的binary原先的NEMU是怎么装载程序的具体过程testcase/Makefile2017/10/20ProgrammingAssignment2-28#一堆变量赋值(:=与=的区别自行上网搜索)xxx:=yyy#一堆目标和规则目标:依赖规则一个Makefile一般长这个样子(在依赖被满足的前提下,通过怎样的规则来实现目标)可能是另外一组规则所要达成的目标(比如可执行文件add作为目标,add.
o就是其依赖,而add.
o又作为其它规则的目标,从add.
c产生)可能是个文件(add),也可能是个虚的目标(clean);目标文件的依赖如果没有发生改变,那么不需要动用规则去产生新的目标文件在命令行键入make之后1.
搜索当前文件夹下的Makefile(还有另外三种可能的文件命名方式,自行搜索)2.
如果没有在make后面跟目标名称,则默认第一个目标为最终目标,否则以目标名称对应的目标为最终目标(makeclean)2017/10/20ProgrammingAssignment2-29#一堆变量赋值(:=与=的区别自行上网搜索)CC:=gccLD:=ldCFLAGS:=……LDFLAGS:=-melf_i386-estart-Ttext=0x30000#makerun|maketestSFILES:=$(shellfindsrc/-name"*.
S")CFILES:=$(shellfindsrc/-name"*.
c")SOBJS:=$(SFILES:.
S=.
o)COBJS:=$(CFILES:.
c=.
o)SBINS:=$(SFILES:.
S=)CBINS:=$(CFILES:.
c=)#一堆目标和规则testcase:start.
o$(SOBJS)$(COBJS)$(SBINS)$(CBINS)%.
o:%.
c$(CC)$(CFLAGS)-c-o$@$:30000:e920000000jmp3002500030005:30005:55push%ebp30006:89e5mov%esp,%ebp30008:83ec10sub$0x10,%esp3000b:e8b8000000call300c830010:05f01f0000add$0x1ff0,%eax30015:8b5508mov0x8(%ebp),%edx30018:8b450cmov0xc(%ebp),%eax3001b:01d0add%edx,%eax3001d:8945fcmov%eax,-0x4(%ebp)30020:8b45fcmov-0x4(%ebp),%eax30023:c9leave30024:c3ret…$objdump–dadd000000020e900005500e589ec83e81000b800000000010f005001f8b000855458b010c89d0fc450000020458bc9fc55c3e589835310ec9be8000000000308100cfc3001fc700f0450000000045c7000004000f80000eb00c751f445000000003deb0000050458b8bf4839400200000458b8bf8838400000600020000050529ae8ffff83ff08c4c189…$hexdumpadd.
img字节按小端序,所以add.
img里面从0x0位置开始的内容对照可执行文件add是什么原先的NEMU是怎么装载程序的具体过程转到NEMU,在执行makerun和maketest时复习上节课讲到的NEMU运行测试用例的过程那个时候举的例子是mov,和add以及其它任何一个testcase没有什么不同2017/10/20ProgrammingAssignment2-211NEMU运行测试用例从makerun说起2017年10月20日星期五ProgrammingAssignment12nemu:$(callgit_commit,"nemu")cdnemu&&makerun:nemudo_not_call_me_testcase$(callgit_commit,"run").
/nemu/nemu-runmovMakefile目标前提依赖(编译nemu和testcase)1.
打上git记录2.
执行.
/nemu/nemu-runmov其中mov是测试用例的名称,对应testcase/src/里面测试用例源文件的文件名NEMU运行测试用例从makerun说起执行.
/nemu/nemu-runmov2017年10月20日星期五ProgrammingAssignment13intmain(intargc,char*argv[]){if(argc==1){reg_test();alu_test();fpu_test();return0;}/*Readthearguments*/if(argc==3){if(strcmp(argv[1],"-run")){printf("Error:%s%s%s\n",argv[0],argv[1],argv[2]);printf("Usage:nemu-run\n");return0;}strcpy(image_path,".
/testcase/bin/");strcat(image_path,argv[2]);strcat(image_path,".
img");strcpy(elf_path,".
/testcase/bin/");strcat(elf_path,argv[2]);single_run();}…}.
img内存镜像路径elf文件路径(现在不管)nemu/src/main.
cNEMU运行测试用例从makerun说起执行.
/nemu/nemu-runmov2017年10月20日星期五ProgrammingAssignment14staticvoidsingle_run(){…restart(INIT_EIP);load_image(image_path,LOAD_OFF);…load_image(elf_path,0);…ui_mainloop(autorun);}nemu/src/main.
cINIT_EIP=LOAD_OFF=0x30000NEMU运行测试用例从makerun说起执行.
/nemu/nemu-runmov2017年10月20日星期五ProgrammingAssignment15InitialEIP指向第一条指令|PhysicalAddressv0x00x30000EIP初始值与load_image位置保持一致0x128000000|TestcaseELFFile|~|TestcaseBinary|~||load_image(){…fread(get_mem_addr()+load_off,file_size,1,fp);…}得到模拟内存的首地址.
img文件的load_off=0x30000ELF可执行目标文件的load_off=0x0ELF可执行目标文件采用.
img镜像文件加载可执行binary的方式看似完美无缺,但有时候却有一些问题好的方面装载过程简单直接,对裸机很友好(现代机器刚开机时也要通过这种方式加载一些最基本的引导程序)不好的方面存储效率问题除了必要的代码和数据,其它的一些运行时内存的内容,如栈、未初始化的全局变量,是不是要在镜像中保留相应的位置这个好解决,不保存就行了链接困难最终的可执行目标文件很可能是由多个目标文件拼接(链接)而成的每个.
c文件都会通过gcc产生一个.
o文件(看看nemu在make的时候产生了多少个.
o文件)最后通过ld把它们链接到一起得到最终的可执行目标文件(所有.
o文件合到一起才得到了nemu这个可执行文件)不同的.
o文件可能会共享一些全局变量和函数(instr/mov.
c中的函数在cpu.
c中被调用,cpu.
c中的cpu全局变量被好多.
c所使用)如果每个.
o文件都是以.
img镜像的方式来存储,拼接的时候困难重重,因此需要一个好的格式来描述目标文件2017/10/20ProgrammingAssignment2-216课本pg.
167ELF可执行目标文件一个好的目标文件应当包括的内容面向执行执行环境的要求:什么操作系统、什么架构……执行的内容:代码、数据的内容及其在内存中的位置面向链接函数名和全局变量所对应的符号可重定位信息拼接后有些跳转地址是不是要修改、全局变量的地址是不是要修改用objdump观察nemu在make后产生的各个.
o文件,似乎都是从0x0地址开始的,显然在链接时要把不同的.
o文件的内容重新定位再拼接到一起以及相关的一些调试信息采用相对统一的格式存储.
o、可执行目标文件、以后要被其它项目使用的库文件在GNU/Linux系统中,采用可执行可链接格式(ExecutableandLinkableFormat,ELF)来存储目标文件2017/10/20ProgrammingAssignment2-217ELF可执行目标文件ELF文件的结构和主要功能2017/10/20ProgrammingAssignment2-218ELF头(ELFHeader)程序头表(ProgramHeaders).
init节.
text节(程序代码).
rodata节(只读数据).
data节(已初始化全局变量数据).
bss节(未初始化全局变量数据,其实啥也没存).
symtab节(符号表).
xxx节.
yyy节.
strtab节(字符串表)节头表(SectionHeaders)索引索引内存摆这里,放那里程序头表说.
.
.
引用ELF可执行目标文件百闻不如一见以testcase/bin/add为例:readelf–aadd|less出来好多内容,一块块地来看2017/10/20ProgrammingAssignment2-219ELFHeader:Magic:7f454c46010101000000000000000000Class:ELF32Data:2'scomplement,littleendianVersion:1(current)OS/ABI:UNIX-SystemVABIVersion:0Type:EXEC(Executablefile)Machine:Intel80386Version:0x1Entrypointaddress:0x30000Startofprogramheaders:52(bytesintofile)Startofsectionheaders:18276(bytesintofile)Flags:0x0Sizeofthisheader:52(bytes)Sizeofprogramheaders:32(bytes)Numberofprogramheaders:3Sizeofsectionheaders:40(bytes)Numberofsectionheaders:15Sectionheaderstringtableindex:14ELF头,它位于整个ELF文件最开始的地方.
在32位Linux系统中,头文件中的Elf32_Ehdr数据结构与之对应.
#defineEI_NIDENT16typedefstruct{unsignedchare_ident[EI_NIDENT];uint16_te_type;uint16_te_machine;uint32_te_version;ElfN_Addre_entry;ElfN_Offe_phoff;ElfN_Offe_shoff;uint32_te_flags;uint16_te_ehsize;uint16_te_phentsize;uint16_te_phnum;uint16_te_shentsize;uint16_te_shnum;uint16_te_shstrndx;}ElfN_Ehdr;ELF可执行目标文件百闻不如一见以testcase/bin/add为例:readelf–aadd|less出来好多内容,一块块地来看2017/10/20ProgrammingAssignment2-220ELFHeader:Magic:7f454c46010101000000000000000000Class:ELF32Data:2'scomplement,littleendianVersion:1(current)OS/ABI:UNIX-SystemVABIVersion:0Type:EXEC(Executablefile)Machine:Intel80386Version:0x1Entrypointaddress:0x30000Startofprogramheaders:52(bytesintofile)Startofsectionheaders:18276(bytesintofile)Flags:0x0Sizeofthisheader:52(bytes)Sizeofprogramheaders:32(bytes)Numberofprogramheaders:3Sizeofsectionheaders:40(bytes)Numberofsectionheaders:15Sectionheaderstringtableindex:14ELF头,它位于整个ELF文件最开始的地方.
在32位Linux系统中,头文件中的Elf32_Ehdr数据结构与之对应.
#defineEI_NIDENT16typedefstruct{unsignedchare_ident[EI_NIDENT];uint16_te_type;uint16_te_machine;uint32_te_version;ElfN_Addre_entry;ElfN_Offe_phoff;ElfN_Offe_shoff;uint32_te_flags;uint16_te_ehsize;uint16_te_phentsize;uint16_te_phnum;uint16_te_shentsize;uint16_te_shnum;uint16_te_shstrndx;}ElfN_Ehdr;PA2-2程序装载的核心内容ELF可执行目标文件百闻不如一见以testcase/bin/add为例:readelf–aadd|less出来好多内容,一块块地来看2017/10/20ProgrammingAssignment2-221ProgramHeaders:TypeOffsetVirtAddrPhysAddrFileSizMemSizFlgAlignLOAD0x0010000x000300000x000300000x001540x00154RE0x1000LOAD0x0020000x000320000x000320000x001400x00140RW0x1000GNU_STACK0x0000000x000000000x000000000x000000x00000RWE0x10SectiontoSegmentmapping:SegmentSections.
.
.
00.
text.
eh_frame01.
got.
plt.
data02通过ElfN_Ehdr中的e_phoff可以找到程序头表的首地址,而所谓的程序头表,在代码中就是Elf32_Phdr类型的一个数组,包含e_phnum个数组元素.
数组中的每一项:Type–该程序段的类型;Offset–该程序段在ELF文件中的偏移量;VirtAddr–该程序段在内存中的虚拟地址,此时等于PhysAddr,以后也不用看PhysAddr;FileSiz-该程序段在文件中所占用的字节数;MemSiz–该程序段在内存中所占用的字节数;其它.
typedefstruct{uint32_tp_type;Elf32_Offp_offset;Elf32_Addrp_vaddr;Elf32_Addrp_paddr;uint32_tp_filesz;uint32_tp_memsz;uint32_tp_flags;uint32_tp_align;}Elf32_Phdr;PA2-2程序装载的核心内容看这个mapping,对比各个segment的Offset和SectionHeaders里面对应Section的Off单独查看程序头表readelf–ladd|less装载ELF可执行文件所谓装载ELF可执行文件,就是把程序头表中,type为LOAD类型的项目,把ELF文件中从Offset开始的FileSiz字节的内容,拷贝到内存VirtAddr开始的MemSiz大小的区域中有时候MemSiz比FileSiz大把内存中VirtAddr+[FileSiz,MemSiz]的区域清零2017/10/20ProgrammingAssignment2-222装载ELF可执行文件2017/10/20ProgrammingAssignment2-223ELFfile0^||||||||Type|OffsetVirtAddrPhysAddr|FileSizMemSizFlgAlignLOAD+--0x0010000x001000000x00100000+0x1d6000x27240RWE0x1000|||||||||00000000000|^|00000000000||||vv||||Memory装载ELF可执行文件2017/10/20ProgrammingAssignment2-224ELFfile0^||||||||Type|OffsetVirtAddrPhysAddr|FileSizMemSizFlgAlignLOAD+--0x0010000x001000000x00100000+0x1d6000x27240RWE0x1000|||||||||00000000000|^|00000000000||||vv||||Memory这一块到底是什么,能尝试找出个例子吗装载ELF可执行文件简单步骤一、读入ELF文件,前面若干字节解释为Elf32_Ehdr(强制类型转换即可)二、读Elf32_Ehdr中的e_phoff和e_phnum,得到程序头表在ELF文件中的起始地址和表项数三、循环读取每一个表项查看其type,如果是LOAD类型(LOAD类型在程序中等于几通过manelf进行查看)则执行上一页中的拷贝操作如果不是LOAD类型则忽略四、程序头表读完了加载完成2017/10/20ProgrammingAssignment2-225装载ELF可执行文件谁来干这装载的工作NEMU是不管的,它是裸机,只认指令序列,才不管你的可执行目标文件存成什么样既然可执行目标文件的格式是操作系统说了算的(Linux和Windows不一样),自然要有一个操作系统于是Kernel登场了!
2017/10/20ProgrammingAssignment2-226$maketestkernel装载ELF可执行文件maketestkernel其行为和maketest很相似区别在于,在物理内存0x30000处装载的不再是testcasebinaryimage,而是kernelbinaryimage物理内存0x0处开始,RamDisk内摆放的仍然是testcaseELFfile2017/10/20ProgrammingAssignment2-227InitialEIP|PhysicalAddressv0x00x30000|TestcaseELFFile|~|KernelBinary|~|装载ELF可执行文件maketestkernelNEMU从0x30000开始执行的第一条指令是Kernel的指令Kernel负责将testcaseELF文件装载到内存里此时如果testcase在链接时仍然以0x30000作为起始地址,那么必然覆盖Kernel的内容,因此必须为kernel让出必要的空间在testcase/Makefile中,修改-Text=0x600002017/10/20ProgrammingAssignment2-228InitialEIP|PhysicalAddressv0x00x300000x60000|TestcaseELFFile|~|KernelBinary|~|TestcaseBinary|~|+^|loader()|实际上为Kernel留出0x30000字节的空间并不完全足够.
通过readelf–akernel仔细观察,发现其实在0x60000以后kernel还有内容.
只是在现阶段,覆盖这些内存区域不影响kernel工作.
等到kernel要启用这些区域时,我们也已经进入到下一阶段了.
不放心的可以把-Text修改为更大的值,比如0x100000以上装载ELF可执行文件Kernel的行为从kernel/start/start.
S开始2017/10/20ProgrammingAssignment2-229#ifndefIA32_SEG//没在include/config.
h中defineIA32_SEG,因此编译这个分支.
globlstartstart:#SetupastackforCcode.
movl$0,%ebpmovl$(128e_phoff;//找到ELF文件中的程序头表eph=ph+elf->e_phnum;for(;php_type==PT_LOAD){//如果类型是LOAD,那么就去装载吧panic("Pleaseimplementtheloader");/*TODO:copythesegmentfromtheELFfiletoitspropermemoryarea*//*TODO:zerorthememoryarea[vaddr+file_sz,vaddr+mem_sz)*/#ifdefIA32_PAGE…//没有开启分页#endif}}volatileuint32_tentry=elf->e_entry;//头文件中指出的testcase起始地址,应该是0x60000…//现在不管returnentry;//返回testcase起始地址,在init_cond()后面执行((void(*)(void))eip)();}装载ELF可执行文件如果loader()实现正确,且指令实现都正确执行maketestkernel2017/10/20ProgrammingAssignment2-234除了test-float,其它都是HITGOODTRAPPA2-2程序的装载&PA2-3调试器ELF文件的装载符号表的解析符号表的解析在程序加载的过程中,我们解析了ELF文件的程序头表部分而符号表解析的过程中,我们关注ELF文件的符号表部分首先要找到名为.
symtab的符号表和名为.
strtab的字符串表从读取节头表开始…2017/10/20ProgrammingAssignment2-236ELF可执行目标文件百闻不如一见以testcase/bin/add为例:readelf–aadd|less出来好多内容,一块块地来看2017/10/20ProgrammingAssignment2-237ELFHeader:Magic:7f454c46010101000000000000000000Class:ELF32Data:2'scomplement,littleendianVersion:1(current)OS/ABI:UNIX-SystemVABIVersion:0Type:EXEC(Executablefile)Machine:Intel80386Version:0x1Entrypointaddress:0x30000Startofprogramheaders:52(bytesintofile)Startofsectionheaders:18276(bytesintofile)Flags:0x0Sizeofthisheader:52(bytes)Sizeofprogramheaders:32(bytes)Numberofprogramheaders:3Sizeofsectionheaders:40(bytes)Numberofsectionheaders:15Sectionheaderstringtableindex:14ELF头,它位于整个ELF文件最开始的地方.
在32位Linux系统中,头文件中的Elf32_Ehdr数据结构与之对应.
#defineEI_NIDENT16typedefstruct{unsignedchare_ident[EI_NIDENT];uint16_te_type;uint16_te_machine;uint32_te_version;ElfN_Addre_entry;ElfN_Offe_phoff;ElfN_Offe_shoff;uint32_te_flags;uint16_te_ehsize;uint16_te_phentsize;uint16_te_phnum;uint16_te_shentsize;uint16_te_shnum;uint16_te_shstrndx;}ElfN_Ehdr;找到名为.
symtab的符号表和名为.
strtab的字符串表ELF可执行目标文件百闻不如一见以testcase/bin/add为例:readelf–aadd|less出来好多内容,一块块地来看2017/10/20ProgrammingAssignment2-238SectionHeaders:[Nr]NameTypeAddrOffSizeESFlgLkInfAl[0]NULL0000000000000000000000000[1].
textPROGBITS000300000010000000d000AX001[2].
eh_framePROGBITS000300d00010d000008400A004[3].
got.
pltPROGBITS0003200000200000000c04WA004[4].
dataPROGBITS0003202000202000012000WA0032[5].
commentPROGBITS0000000000214000002601MS001[6].
debug_arangesPROGBITS0000000000216800004000008[7].
debug_infoPROGBITS000000000021a800014200001[8].
debug_abbrevPROGBITS000000000022ea0000d600001[9].
debug_linePROGBITS000000000023c00000dc00001[10].
debug_strPROGBITS0000000000249c001a3701MS001[11].
debug_macroPROGBITS00000000003ed30005f900001[12].
symtabSYMTAB000000000044cc0001901013154[13].
strtabSTRTAB0000000000465c00007800001[14].
shstrtabSTRTAB000000000046d400009000001KeytoFlags:W(write),A(alloc),X(execute),M(merge),S(strings),I(info),L(linkorder),O(extraOSprocessingrequired),G(group),T(TLS),C(compressed),x(unknown),o(OSspecific),E(exclude),p(processorspecific)Therearenosectiongroupsinthisfile.
通过ElfN_Ehdr中的e_shoff可以找到节头表的首地址,而所谓的节头表,在代码中就是ELF32_Shdr类型的一个数组,包含e_shnum个数组元素.
数组中的每一项:Addr–该节在内存中的地址,Off–该节在ELF文件中的位置,Size-该节在文件中所占用的字节数.
typedefstruct{uint32_tsh_name;uint32_tsh_type;uint32_tsh_flags;Elf32_Addrsh_addr;Elf32_Offsh_offset;uint32_tsh_size;uint32_tsh_link;uint32_tsh_info;uint32_tsh_addralign;uint32_tsh_entsize;}Elf32_Shdr;这里的sh_name只是一个索引值,只有用该索引值去查了.
shstrtab之后才能得到.
text,.
data这样的字符串,而.
shstrtab在哪里呢ELFHeader中的e_shstrndx变量告诉我们,它在SectionHeaders数组中的第14项找到名为.
symtab的符号表和名为.
strtab的字符串表ELF可执行目标文件百闻不如一见以testcase/bin/add为例:readelf–aadd|less出来好多内容,一块块地来看2017/10/20ProgrammingAssignment2-239SectionHeaders:[Nr]NameTypeAddrOffSizeESFlgLkInfAl[0]NULL0000000000000000000000000[1].
textPROGBITS000300000010000000d000AX001[2].
eh_framePROGBITS000300d00010d000008400A004[3].
got.
pltPROGBITS0003200000200000000c04WA004[4].
dataPROGBITS0003202000202000012000WA0032[5].
commentPROGBITS0000000000214000002601MS001[6].
debug_arangesPROGBITS0000000000216800004000008[7].
debug_infoPROGBITS000000000021a800014200001[8].
debug_abbrevPROGBITS000000000022ea0000d600001[9].
debug_linePROGBITS000000000023c00000dc00001[10].
debug_strPROGBITS0000000000249c001a3701MS001[11].
debug_macroPROGBITS00000000003ed30005f900001[12].
symtabSYMTAB000000000044cc0001901013154[13].
strtabSTRTAB0000000000465c00007800001[14].
shstrtabSTRTAB000000000046d400009000001KeytoFlags:W(write),A(alloc),X(execute),M(merge),S(strings),I(info),L(linkorder),O(extraOSprocessingrequired),G(group),T(TLS),C(compressed),x(unknown),o(OSspecific),E(exclude),p(processorspecific)Therearenosectiongroupsinthisfile.
通过ElfN_Ehdr中的e_shoff可以找到节头表的首地址,而所谓的节头表,在代码中就是ELF32_Shdr类型的一个数组,包含e_shnum个数组元素.
数组中的每一项:Addr–该节在内存中的地址,Off–该节在ELF文件中的位置,Size-该节在文件中所占用的字节数.
typedefstruct{uint32_tsh_name;uint32_tsh_type;uint32_tsh_flags;Elf32_Addrsh_addr;Elf32_Offsh_offset;uint32_tsh_size;uint32_tsh_link;uint32_tsh_info;uint32_tsh_addralign;uint32_tsh_entsize;}Elf32_Shdr;这里的sh_name只是一个索引值,只有用该索引值去查了.
shstrtab之后才能得到.
text,.
data这样的字符串,而.
shstrtab在哪里呢ELFHeader中的e_shstrndx变量告诉我们,它在SectionHeaders数组中的第14项符号表,及其对应的字符串表!
找到名为.
symtab的符号表和名为.
strtab的字符串表NEMU中找到符号表的代码nemu/src/monitor/elf.
c/*Loadsectionheadertable读取节头表*/uint32_tsh_size=elf->e_shentsize*elf->e_shnum;Elf32_Shdr*sh=malloc(sh_size);fseek(fp,elf->e_shoff,SEEK_SET);fread(sh,sh_size,1,fp);/*Loadsectionheaderstringtable读取节头表对应的字符串表*/char*shstrtab=malloc(sh[elf->e_shstrndx].
sh_size);fseek(fp,sh[elf->e_shstrndx].
sh_offset,SEEK_SET);fread(shstrtab,sh[elf->e_shstrndx].
sh_size,1,fp);inti;for(i=0;ie_shnum;i扫描节头表*/if(sh[i].
sh_type==SHT_SYMTAB&&strcmp(shstrtab+sh[i].
sh_name,".
symtab")==0){/*Loadsymboltablefromexec_file得到符号表*/symtab=malloc(sh[i].
sh_size);fseek(fp,sh[i].
sh_offset,SEEK_SET);fread(symtab,sh[i].
sh_size,1,fp);nr_symtab_entry=sh[i].
sh_size/sizeof(symtab[0]);}elseif(sh[i].
sh_type==SHT_STRTAB&&strcmp(shstrtab+sh[i].
sh_name,".
strtab")==0){/*Loadstringtablefromexec_file得到符号表对应的字符串表*/strtab=malloc(sh[i].
sh_size);fseek(fp,sh[i].
sh_offset,SEEK_SET);fread(strtab,sh[i].
sh_size,1,fp);}}找到名为.
symtab的符号表和名为.
strtab的字符串表这一步和解析符号名称时的操作一样,等一下细讲符号表的解析符号表也是个数组,其类型为Elf32_Sym,首地址查节头表来获取2017/10/20ProgrammingAssignment2-241Symboltable'.
symtab'contains25entries:Num:ValueSizeTypeBindVisNdxName0:000000000NOTYPELOCALDEFAULTUND1:000300000SECTIONLOCALDEFAULT12:000300d00SECTIONLOCALDEFAULT23:000320000SECTIONLOCALDEFAULT34:000320200SECTIONLOCALDEFAULT45:000000000SECTIONLOCALDEFAULT56:000000000SECTIONLOCALDEFAULT67:000000000SECTIONLOCALDEFAULT78:000000000SECTIONLOCALDEFAULT89:000000000SECTIONLOCALDEFAULT910:000000000SECTIONLOCALDEFAULT1011:000000000SECTIONLOCALDEFAULT1112:000000000FILELOCALDEFAULTABSadd.
c13:000000000FILELOCALDEFAULTABS14:000320000OBJECTLOCALDEFAULT3_GLOBAL_OFFSET_TABLE_15:000300c80FUNCGLOBALHIDDEN1__x86.
get_pc_thunk.
ax16:0003000532FUNCGLOBALDEFAULT1add17:000300cc0FUNCGLOBALHIDDEN1__x86.
get_pc_thunk.
bx18:000321400NOTYPEGLOBALDEFAULT4__bss_start19:00030025163FUNCGLOBALDEFAULT1main20:00032040256OBJECTGLOBALDEFAULT4ans21:000321400NOTYPEGLOBALDEFAULT4_edata22:000321400NOTYPEGLOBALDEFAULT4_end23:000300000NOTYPEGLOBALDEFAULT1start24:0003202032OBJECTGLOBALDEFAULT4test_datareadelf–saddtypedefstruct{uint32_tst_name;Elf32_Addrst_value;uint32_tst_size;unsignedcharst_info;unsignedcharst_other;uint16_tst_shndx;}Elf32_Sym;st_name–符号名称,对应strtab中的偏移量st_value–符号的地址st_size-符号所占用的字节数st_info–包含了Type信息,manelf查看说明inttest_data[]={0,1,2,0x7fffffff,0x80000000,0x80000001,0xfffffffe,0xffffffff};testcase/src/add.
c这里的name是解析过的符号表的解析符号表(.
symtab)与字符串表(.
strtab)结合,获取符号的字符串形式的名称2017/10/20ProgrammingAssignment2-2420000465020200300200000001100040000616464add|000046602e63005f474c4f42414c5f4f46465345|.
c.
_GLOBAL_OFFSE|00004670545f5441424c455f005f5f7838362e67|T_TABLE_.
__x86.
g|0000468065745f70635f7468756e6b2e61780061|et_pc_thunk.
ax.
a|000046906464005f5f7838362e6765745f70635f|dd.
__x86.
get_pc_|000046a07468756e6b2e6278005f5f6273735f73|thunk.
bx.
__bss_s|000046b074617274006d61696e00616e73005f65|tart.
main.
ans.
_e|000046c064617461005f656e6400746573745f64|data.
_end.
test_d|000046d061746100002e73796d746162002e7374|ata.
.
.
symtab.
.
st|hexdump–CaddSectionHeaders:[Nr]NameTypeAddrOffSizeESFlgLkInfAl[13].
strtabSTRTAB0000000000465c00007800001Symboltable'.
symtab'contains25entries:Num:ValueSizeTypeBindVisNdxName24:0003202032OBJECTGLOBALDEFAULT46d'\0'符号表的解析符号表(.
symtab)与字符串表(.
strtab)结合nemu/src/monitor/elf.
c找到一个符号(使用全局变量名或函数名)在内存中的地址2017/10/20ProgrammingAssignment2-243uint32_tlook_up_symtab(char*sym,bool*success){inti;for(i=0;ist_info);if((type==STT_FUNC||type==STT_OBJECT)&&strcmp(strtab+symtab[i].
st_name,sym)==0){*success=true;returnsymtab[i].
st_value;}}*success=false;return0;}符号表的解析符号表解析了有啥用如果你想写一个链接器可以将处于不同.
o文件中的全局变量或函数的调用和定义通过内存地址联系到一起readelf–snemu/src/cpu/decode/opcode.
oreadelf–snemu/src/cpu/instr/mov.
oProgrammingAssignment2-244Symboltable'.
symtab'contains249entries:Num:ValueSizeTypeBindVisNdxName.
.
.
223:000000000NOTYPEGLOBALDEFAULTUNDmov_i2r_b224:000000000NOTYPEGLOBALDEFAULTUNDmov_i2r_v.
.
.
opcode.
oSymboltable'.
symtab'contains165entries:Num:ValueSizeTypeBindVisNdxName.
.
.
149:000002a0176FUNCGLOBALDEFAULT39mov_i2rm_b151:00000350176FUNCGLOBALDEFAULT39mov_i2rm_v.
.
.
mov.
o如果发现符号表中有多个Type为FUNC或OBJECT,Bind类型为GLOBAL,其Ndx都显示在某一个section中被定义了的符号具有同样的名字:multipledefinitionofxxx去掉staticvoidinstr_execute_2op()前面的static就能够触发(比如尝试mov.
c和sar.
c,把static去掉),观察一下对应的符号表,是不是有什么变化符号表的解析符号表解析了有啥用对于NEMU来说你可以使用xtest_data来查看test_data的起始地址再使用x起始地址+offset来查看test_data的内容也可以使用x*(test_data+offset)来查看test_data的内容你也可以使用bmain来在main函数开始处设置断点在NEMU中使用上述功能涉及对表达式求值功能的实现相应教程:看教程PA2-3部分代码:nemu/src/monitor/expr.
c我们下次课再讲2017/10/20ProgrammingAssignment2-245实用小贴士引入kernel之后makerun不能用了maketestkernel不进入交互模式了,调试好困难可以使用BREAK_POINT宏强制进入交互模式2017/10/20ProgrammingAssignment2-246#ifndefIA32_SEG.
globlstartstart:#SetupastackforCcode.
BREAK_POINT#每次一进Kernel就break,进入交互模式movl$0,%ebpmovl$(128<<20),%espjmpinit#neverreturn任务PA2-2kernel装载程序2017年11月1日24时截止期间建议充分熟悉ELF文件以及相应的符号解析部分建议大家有能力的尽可能对照教程往前做,不要等,为配合理论课我们前面只能放得很慢PA2-3的截止因为要等理论课进度,我们会再往后延一些延了这么久了,大家就努力把这个部分做完吧下次课我们讲表达式求值同时为PA3开个头2017/10/20ProgrammingAssignment2-247祝大家学习快乐,身心健康!
欢迎大家踊跃参加问卷调查(量表一、二、三到PA2截止时做一次,如果PA2-2截止的时候也能做一次最好)PA2-2到此结束
易探云香港vps主机价格多少钱?香港vps主机租用费用大体上是由配置决定的,我们选择香港vps主机租用最大的优势是免备案vps。但是,每家服务商的机房、配置、定价也不同。我们以最基础配置为标准,综合比对各大香港vps主机供应商的价格,即可选到高性能、价格适中的香港vps主机。通常1核CPU、1G内存、2Mbps独享带宽,价格在30元-120元/月。不过,易探云香港vps主机推出四个机房的优惠活动,...
已经有一段时间没有听到Gigsgigscloud服务商的信息,这不今天看到商家有新增一款国际版线路的美国VPS主机,年付也是比较便宜的只需要26美元。线路上是接入Cogentco、NTT、AN2YIX以及其他亚洲Peering。这款方案的VPS主机默认的配置是1Gbps带宽,比较神奇的需要等待手工人工开通激活,不是立即开通的。我们看看这款服务器在哪里选择看到套餐。内存CPUSSD流量价格购买地址1...
gigsgigsCloud日本东京软银VPS的大带宽配置有100Mbps、150Mbps和200Mbps三种,三网都走软银直连,售价最低9.8美元/月、年付98美元。gigsgigscloud带宽较大延迟低,联通用户的好选择!Gigsgigscloud 日本软银(BBTEC, SoftBank)线路,在速度/延迟/价格方面,是目前联通用户海外VPS的最佳选择,与美国VPS想比,日本软银VPS延迟更...
电脑操作系统有哪些为你推荐
小企业如何做品牌一个小企业,如何做大做强?phpcms模板请教 phpcms v9 如何设置新模板为系统默认模板?linux防火墙设置怎样用iptables配置好Linux防火墙?苹果appstore宕机为什App Store下载软件 到了一半就停了 不动了企业建网站企业建网站有什么用?支付宝调整还款日支付宝还款日期可以更改吗?文档下载如何 下载 文库文件泉州商标注册请问泉州商标注册要怎么办理?在哪办理?curl扩展大神帮忙看下centos 7.2 系统 php7.0.12的 curl 扩展怎么开启,谢谢啦申请400电话400电话如何申请办理?
万网域名管理 国外永久服务器 主机点评 狗爹 外国服务器 搜狗抢票助手 2017年万圣节 qq数据库下载 权嘉云 申请个人网站 国外免费全能空间 nerds 空间技术网 卡巴斯基免费试用 电信托管 东莞idc www789 主机管理系统 广东服务器托管 闪讯网 更多