[hellogcc] Re: [hellogcc] Re: gcc中的reloc实现简介

  • From: Liu <proljc@xxxxxxxxx>
  • To: hellogcc@xxxxxxxxxxxxx
  • Date: Fri, 21 Oct 2011 09:59:48 +0800

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.
>
>

Other related posts: