介绍
我正在编译一个简单的汇编代码(Intel 语法、x86、Linux)打印“Hello World!”。这里是:
SECTION .rodata
msg: db 'Hello world!', 0xA
msg_len: equ $ - msg
SECTION .text
global _start
_start:
mov eax, 4 ; `write` system call
mov ebx, 1 ; to stdout
mov ecx, msg
mov edx, msg_len
int 0x80
mov eax, 1 ; `exit` system call
xor ebx, ebx ; exit code 0
int 0x80
我使用以下命令编译它:nasm -f elf32 -o hello_world.o hello_world.s
ld -m elf_i386 -o hello_world hello_world.o
代码运行良好,但我担心的是文件大小:-rwxrwxr-x 1 nikolay nikolay 8704 Apr 27 15:20 hello_world
-rw-rw-r-- 1 nikolay nikolay 243 Apr 26 22:16 hello_world.s
-rw-rw-r-- 1 nikolay nikolay 640 Apr 27 15:20 hello_world.o
问题目标文件比源代码稍大,但似乎合理,因为ELF文件中应该有一些元数据或其他东西,源代码不包含,对吧?但是可执行文件甚至比目标文件还要大 10 倍以上!
此外,目标文件中有一些零字节,但我不会说它们太多。但是,可执行文件中有很多零(请参阅
Additional info
部分中两者的屏幕截图)。调查
我曾尝试阅读一些关于 ELF 的文章,包括维基百科和手册页。我没有仔细阅读所有内容,所以我可能漏掉了一些内容,但我发现有用的是
dumpelf
实用程序(来自 pax-utils
包,可通过 apt
安装),我使用它转储了我的 elf 文件并发现了一些可能是这些零流的原因:在可执行文件的所有三个头文件中,都有
p_align
字段集:.p_align = 4096 , /* (min mem alignment in bytes) */
这应该意味着每个部分都应该用零字节填充,以便其长度是 4096 的倍数。并且因为以下每个部分的大小相对较小,所以要添加很多零字节,那就是这些零来自哪里。问题)
所以,我想知道:
我还注意到前三个部分(
''
、 '.rodata'
、 '.text'
)从 0
开始, 4096
和 8192
分别,但以下( '.symtab'
, '.strtab'
, '.shstrtab'
)似乎不再对齐:它们开始于 8208
, 8368
和 8422
... 为什么?这里发生了什么?p_vaddr
和 p_paddr
字段设置为前三个部分开始的地址,那么如果我们已经知道 对齐部分的原因是什么?精确来自标题的部分的地址?它与内存页(在我的机器上大小为 4KiB)有关吗?--nmagic
ld
中的参数的手册,它完全禁用了对齐(并且,万岁!,可执行文件没有与目标文件的大小相同),但我猜对齐是故意存在的,所以也许我只需要减少值,以便它更适合我的情况?如果您知道我遗漏了什么,我非常感谢您回答这些问题中的任何一个或任何其他细节。也请告诉我我是否在任何地方错了。先感谢您!
附加信息
我的目标文件的转储(带有
xxd hello_world.o | grep -E '0000|$' --color=always | less -R
):我的可执行文件转储的一部分(使用类似于上面的命令):
一个新的部分从地址 0x1000 开始
dumpelf hello_world.o
的输出:#include <elf.h>
/*
* ELF dump of 'hello_world.o'
* 640 (0x280) bytes
*/
Elf32_Dyn dumpedelf_dyn_0[];
struct {
Elf32_Ehdr ehdr;
Elf32_Phdr phdrs[0];
Elf32_Shdr shdrs[7];
Elf32_Dyn *dyns;
} dumpedelf_0 = {
.ehdr = {
.e_ident = { /* (EI_NIDENT bytes) */
/* [0] EI_MAG: */ 0x7F,'E','L','F',
/* [4] EI_CLASS: */ 1 , /* (ELFCLASS32) */
/* [5] EI_DATA: */ 1 , /* (ELFDATA2LSB) */
/* [6] EI_VERSION: */ 1 , /* (EV_CURRENT) */
/* [7] EI_OSABI: */ 0 , /* (ELFOSABI_NONE) */
/* [8] EI_ABIVERSION: */ 0 ,
/* [9-15] EI_PAD: */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
},
.e_type = 1 , /* (ET_REL) */
.e_machine = 3 , /* (EM_386) */
.e_version = 1 , /* (EV_CURRENT) */
.e_entry = 0x0 , /* (start address at runtime) */
.e_phoff = 0 , /* (bytes into file) */
.e_shoff = 64 , /* (bytes into file) */
.e_flags = 0x0 ,
.e_ehsize = 52 , /* (bytes) */
.e_phentsize = 0 , /* (bytes) */
.e_phnum = 0 , /* (program headers) */
.e_shentsize = 40 , /* (bytes) */
.e_shnum = 7 , /* (section headers) */
.e_shstrndx = 3
},
.phdrs = {
/* no program headers ! */ },
.shdrs = {
/* Section Header #0 '' 0x40 */
{
.sh_name = 0 ,
.sh_type = 0 , /* [SHT_NULL] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 0 , /* (bytes) */
.sh_size = 0 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 0 ,
.sh_entsize = 0
},
/* Section Header #1 '.rodata' 0x68 */
{
.sh_name = 1 ,
.sh_type = 1 , /* [SHT_PROGBITS] */
.sh_flags = 2 ,
.sh_addr = 0x0 ,
.sh_offset = 352 , /* (bytes) */
.sh_size = 13 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 4 ,
.sh_entsize = 0
},
/* Section Header #2 '.text' 0x90 */
{
.sh_name = 9 ,
.sh_type = 1 , /* [SHT_PROGBITS] */
.sh_flags = 6 ,
.sh_addr = 0x0 ,
.sh_offset = 368 , /* (bytes) */
.sh_size = 31 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 16 ,
.sh_entsize = 0
},
/* Section Header #3 '.shstrtab' 0xB8 */
{
.sh_name = 15 ,
.sh_type = 3 , /* [SHT_STRTAB] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 400 , /* (bytes) */
.sh_size = 51 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 1 ,
.sh_entsize = 0
},
/* Section Header #4 '.symtab' 0xE0 */
{
.sh_name = 25 ,
.sh_type = 2 , /* [SHT_SYMTAB] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 464 , /* (bytes) */
.sh_size = 112 , /* (bytes) */
.sh_link = 5 ,
.sh_info = 6 ,
.sh_addralign = 4 ,
.sh_entsize = 16
},
/* Section Header #5 '.strtab' 0x108 */
{
.sh_name = 33 ,
.sh_type = 3 , /* [SHT_STRTAB] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 576 , /* (bytes) */
.sh_size = 37 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 1 ,
.sh_entsize = 0
},
/* Section Header #6 '.rel.text' 0x130 */
{
.sh_name = 41 ,
.sh_type = 9 , /* [SHT_REL] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 624 , /* (bytes) */
.sh_size = 8 , /* (bytes) */
.sh_link = 4 ,
.sh_info = 2 ,
.sh_addralign = 4 ,
.sh_entsize = 8
},
},
.dyns = dumpedelf_dyn_0,
};
Elf32_Dyn dumpedelf_dyn_0[] = {
/* no dynamic tags ! */ };
dumpelf hello_world
的输出:#include <elf.h>
/*
* ELF dump of 'hello_world'
* 8704 (0x2200) bytes
*/
Elf32_Dyn dumpedelf_dyn_0[];
struct {
Elf32_Ehdr ehdr;
Elf32_Phdr phdrs[3];
Elf32_Shdr shdrs[6];
Elf32_Dyn *dyns;
} dumpedelf_0 = {
.ehdr = {
.e_ident = { /* (EI_NIDENT bytes) */
/* [0] EI_MAG: */ 0x7F,'E','L','F',
/* [4] EI_CLASS: */ 1 , /* (ELFCLASS32) */
/* [5] EI_DATA: */ 1 , /* (ELFDATA2LSB) */
/* [6] EI_VERSION: */ 1 , /* (EV_CURRENT) */
/* [7] EI_OSABI: */ 0 , /* (ELFOSABI_NONE) */
/* [8] EI_ABIVERSION: */ 0 ,
/* [9-15] EI_PAD: */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
},
.e_type = 2 , /* (ET_EXEC) */
.e_machine = 3 , /* (EM_386) */
.e_version = 1 , /* (EV_CURRENT) */
.e_entry = 0x8049000 , /* (start address at runtime) */
.e_phoff = 52 , /* (bytes into file) */
.e_shoff = 8464 , /* (bytes into file) */
.e_flags = 0x0 ,
.e_ehsize = 52 , /* (bytes) */
.e_phentsize = 32 , /* (bytes) */
.e_phnum = 3 , /* (program headers) */
.e_shentsize = 40 , /* (bytes) */
.e_shnum = 6 , /* (section headers) */
.e_shstrndx = 5
},
.phdrs = {
/* Program Header #0 0x34 */
{
.p_type = 1 , /* [PT_LOAD] */
.p_offset = 0 , /* (bytes into file) */
.p_vaddr = 0x8048000 , /* (virtual addr at runtime) */
.p_paddr = 0x8048000 , /* (physical addr at runtime) */
.p_filesz = 148 , /* (bytes in file) */
.p_memsz = 148 , /* (bytes in mem at runtime) */
.p_flags = 0x4 , /* PF_R */
.p_align = 4096 , /* (min mem alignment in bytes) */
},
/* Program Header #1 0x54 */
{
.p_type = 1 , /* [PT_LOAD] */
.p_offset = 4096 , /* (bytes into file) */
.p_vaddr = 0x8049000 , /* (virtual addr at runtime) */
.p_paddr = 0x8049000 , /* (physical addr at runtime) */
.p_filesz = 31 , /* (bytes in file) */
.p_memsz = 31 , /* (bytes in mem at runtime) */
.p_flags = 0x5 , /* PF_R | PF_X */
.p_align = 4096 , /* (min mem alignment in bytes) */
},
/* Program Header #2 0x74 */
{
.p_type = 1 , /* [PT_LOAD] */
.p_offset = 8192 , /* (bytes into file) */
.p_vaddr = 0x804A000 , /* (virtual addr at runtime) */
.p_paddr = 0x804A000 , /* (physical addr at runtime) */
.p_filesz = 13 , /* (bytes in file) */
.p_memsz = 13 , /* (bytes in mem at runtime) */
.p_flags = 0x4 , /* PF_R */
.p_align = 4096 , /* (min mem alignment in bytes) */
},
},
.shdrs = {
/* Section Header #0 '' 0x2110 */
{
.sh_name = 0 ,
.sh_type = 0 , /* [SHT_NULL] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 0 , /* (bytes) */
.sh_size = 0 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 0 ,
.sh_entsize = 0
},
/* Section Header #1 '.text' 0x2138 */
{
.sh_name = 27 ,
.sh_type = 1 , /* [SHT_PROGBITS] */
.sh_flags = 6 ,
.sh_addr = 0x8049000 ,
.sh_offset = 4096 , /* (bytes) */
.sh_size = 31 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 16 ,
.sh_entsize = 0
},
/* Section Header #2 '.rodata' 0x2160 */
{
.sh_name = 33 ,
.sh_type = 1 , /* [SHT_PROGBITS] */
.sh_flags = 2 ,
.sh_addr = 0x804A000 ,
.sh_offset = 8192 , /* (bytes) */
.sh_size = 13 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 4 ,
.sh_entsize = 0
},
/* Section Header #3 '.symtab' 0x2188 */
{
.sh_name = 1 ,
.sh_type = 2 , /* [SHT_SYMTAB] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 8208 , /* (bytes) */
.sh_size = 160 , /* (bytes) */
.sh_link = 4 ,
.sh_info = 6 ,
.sh_addralign = 4 ,
.sh_entsize = 16
},
/* Section Header #4 '.strtab' 0x21B0 */
{
.sh_name = 9 ,
.sh_type = 3 , /* [SHT_STRTAB] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 8368 , /* (bytes) */
.sh_size = 54 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 1 ,
.sh_entsize = 0
},
/* Section Header #5 '.shstrtab' 0x21D8 */
{
.sh_name = 17 ,
.sh_type = 3 , /* [SHT_STRTAB] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 8422 , /* (bytes) */
.sh_size = 41 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 1 ,
.sh_entsize = 0
},
},
.dyns = dumpedelf_dyn_0,
};
Elf32_Dyn dumpedelf_dyn_0[] = {
/* no dynamic tags ! */ };
最佳答案
对齐是 4096 字节,这是此架构上的页面大小。这并非巧合,因为 man page说到 nmagic:“关闭部分的页面对齐”。
通过正常(非 nmagic)二进制文件的大小,您可以猜测链接器布局了三个页面,大概具有不同的访问权限(代码 = 不可写,数据 = 不可执行,rodata = 只读),这些权限只能设置为-页。运行时,磁盘布局与 RAM 中的布局相匹配。
这对于需求分页很重要。程序启动时,整个可执行文件基本上都被mmaped了,并根据需要通过页面错误从磁盘加载页面。此外,页面可以在其其他运行实例之间共享(这对于动态库更重要),并且可以在需要时由于内存压力而从 RAM 中删除。
nmagic 可执行文件在运行时仍会加载到三个页面中,但由于那些不再与磁盘上的内容匹配,因此它不是按需分页的。我不建议在更大的东西上使用该选项。
注意:如果您制作了一个运行时间更长的可执行文件(可能添加了对输入的读取),您可以通过查看/proc/[pid]/maps 和 smaps 来检查正在运行的进程的内存布局详细信息。
关于linux - ELF 可执行文件 : many zero bytes,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67288459/