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到此结束
这个月11号ShockHosting发了个新上日本东京机房的邮件,并且表示其他机房可以申请转移到日本,刚好赵容手里有个美国的也没数据就发工单申请新开了一个,这里做个简单的测试,方便大家参考。ShockHosting成立于2013年,目前提供的VPS主机可以选择11个数据中心,包括美国洛杉矶、芝加哥、达拉斯、杰克逊维尔、新泽西、澳大利亚、新加坡、日本、荷兰和英国等。官方网站:https://shoc...
商家介绍:星梦云怎么样,星梦云好不好,资质齐全,IDC/ISP均有,从星梦云这边租的服务器均可以备案,属于一手资源,高防机柜、大带宽、高防IP业务,一手整C IP段,四川电信,星梦云专注四川高防服务器,成都服务器,雅安服务器,。活动优惠促销:1、成都电信夏日激情大宽带活动机(封锁UDP,不可解封):机房CPU内存硬盘带宽IP防护流量原价活动价开通方式成都电信优化线路2vCPU2G40G+60G21...
TMThosting发布了一个2021 Summer Sale活动,针对西雅图VPS主机提供月付7折优惠码,年付65折优惠码,独立服务器提供95折优惠码,本轮促销活动到7月25日。这是一家成立于2018年的国外主机商,主要提供VPS和独立服务器租用业务,数据中心包括美国西雅图和达拉斯,其中VPS基于KVM架构,都有提供免费的DDoS保护,支持选择Windows或者Linux操作系统。Budget ...
电脑操作系统有哪些为你推荐
功放iphonesns平台社交网站是啥意思?开启javascript启用javascript是甚么意思conn.asp数据库连接出错,请打开conn.asp文件检查连接字串。163yeah163,126,yeah哪个更好啊,各有什么特点啊波音737起飞爆胎为什么很少见到飞机轮胎爆胎?flashfxp注册码求一个flashfxp v3.0.2的注册码补贴esetpiaonimai跪求朴妮唛的的韩文歌,不知道是哪一部的,第一首放的是Girl's Day《Oh! My God》。求第三首韩文歌曲,一男一女唱的。颁发的拼音大致的致的拼音
net主机 vmsnap3 免费博客空间 蜗牛魔方 电信虚拟主机 metalink 东莞服务器 网站加速软件 浙江服务器 镇江高防 网页加速 ssl加速 免备案cdn加速 免费赚q币 shuangcheng 什么是dns 硬防 rewrite规则 dbank ddos攻击软件 更多