使用GCC生成无格式二进制文件(plain binary files)

使用GCC生成无格式二进制文件(plain binary files

我在互联网上搜索很久,只找到一些零星的关于这方面的资料。我想使用gcc开发一个自己使用的专用工具,结合自己的工作经验,写了这篇总结性的资料。

1.         软硬件环境

l          至少一台正在使用的80x86系列的32-bit电脑,越高档越好。

l          一套Linux的发行版,如RedhatMandrakeTurboLinux等。

l          GNU GCC编译器。该编译器在Linux下很常用。

l          Linux上的binutils

l          自己熟悉的文本编辑器,如vi等。

如果你不具备这些条件,就不要再往下看了。我的工作环境是,在一台赛扬433上安装了Redhat Linux8.0128M内存,gcc是默认的,版本为3.2.2。可以使用如下命令查看gcc的版本:

gcc --version

 

2.         使用C语言生成一个二进制文件

使用自己喜欢的文本编辑器写一个test.c

int main()

{

}

再使用如下命令编译:

gcc –c test.c

ld –o test –Ttext 0x0 –e main test.o

objcopy –R .note –R .comment –S –O binary test test.bin

最后生成的二进制文件是test.bin,可以使用你喜欢的反汇编工具看看这个文件里到底是什么。我使用Linux下的objdump进行反汇编:

objdump –D –b binary –a i386 test.bin

结果如下:

00000000 <main>:

   0:          55                        push   %ebp

   1:          89 e5                      mov    %esp,%ebp

   3:          83 ec 08                    sub    $0x8,%esp

   6:          83 e4 f0                     and    $0xfffffff0,%esp

   9:          b8 00 00 00 00                mov    $0x0,%eax

   e: 29 c4                      sub    %eax,%esp

  10:          c9                        leave 

  11:          c3                        ret  

其中第一列是指令的内存地址;第二列是指令的机器码;第三列是汇编指令。相信你的结果与此同。如果你的gcc与我的不一样,例如2.7.x版本的gcc,你的结果很可能会有所不同,缺少如下的四条指令,这是正常的,这两个版本的gcc所使用的堆栈框架不同(下面介绍的例子也会因为编译器版本的不同造成其结果有别):

   3:          83 ec 08                    sub    $0x8,%esp

   6:          83 e4 f0                     and    $0xfffffff0,%esp #堆栈对齐,以16Bytes为单位分配局部变量空间

   9:          b8 00 00 00 00                mov    $0x0,%eax

   e: 29 c4                      sub    %eax,%esp

上述代码都是32-bit代码,你需要在像Linux这样的 32-bit环境下运行,并且是保护模式。也可以只用下面的指令直接生成test.bin

gcc –c test.c

ld –Ttext 0x0 –e main --oformat binary –o test.bin test.o

上面的test.c中只有一个函数,而且还只是个框架。其反汇编代码也没什么难理解的。

3.         编写带局部变量的程序

再创建一个新的test.c,看看gcc是如何处理局部变量的。

int main()

{

int i;

i=0x12345678;

}

使用上述两种方法的人一种编译,生成test.bin。然后使用objdump进行反汇编:

00000000 <main>:

   0:          55                        push   %ebp

   1:          89 e5                      mov    %esp,%ebp

   3:          83 ec 08                    sub    $0x8,%esp

   6:          83 e4 f0                     and    $0xfffffff0,%esp

   9:          b8 00 00 00 00                mov    $0x0,%eax

   e: 29 c4                      sub    %eax,%esp

  10:          c7 45 fc 78 56 34 12    movl   $0x12345678,0xfffffffc(%ebp)

  17:          c9                        leave 

  18:          c3                        ret

与第一个例子相比,开头的六条指令和最后的两条指令完全相同,仅有一条指令不同。这条语句是给局部变量赋值,其空间的分配在前面已经进行了。在gcc中,堆栈中的局部变量空间按16字节为单位进行分配,而不是通常的1字节为单位。如果将

int i;

i=0x12345678;

改为

int i=0x12345678;

其结果没有区别。但是,如果是全局变量,就不一样了。

4.         编写带全局变量的程序

test.c改为:

int i;

int main()

{

i=0x12345678;

}

使用同样的方法编译,然后再进行反汇编:

00000000 <main>:

   0:          55                        push   %ebp

   1:          89 e5                      mov    %esp,%ebp

   3:          83 ec 08                    sub    $0x8,%esp

   6:          83 e4 f0                     and    $0xfffffff0,%esp

   9:          b8 00 00 00 00                mov    $0x0,%eax

   e: 29 c4                      sub    %eax,%esp

  10:          c7 05 1c 10 00 00 78   movl   $0x12345678,0x101c

  17:          56 34 12

  1a: c9                        leave 

  1b:          c3                        ret   

我们定义的全局变量被放到了0x101c处,这是gcc默认以page-align对齐数据段的结果,此处的page与页式内存管理中的page没有关系。在使用ld链接时,使用-N参数可以关闭对齐效果。

00000000 <main>:

   0:          55                        push   %ebp

   1:          89 e5                      mov    %esp,%ebp

   3:          83 ec 08                    sub    $0x8,%esp

   6:          83 e4 f0                     and    $0xfffffff0,%esp

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值