位置相关代码与位置独立代码之间的区别?

时间:2020-01-11 03:18:19

标签: c

我了解到,当前的gcc编译器默认会生成位置无关的代码。但是,为了了解位置相关代码的外观,我编译了此

int Add(int x, int y) {
    return x+y;
}

int Subtract(int x, int y) {
    return x-y;
}

int main() {
    bool flag = false;
    int x=10,y=5,z;

    if (flag) {
        z = Add(x,y);
    }
    else {
        z = Subtract(x,y);
    }
}

g++ -c check.cpp -no-pie。但是,无论是否带有-no-pie标志,生成的代码都是相同的。 <main+0x34>似乎是相对偏移量。

  26:   55                      push   %rbp
  27:   48 89 e5                mov    %rsp,%rbp
  2a:   48 83 ec 10             sub    $0x10,%rsp
  2e:   c6 45 f3 00             movb   $0x0,-0xd(%rbp)
  32:   c7 45 f4 0a 00 00 00    movl   $0xa,-0xc(%rbp)
  39:   c7 45 f8 05 00 00 00    movl   $0x5,-0x8(%rbp)
  40:   80 7d f3 00             cmpb   $0x0,-0xd(%rbp)
  44:   74 14                   je     5a <main+0x34>
  46:   8b 55 f8                mov    -0x8(%rbp),%edx
  49:   8b 45 f4                mov    -0xc(%rbp),%eax
  4c:   89 d6                   mov    %edx,%esi
  4e:   89 c7                   mov    %eax,%edi
  50:   e8 00 00 00 00          callq  55 <main+0x2f>
  55:   89 45 fc                mov    %eax,-0x4(%rbp)
  58:   eb 12                   jmp    6c <main+0x46>
  5a:   8b 55 f8                mov    -0x8(%rbp),%edx
  5d:   8b 45 f4                mov    -0xc(%rbp),%eax
  60:   89 d6                   mov    %edx,%esi
  62:   89 c7                   mov    %eax,%edi
  64:   e8 00 00 00 00          callq  69 <main+0x43>
  69:   89 45 fc                mov    %eax,-0x4(%rbp)
  6c:   b8 00 00 00 00          mov    $0x0,%eax
  71:   c9                      leaveq 
  72:   c3                      retq
在两种情况下,

都是objdump,只是主要的。我使用的标志是否不正确,或者该代码块的PIC和非PIC的汇编代码应该相同?如果应该相同,请提供一个不是的摘要!

1 个答案:

答案 0 :(得分:2)

您必须访问模块或部分之外的项目才能看到区别。

unsigned int x;
void fun ( void )
{
    x = 5;
}

因此这将.text转换为.data。

取决于位置。

00000000 <fun>:
   0:   e3a02005    mov r2, #5
   4:   e59f3004    ldr r3, [pc, #4]    ; 10 <fun+0x10>
   8:   e5832000    str r2, [r3]
   c:   e12fff1e    bx  lr
  10:   00000000

与位置无关

00000000 <fun>:
   0:   e3a02005    mov r2, #5
   4:   e59f3010    ldr r3, [pc, #16]   ; 1c <fun+0x1c>
   8:   e59f1010    ldr r1, [pc, #16]   ; 20 <fun+0x20>
   c:   e08f3003    add r3, pc, r3
  10:   e7933001    ldr r3, [r3, r1]
  14:   e5832000    str r2, [r3]
  18:   e12fff1e    bx  lr
  1c:   00000008
  20:   00000000

在第一种情况下,链接器将地址填写到存储位置

   8:   e5832000    str r2, [r3]
   c:   e12fff1e    bx  lr
  10:   00000000  <--- here

。pc相对地址从4:到10:在.text部分之内,因此可以依赖或独立。

   4:   e59f3004    ldr r3, [pc, #4]    ; 10 <fun+0x10>
   8:   e5832000    str r2, [r3]
   c:   e12fff1e    bx  lr
  10:   00000000

它获取到外部实体的地址,由链接器填写,然后直接在该地址访问该项目。

   4:   e59f3010    ldr r3, [pc, #16]   ; 1c <fun+0x1c>
   8:   e59f1010    ldr r1, [pc, #16]   ; 20 <fun+0x20>
   c:   e08f3003    add r3, pc, r3
  10:   e7933001    ldr r3, [r3, r1]
  14:   e5832000    str r2, [r3]
  18:   e12fff1e    bx  lr
  1c:   00000008
  20:   00000000

更容易看到链接(-Ttext = 0x1000 -Tdata = 0x2000)

00001000 <fun>:
    1000:   e3a02005    mov r2, #5
    1004:   e59f3010    ldr r3, [pc, #16]   ; 101c <fun+0x1c>
    1008:   e59f1010    ldr r1, [pc, #16]   ; 1020 <fun+0x20>
    100c:   e08f3003    add r3, pc, r3
    1010:   e7933001    ldr r3, [r3, r1]
    1014:   e5832000    str r2, [r3]
    1018:   e12fff1e    bx  lr
    101c:   00010010
    1020:   0000000c

Disassembly of section .got:

00011024 <_GLOBAL_OFFSET_TABLE_>:
    ...
   11030:   00002000

Disassembly of section .bss:

00002000 <x>:
    2000:   00000000

(显然,我还应该指定GOT的去向)。

尽管全局偏移表和.bss是链接在一起的不同部分,但它们彼此相对固定。位置无关性是使.bss(或.data等)相对于.text移动的能力。因此,如果您考虑位置相关的解决方案,如果要移动.data并说整个二进制文件中散布了1000个引用,则要移动.bss,则必须修补其中的每一个。

相反,全局偏移表在此处提供了变量x的地址所在的单个位置,并且对变量x的所有访问本质上都将使用双重间接访问。也许不是很明显,但是位置依赖的获取表的方式将是链接器填写其绝对地址,但这不是独立的,并且被编译为独立的,因此必须完成pc相对数学查找全局偏移量表,因此对于在0x100c处执行指令时的该指令集,程序计数器为0x100c + 8。

    100c:   e08f3003    add r3, pc, r3

因此,我们将0x100C + 8 + 0x00010010 = 0x11024加上0x0000000c到0x11030。因此,计算出GOT的地址,然后计算其中的偏移量,这便为我们提供了该商品的地址。 0x2000。因此,您需要在此处进行第二次间接获取。

如果将.text放置在0x1000以外的地址上,但不要移动.bss,那很好,这一切都将起作用,直到GOT从.text移到相同的相对偏移。如果要离开.text但移动.bss,则必须更新GOT,如果将.bss从0x2000移至0x3000,则相差+ 0x1000,因此您要遍历GOT并向每个项目添加0x1000弥补差异。

为了访问遥远的项目或相对于.text不依赖于位置的项目,位置独立性实质上必须执行两次间接访问,而不是单一的间接访问(或比依赖于位置的级别多一层)。这意味着更多的代码,更多的内存访问。代码更多,速度更慢。

要使其工作.text扩展到其他.text项目,不能使用固定地址,它必须使用间接/计算地址。同样,此处使用的GOT(由GNU使用)必须相对于.text处于固定的相对位置。然后,您可以从那里相对于代码移动数据,并且仍然可以访问它。因此,您必须有一些规则。 .text是代码,并且假定为只读,因此无法支持此偏移表,该偏移表必须位于ram中,因此不能简单地将其内置到.text节中。