创建Linux下可运行的超小型ELF可执行文件(3)

p { margin-bottom: 0.21cm; }

我们的程序本身只有 7 字节,难道 ELF 真的需要 361 字节的额外空间吗?

我们用 objdump 来看一下文件内容:

$ objdump -x a.out | less

让我们看看块列表:

Sections:

Idx Name Size VMA LMA File off Algn

0 .text 00000007 08048080 08048080 00000080 2**4

CONTENTS, ALLOC, LOAD, READONLY, CODE

1 .comment 0000001c 00000000 00000000 00000087 2**0

CONTENTS, READONLY

完整的 .text 节显示是 7 字节,这说明完全控制我们程序的机器码很安全。

但是 .comment 节是做什么用的呢?它有 28 字节!我们并不确定 .comment 节是做什么用的,但是看起来它并不是必要的代码。。。

我们可以看看 .comment 节到底存储了什么内容,在文件偏移 0x00000087 处,使用 hexdump 查看:

00000080: 31C0 40B3 2ACD 8000 5468 6520 4E65 7477 1.@.*...The Netw

00000090: 6964 6520 4173 7365 6D62 6C65 7220 302E ide Assembler 0.

000000A0: 3938 0000 2E73 796D 7461 6200 2E73 7472 98...symtab..str

谁能想到 nasm 会暗中做这些事?也许我们该换用 gas AT&T

; tiny.s

.globl _start

.text

_start:

xorl %eax, %eax

incl %eax

movb $42, %bl

int $0x80

 

$ gcc -s -nostdlib tiny.s

$ ./a.out ; echo $?

42

$ wc -c a.out

368 a.out

一样的结果!

但是,通过 objdump 可以发现文件内容不同了:

Sections:

Idx Name Size VMA LMA File off Algn

0 .text 00000007 08048074 08048074 00000074 2**2

CONTENTS, ALLOC, LOAD, READONLY, CODE

1 .data 00000000 0804907c 0804907c 0000007c 2**2

CONTENTS, ALLOC, LOAD, DATA

2 .bss 00000000 0804907c 0804907c 0000007c 2**2

ALLOC

又多了两节,虽然他们的长度为零,但是仍然导致了额外的空间。

对于这些额外消耗,我们怎样避免呢?

要回答这个问题,我们需要理解 ELF 文件格式。

关于 intel-386 架构下 ELF 文件格式的正式描述可以参考 http://refspecs.freestandards.org/elf/elf.pdf 。你也可以参考普通文本格式的 1.0 http://www.muppetlabs.com/~breadbox/software/ELF.txt 。这些规范覆盖了很多方面,所以如果你不想自己读完全部的文档,我也可以理解。基本上,我们需要懂得如下内容就可以了:

 

每一个 ELF 文件都是以一个叫做 ELF header 的结构体开始的。这个结构体有 52 字节长,它包含一些描述文件内容的信息。比如,第一个 16 字节包含文件的魔数签名 (7F 45 4C 46) ,还有 1 字节的标志位表明是 32 位还是 64 位,大尾还是小尾编码,等等。 ELF header 里的其他字段包含信息比如目标处理器架构,文件类型喂可执行,目标文件还是共享目标文件,程序的起始地址, program header table section header table 的位置。

 

Program header table section header table 能够出现在文件的任何位置,但是前者一般紧跟在 ELF header 之后,后者一般出现在文件末尾或者接近末尾的地方。这两个表完成相似的目的,都是为了标示文件中的组件块。尽管如此, Section header table 倾向于标示程序中各种块在文件中的位置,而 program header table 描述这些块怎样被加载到内存中的什么位置。简单地讲, section header table 对编译器和链接器有用,而 program header table 对程序加载器有用。 Program header table 对于目标文件来说是可选的,在实际应用中从来不出现。同样地, section header table 对于可执行文件是可选的,但是在实际应用中总是出现。

 

那么,这就是我们第一个问题的答案。程序中的一些过度的空间消耗用于完全没必要的 section header table ,和一些同样对程序的内存映像毫无帮助的没有用的块。

 

接下来,我们转向第二个问题:怎样除掉这些无用块呢?

 

没有标准的工具用于制作不包含 section header table 之类的可执行文件。如果想做这样的事,那只能靠我们自己了。

 

但是那并不意味着我们必须打开二进制编辑器手写十六进制代码。 nasm 有一种普通二进制文本输出格式,那正好适合我们用。我们所需要的全部信息就是一个空 ELF 可执行文件的内存映像,然后把我们的程序填进去。

 

我们能够看 ELF 规范,和 /usr/include/linux/elf.h ,并参考标准工具创建的可执行文件,来推断一个空的 ELF 可执行文件应该是什么样。但是,如果你不是很有耐心,那么可以用下面提供的:

BITS 32

 

org 0x08048000

 

ehdr: ; Elf32_Ehdr

db 0x7F, "ELF", 1, 1, 1, 0 ; e_ident

times 8 db 0

dw 2 ; e_type

dw 3 ; e_machine

dd 1 ; e_version

dd _start ; e_entry

dd phdr - $$ ; e_phoff

dd 0 ; e_shoff

dd 0 ; e_flags

dw ehdrsize ; e_ehsize

dw phdrsize ; e_phentsize

dw 1 ; e_phnum

dw 0 ; e_shentsize

dw 0 ; e_shnum

dw 0 ; e_shstrndx

 

ehdrsize equ $ - ehdr

 

phdr: ; Elf32_Phdr

dd 1 ; p_type

dd 0 ; p_offset

dd $$ ; p_vaddr

dd $$ ; p_paddr

dd filesize ; p_filesz

dd filesize ; p_memsz

dd 5 ; p_flags

dd 0x1000 ; p_align

 

phdrsize equ $ - phdr

 

_start:

 

; your program here

 

filesize equ $ - $$

这个映像包含一个 ELF header ,标示文件是 intel 386 可执行文件,没有 section header program header table 包含一个元素。前文中讲过 program header 引导程序加载器把整个文件加载到内存映像中地址 0x08048000 处,这是默认的可执行文件的加载地址。接着就开始执行 _start 处的代码,它紧随 program header table 之后。没有 .data 段,没有 .bss 段,没有注释,除了真正必须的信息。

 

我们的小程序现在变成:

; tiny.asm

org 0x08048000

 

;

; (as above)

;

 

_start:

mov bl, 42

xor eax, eax

inc eax

int 0x80

 

filesize equ $ - $$

试一下:

$ nasm -f bin -o a.out tiny.asm

$ chmod +x a.out

$ ./a.out ; echo $?

42

$ wc -c a.out

91 a.out

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值