现在的位置: 首页 > 综合 > 正文

全局变量

2018年02月24日 ⁄ 综合 ⁄ 共 2038字 ⁄ 字号 评论关闭

全局变量

实验品

  小白鼠(global.c):

#include <stdio.h>

int i = 1;

int main()
{
    ++i;

    printf("%d\n",i);
    return 0;
}

  i 的定义被放在了函数体的外边, i 就成为了全局变量,程序运行的结果我不关心,我关心的是 i 最后变成了什么。

反汇编

  这次悟空的火眼金睛也不给力了(后面我们会看到),我们得反汇编可执行文件才能看到最终结果:

[lqy@localhost temp]$ gcc -o global global.c
[lqy@localhost temp]$ objdump -s -d global > global.txt
[lqy@localhost temp]$ 
  • gcc -o global global.c 是编译 global.c 生成可执行文件 global
  • objdump -s -d global > global.txt 是反汇编 global
    • -s 参数可以将所有段的内容以十六进制的方式打印出来
    • -d 参数可以将所有包含指令的段反汇编
    • > global.txt 是将标准输出输出到 global.txt 文件(专业点的话,叫"输出重定向")

objdump 是 linux 下一款反汇编工具,能够反汇编目标文件、可执行文件

  这样操作后,global 文件的反汇编结果输出到了 global.txt 文件中,打开 global.txt(文件比较长,我的有 357 行),定位到 main 函数:

080483c4 <main>:
 80483c4:   55                      push   %ebp
 80483c5:   89 e5                   mov    %esp,%ebp
 80483c7:   83 e4 f0                and    $0xfffffff0,%esp
 80483ca:   83 ec 10                sub    $0x10,%esp
 80483cd:   a1 64 96 04 08          mov    0x8049664,%eax
 80483d2:   83 c0 01                add    $0x1,%eax
 80483d5:   a3 64 96 04 08          mov    %eax,0x8049664
 80483da:   8b 15 64 96 04 08       mov    0x8049664,%edx
 80483e0:   b8 c4 84 04 08          mov    $0x80484c4,%eax
 80483e5:   89 54 24 04             mov    %edx,0x4(%esp)
 80483e9:   89 04 24                mov    %eax,(%esp)
 80483ec:   e8 03 ff ff ff          call   80482f4 
 80483f1:   b8 00 00 00 00          mov    $0x0,%eax
 80483f6:   c9                      leave
 80483f7:   c3                      ret

  6~8行就是 ++i 的反汇编结果: 全局变量 i 最后变成了绝对地址为 0x8049664 的内存块(大小为4字节)。 注意这里的 0x8049664 不是指立即数 0x8049664,如果要表示值为 0x8049664 的立即数,应该写为 $0x8049664。

悟空的弱点

  通过火眼金睛:

gcc -S -o global.s global.c

  我们看到 ++i 的汇编形式是:

movl    i, %eax
addl    $1, %eax
movl    %eax, i

  悟空,你太让我们失望了!

目标文件中的全局变量

  目标文件也可以用 objdump 来反汇编:

[lqy@localhost temp]$ gcc -c -o global.o global.c
[lqy@localhost temp]$ objdump -s -d global.o > global.txt 
[lqy@localhost temp]$ 

  global.o 反汇编出来的 global.txt 就没那么长了,只有 35 行,其中 ++i 部分的结果是:

   9:   a1 00 00 00 00          mov    0x0,%eax
   e:   83 c0 01                add    $0x1,%eax
  11:   a3 00 00 00 00          mov    %eax,0x0

  怎么会这样?怎么不是 0x8049664 呢?这就是目标文件 和 可执行文件的区别了:全局变量在目标文件中只有一个冒牌地址,在链接后(各目标文件协商(为自己的全局变量争一块地盘)完毕)才填入最终的绝对地址。

目标文件和可执行文件

  目标文件是编译后的产物,已经完成了 C语言 到 机器码 的转变,但是部分机器码的操作数还需要在链接过程中修正。

  可执行文件是由多个目标文件和 C 运行库链接而成的, C 运行库提供了诸如 printf 等函数的实现。

  linux 下目标文件(默认扩展名是.o)和可执行文件都是 ELF 格式(文件内容按照一定格式进行组织)的二进制文件;类似的,Windows 下 VISUAL C++ 编译出来的目标文件(扩展名是.obj)采用 COFF 格式,而可执行文件(扩展名是.exe)采用 PE 格式, ELF 和 PE 都是从 COFF 发展而来的。

  因为 linux 下目标文件和可执行文件的内容格式是一样的,所以 objdump 既可以反汇编可执行文件也可以反汇编目标文件。

小结

  全局变量也变成无名无姓的内存地址了,而且还是常量!

  这次又介绍了一个工具:objdump。所有后面要用到的工具和使用方法都介绍完了:gcc、objdump, ……嗯……(好像还不够)……至少主要的都介绍完了O(∩_∩)O~……

抱歉!评论已关闭.