首页
社区
课程
招聘
[旧帖] [翻译]42字节可执行文件;ELF介绍;求Kx 0.00雪花
发表于: 2012-9-22 11:42 1114

[旧帖] [翻译]42字节可执行文件;ELF介绍;求Kx 0.00雪花

2012-9-22 11:42
1114
Size is Everything!

本文介绍了给程序瘦身的过程,同时也介绍ELF文件格式。本文的例子运行在386架构,Linux系统。为了展示这个过程,我们需要一个小程序。小程序如下:

       /*tiny.c*/
       int main(void) {return 42;}

编译执行 $gcc -Wall tiny.c
             $./a.out ; echo $?

a.out这个可执行文件的大小: $wc -c a.out 
                                          3998 a.out
(不同平台可能有变化)
看起来并不大,但是还是有很多水分可以挤。

第一步,试一下编译时优化。
$ gcc -Wall -s -03 tiny.c
$ wc -c a.out
    2616 a.out

(似乎没有-o3结果是一样的,还没弄明白gcc的优化原理是啥)
看起来没什么可优化的;

2. 第二步,在即使只有一行的C程序中,也都没有啥可压缩的,使用汇编,避免C自动带来的开销。让程序返回42,也即需要将累加寄存器,eax,赋值为42,然后返回。
; tiny.asm
BITS 32
GLOBAL main
SECTION .text
main:
            mov eax, 42;
            ret

编译如下:
$ nasm -f elf tiny.asm
$gcc -s tiny.o
$./a.out;echo $?

结果正确,大小 2604
结果影响不大,原因在于main()接口带来的开销,链接器仍然会添加与OS的接口,正是这个接口调用了main()。如何去掉这个接口呢?

如果不使用main,而自定义_start如何?
;tiny.asm
BITS 32
GLOBAL _start
SECTION .text
_start:
           mov eax,42
           ret

GCC编译:
$nasm -f elf tiny.asm
$gcc -s tiny.o
报错如下:
tiny.o(.text+0x0): multiple definition of `_start'
  /usr/lib/crt1.o(.text+0x0): first defined here
  /usr/lib/crt1.o(.text+0x36): undefined reference to `main'

GCC手册告诉我们,如果使用参数-nostartfiles,则链接过程中不用标准系统入口,其它正常。

那么编译如下:
$nasm -f elf tiny.asm
$gcc -s -nostartfiles tiny.o
$.a.out; echo $?
Segmentation fault
  139

GCC没有报错,但是程序出错了。为什么呢?

原因在于我们把_start作为一个C程序,并希望从它返回。但是,它并不是一个程序,它只是一个符号用来告诉链接器程序的入口地址。如果我们查看的话,会发现栈顶的位置是1,当然不是一个地址。实际上,它是我们的argc的值。栈顶没有返回地址。
那么如何从_start返回呢?调用exit()函数!

准确地说,调用的是_exit()函数。因为,exit()另外还有其他一些功能,但因为我们希望绕开库函数启动代码,我们也得避免库的退出代码,直接使用OS的退出处理方式。

版本三:调用_exit()。_exit()接收一个整数值作为参数,我们需要将42压入栈,然后调用。

;tiny.asm
BITS 32
EXTERN _exit
GLOBAL _start
SECTION .text
_start:
         push dword 42
         call _exit

编译如下:
$nasm -f elf tiny.asm
$gcc -s -nostartfiles tiny.o
$./a.out;echo $?

成功;大小1340;缩小了一半。

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//