C++ 反汇编中的CALL 指令地址

178 views
Skip to first unread message

Peng Liang

unread,
May 27, 2011, 9:09:13 AM5/27/11
to sh...@googlegroups.com
具体问题实际上和

http://www.rhinocerus.net/forum/lang-asm-x86/254996-strange-call-instruction-linux-disassembly.html

中的问题一样, 可是我没有搜到答案。

我在描述一下:

C++ 的 Hello World 程序 hello.cpp

#include <stdio.h>

int main(int argc, char *argv[]) {
  printf("Hello World!");
  return 0;
}

g++ 编译 g++ -c -o hello.o hello.cpp
用 objdump 反编译, objdump  -d hello.o, 结果如下:

====================================================
hello.o:     file format elf32-i386

Disassembly of section .text:

00000000 <main>:
   0:    55                                push   %ebp
   1:    89 e5                            mov    %esp,%ebp
   3:    83 e4 f0                        and    $0xfffffff0,%esp
   6:    83 ec 10                       sub    $0x10,%esp
   9:    8b 45 0c                       mov    0xc(%ebp),%eax
   c:    83 c0 04                       add    $0x4,%eax
   f:     8b 00                            mov    (%eax),%eax
  11:    89 44 24 04                   mov    %eax,0x4(%esp)
  15:    c7 04 24 00 00 00 00     movl   $0x0,(%esp)
  1c:    e8 fc ff ff ff                     call    1d <main+0x1d>
  21:    b8 00 00 00 00              mov    $0x0,%eax
  26:    c9                                 leave 
  27:    c3                                 ret   

===================================================

我想知道 这个 fc ff ff ff  也就是 -4 这个地址 具体有什么意义?

实际上   1c:    e8 fc ff ff ff    call    1d <main+0x1d> 这条指令 应该是 call _printf,

_printf 是 unresolved symbol, 实际地址应该是在 LD 链接之后才能正确引用, 可是这里 汇编器把它设为 -4 是为什么呢?

============================

如果 你用 mingw-g++ 进行编译,汇编, 然后用 mingw-objdump 反编译, 这条指令就会是 e8 00 00 00 00,

另外, 如果是 hello.c, 无论用 gcc 编译 反汇编, 还是用 mingw-gcc 编译 反汇编 这条指令都是
e8 00 00 00 00,

所以我想知道 为什么 C++ 会不一样, 这个 0 和 -4 到底起了什么作用呢?

有对汇编 和 链接 研究过的, 给点建议。

谢谢~


--
Best regards,
Peng Liang

胡瀚森

unread,
May 27, 2011, 9:24:31 AM5/27/11
to sh...@googlegroups.com
个人猜测一下,有可能在连接之前是在符号表中的偏移量。
具体编译器怎么安排符号表就不明了。

2011/5/27 Peng Liang <pengli...@gmail.com>

Peng Liang

unread,
May 27, 2011, 9:32:45 AM5/27/11
to sh...@googlegroups.com


2011/5/27 胡瀚森 <softrank.net@gmail.com>
个人猜测一下,有可能在连接之前是在符号表中的偏移量。
具体编译器怎么安排符号表就不明了。

符号表的偏移量 是 -4, 就说不通了

另外, 如果调用多个不同的函数, 比如再加一句 putchar('c'); 在 printf()后面,

反汇编出来, 你就会发现调用这两个函数的 Call 指令 完全相同。

Shell Xu

unread,
May 27, 2011, 9:39:04 AM5/27/11
to sh...@googlegroups.com
我对linux的elf格式不是很熟悉,我比较熟悉windows的PE格式。在这种格式里面,有所谓“重定位”的问题。即在代码载入的时候,根据各种基地址计算出正确的引用地址,然后修改代码的特定位置,将正确的数值填回去。
我记得x86asm的指令中,e8是绝对地址调用,因此后面跟的应该是虚拟地址空间中的地址。十多年前的记忆了,不知道有没有记错。

--
无能者无所求,饱食而遨游,泛若不系之舟
blog: http://shell909090.com/blog/
twitter: http://twitter.com/shell909090

Gao Ya'nan

unread,
May 27, 2011, 9:51:45 AM5/27/11
to sh...@googlegroups.com
看一下 link and loader,程序执行的时候 loader 会做一些处理。

2011/5/27 Peng Liang <pengli...@gmail.com>:
>
>
> 2011/5/27 胡瀚森 <softra...@gmail.com>

Peng Liang

unread,
May 27, 2011, 9:59:53 AM5/27/11
to sh...@googlegroups.com


2011/5/27 Shell Xu <shell...@gmail.com>

我对linux的elf格式不是很熟悉,我比较熟悉windows的PE格式。在这种格式里面,有所谓“重定位”的问题。即在代码载入的时候,根据各种基地址计算出正确的引用地址,然后修改代码的特定位置,将正确的数值填回去。

重定位的算法就是这样的, 跟格式没有太大关系, 都是细节上的处理, 比如这个 -4 和 0 的问题, 肯定就是某个细节上的不同。
算法上没有本质不同。

另外, mingw-g++ 编译出来就是 PE 格式~

 
我记得x86asm的指令中,e8是绝对地址调用,因此后面跟的应该是虚拟地址空间中的地址。十多年前的记忆了,不知道有没有记错。


这个 e8 后面 的 -4 和 0 绝对不是 绝对地址, 不过 Link 重定向之后其后面的调用地址应该是 绝对地址, 也没有记错。
 

Shell Xu

unread,
May 27, 2011, 10:29:14 AM5/27/11
to sh...@googlegroups.com

哈哈,那我们说的应该是一回事。我只说e8是绝对地址调用,很明显0不可能是目标地址。

> --
> Best regards,
> Peng Liang
>
>

--

Peng Liang

unread,
May 27, 2011, 10:30:06 AM5/27/11
to sh...@googlegroups.com
另外, 如果是 hello.c, 无论用 gcc 编译 反汇编, 还是用 mingw-gcc 编译 反汇编 这条指令都是 e8 00 00 00 00,

所以我想知道 为什么 C++ 会不一样, 这个 0 和 -4 到底起了什么作用呢?


更正, 这个跟C 和 C++ 应该没什么关系, hello.c 用 gcc 编译反汇编 也是 e8 ff ff ff fc, 

应该 gcc 和 mingw-gcc 的区别, 希望不要误导大家....

Icat

unread,
May 27, 2011, 10:33:41 AM5/27/11
to sh...@googlegroups.com
可能是编译器对于 unresolved func的一个处理吧, 搞几个不同的函数上去, 不知道是否都一样

2011/5/27 Peng Liang <pengli...@gmail.com>

李晓岚

unread,
May 27, 2011, 9:31:33 AM5/27/11
to sh...@googlegroups.com
-4是重定位时链接器使用的addend。
--
不学无术

李晓岚

unread,
May 27, 2011, 9:36:49 AM5/27/11
to sh...@googlegroups.com
因为使用的是相对PC的call指令,PC超前当前指令4个字节,重定位时计算得到的值需要和addend相加。
--
不学无术



2011/5/27 Peng Liang <pengli...@gmail.com>

Peng Liang

unread,
May 28, 2011, 12:00:12 AM5/28/11
to sh...@googlegroups.com


2011/5/27 李晓岚 <leexi...@gmail.com>
因为使用的是相对PC的call指令,PC超前当前指令4个字节,重定位时计算得到的值需要和addend相加。


谢谢, 总算找到一点方向~ 能否再指点下为什么 PE 格式的 目标文件, 也就是用 mingw-g++ 编译出来的, 没有 -4 的偏移?

COFF 和 ELF 文件链接时的 addend 处理不一样么?

我去查查看 COFF 和 ELF 的标准看看 有说明不

李晓岚

unread,
May 28, 2011, 1:28:03 AM5/28/11
to sh...@googlegroups.com
ELF 文件中有两种重定位信息的Entry,一种是在Entry内部就带有Addend,一种是不带的。在不带addend信息的情况下,链接器就要用到代码段里的addend信息了。COFF文件不了解,重定位信息估计是后者。
--
不学无术



2011/5/28 Peng Liang <pengli...@gmail.com>

Serenade

unread,
May 28, 2011, 1:35:42 AM5/28/11
to sh...@googlegroups.com
使用flat memory Model的系统环境下,可执行程序一般没有什么重定位的问题,重定位表大多都是被strip掉的;这个是import表的处理
你gdb一下,运行时就会被加载器替换了,或者你用ida看,地址也是会被正确定位的。


--
Thanx & Regards,
小溪同学

adream307

unread,
May 28, 2011, 1:37:35 AM5/28/11
to sh...@googlegroups.com
在 2011-05-27,Icat <ica...@gmail.com> 写道:
可能是编译器对于 unresolved func的一个处理吧, 搞几个不同的函数上去, 不知道是否都一样
--------------------------------------------
我感觉是这样的,只是编译器对全局符号的一种填充
//h.c
#include<stdio.h>
static int print_hello()
{
    printf("hello\n");
    return 0;
}
extern int print_hello2()
{
    printf("hello2\n");
    return 0;
}
int main()
{
    getchar();
    print_hello();
    print_hello2();
    printf("Hello world!\n");
    return 0;    
}
objdump 的结果
00000000 <print_hello>:
   0: 55                   push   %ebp
   1: 89 e5                 mov    %esp,%ebp
   3: 83 ec 08             sub    $0x8,%esp
   6: c7 04 24 00 00 00 00 movl   $0x0,(%esp)
   d: e8 fc ff ff ff       call   e <print_hello+0xe>
  12: b8 00 00 00 00       mov    $0x0,%eax
  17: c9                   leave  
  18: c3                   ret    

00000019 <print_hello2>:
  19: 55                   push   %ebp
  1a: 89 e5                 mov    %esp,%ebp
  1c: 83 ec 08             sub    $0x8,%esp
  1f: c7 04 24 06 00 00 00 movl   $0x6,(%esp)
  26: e8 fc ff ff ff       call   27 <print_hello2+0xe>
  2b: b8 00 00 00 00       mov    $0x0,%eax
  30: c9                   leave  
  31: c3                   ret    

00000032 <main>:
  32: 8d 4c 24 04           lea    0x4(%esp),%ecx
  36: 83 e4 f0             and    $0xfffffff0,%esp
  39: ff 71 fc             pushl  0xfffffffc(%ecx)
  3c: 55                   push   %ebp
  3d: 89 e5                 mov    %esp,%ebp
  3f: 51                   push   %ecx
  40: 83 ec 04             sub    $0x4,%esp
  43: e8 fc ff ff ff       call   44 <main+0x12>
  48: e8 b3 ff ff ff       call   0 <print_hello>
  4d: e8 fc ff ff ff       call   4e <main+0x1c>
  52: c7 04 24 0d 00 00 00 movl   $0xd,(%esp)
  59: e8 fc ff ff ff       call   5a <main+0x28>
  5e: b8 00 00 00 00       mov    $0x0,%eax
  63: 83 c4 04             add    $0x4,%esp
  66: 59                   pop    %ecx
  67: 5d                   pop    %ebp
  68: 8d 61 fc             lea    0xfffffffc(%ecx),%esp
  6b: c3                   ret    

注意:因为 print_hello 是 static ,不是全局的, 所以它对应的是 b3 ff ff ff
而其他几个函数名都是全局符号,都是 fc ff ff ff



Peng Liang

unread,
May 28, 2011, 1:49:24 AM5/28/11
to sh...@googlegroups.com


2011/5/28 adream307 <adre...@gmail.com>

是的, 是对符号的填充, 不过这个填充是有意义的, 就是上面说的 r_addend 的作用,

链接的时候 这个值是参与 实际地址的计算的。

我现在在读代码, 想要找到设置这个值的地方...

adream307

unread,
May 28, 2011, 2:15:46 AM5/28/11
to sh...@googlegroups.com
链接的时候 这个值是参与 实际地址的计算的。
-------------------------
一个 .o 文件内有很多 fc ff ff ff,链接的时候根据它在.o文件内出现的先后顺序到符号表里去找该 fc ff ff ff所读对的函数名,而不是用这个值参与实际计算
你 readelf -s 看看,可以列出.o文件内的所有符号


hengi_Q

unread,
May 30, 2011, 2:29:37 AM5/30/11
to sh...@googlegroups.com


在 2011年5月27日 下午9:31,李晓岚 <leexi...@gmail.com>写道:
-4是重定位时链接器使用的addend。
请不要在邮件列表中使用这种彩色大字体,谢谢!
 
Reply all
Reply to author
Forward
0 new messages