On Thu, Oct 20, 2011 at 05:09:19PM +0800, Liu wrote: > 我做的时候没找到资料,现在尝试造一个资料。 > #include <stdio.h> > > int add(int a, int b) > { > int c; > c = a + b; > } > > int main(void) > { > int c; > > c = add(1, 2); > printf("c = %d\n", c); > > return 0; > } 很好的文章。 > > \section{GOT和PLT是干神马吃滴} > 上一节说过,调用的printf是libc.so里面的函数,也就是一个reloc的函数,而这个reloc有涉及到了PLT,追查PLT发现了一个叫GOT我玩意儿。 > > 我只能简单说一下,具体概念自己去补充, > > PLT是把位置无关的调用定位到实际地址。GOT则是把位置无关的代码地位到实际地址。 > PLT/GOT的作用比你说的要多一些,还是自己看linker&loader吧。 > 很乱,对吧?结合实际例子看看。 > \begin{shaded} > \begin{verbatim} > 400567: e8 c4 fe ff ff callq 400430 <printf@plt> > \end{verbatim} > \end{shaded} > PLT把这个位置无关的call,即call printf定位到400430,400430是什么? > \begin{shaded} > \begin{verbatim} > 0000000000400430 <printf@plt>: > 400430: ff 25 d2 1b 00 00 jmpq *0x1bd2(%rip) # 402008 > <_GLOBAL_OFFSET_TABLE_+0x20> > \end{verbatim} > \end{shaded} > 就是这个,printf函数的地址。 > > 但是400430地址的代码是一个jmp,马上jmp到了一个地址,而这个地址恰恰就是\_GLOBAL\_OFFSET\_TABLE\_+0x20,\_GLOBAL\_OFFSET\_TABLE\_是一个基地址,通过相对基地址的偏移来寻址的。 > > \section{gcc里面是怎么处理GOT和PLT的} > 现在应该明确了一件事情,那就是gcc对reloc的处理其实只要关心call就好。 这句不对吧。其实你这里讲的都是PIC 代码。reloc 和 PIC没有什么直接关系。 PIC和非PIC都需要 reloc,数据段也需要reloc。 严格说,gcc对pic的处理关心call,但是不是*只*关心call。只是说在这个例子, 关心call。 > > machine.h里面需要有一行代码定义GOT使用哪个寄存器存放基地址。 > \begin{shaded} > \begin{verbatim} > #define PIC_OFFSET_TABLE_REGNUM num > \end{verbatim} > \end{shaded} > > 然后machine.md里面对call要有相应的照顾。 > \begin{shaded} > \begin{verbatim} > (define_expand "call" > [(parallel [(call (match_operand:SI 0 "sym_ref_mem_operand" "") > (match_operand 1 "" "i")) > (clobber (reg:SI 9))])] 能把上边这个解释一下不? operand 0 和operand 1分别是什么? 为啥clobber reg:SI 9?这个是什么? 问题可能有点跑题 :) > "" > " > { > if (flag_pic) > { > rtx reg = gen_reg_rtx (SImode); > emit_insn (gen_set_got (pic_offset_table_rtx)); > emit_call_insn (gen_call_internal_plt (operands[0], reg)); > } > else > emit_call_insn (gen_call_internal (operands[0], operands[1])); > DONE; > }") > \end{verbatim} > \end{shaded} > 首先,call的操作数就是sym\_ref\_mem\_operand了,sym是一个标号,位置未确定的那种。 > > 然后,通过emit\_insn (gen\_set\_got (pic\_offset\_table\_rtx))来找到相对GOT的偏移。 > > 最后才是emit\_call\_insn (gen\_call\_internal\_plt (operands[0], > reg))输出PIC的call代码。 > > 跟最开始,我们通过printf这个sym找到PLT,再通过PLT找到GOT的线索刚好是一个逆过程。 > > 做一个port要做完整,PIC这种基本的东西,该有的都应该做了的。 > \end{document} -- Yao Qi <qiyaoltc AT gmail DOT com> If you learn one useless thing every day, in a single year you'll learn 365 useless things.