2011/10/21 Yao Qi <qiyaoltc@xxxxxxxxx>: > 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吧。 > 恩,所以说PLT/GOT等概念要自己补充,这个东西一两句说不清的。我只是提取出来了这里需要的点一下,其实我也没有理解非常透彻。 >> 很乱,对吧?结合实际例子看看。 >> \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?这个是什么? > > 问题可能有点跑题 :) > 恩,有时间的话我整理一下md文件里面我个人能理解和掌握的,这个例子里面确实和这些内容没太大关联。 这个md文件水很深,也不是一年半载能讲清楚的,有时间的话,一点儿一点儿来,通过不同的例子,不同的角度,来慢慢讲清楚不同的用法。 >> "" >> " >> { >> 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. > >