我有一个正在组装和链接的 NASM 程序集文件(在 Intel-64 Linux 上)。
有一个文本文件,我希望文本文件的内容出现在生成的二进制文件中(基本上是一个字符串)。该二进制文件是 ELF 可执行文件。
我的计划是在 ELF 文件中创建一个新的只读数据部分(相当于常规.rodata
部分)。
理想情况下,会有一个工具可以将文件逐字添加为 elf 文件中的新部分,或者有一个链接器选项来逐字包含文件。
这可能吗?
使用BINUTILS中的OBJCOPY可以做到这一点并且最容易做到这一点。您有效地将数据文件作为二进制输入,然后将其输出为可以链接到您的程序的目标文件格式。
OBJCOPY甚至会生成一个开始和结束符号以及数据区域的大小,以便您可以在代码中引用它们。基本的想法是你会想要告诉它你的输入文件是二进制的(即使它是文本);您的目标是 x86-64 目标文件;指定输入文件名和输出文件名。
假设我们有一个使用内容调用的输入文件myfile.txt
:
the
quick
brown
fox
jumps
over
the
lazy
dog
这样的事情将是一个起点:
objcopy --input binary \
--output elf64-x86-64 \
--binary-architecture i386:x86-64 \
myfile.txt myfile.o
如果您想生成 32 位对象,您可以使用:
objcopy --input binary \
--output elf32-i386 \
--binary-architecture i386 \
myfile.txt myfile.o
输出将是一个名为myfile.o
. 如果我们使用OBJDUMP和类似的命令查看目标文件的标题,objdump -x myfile.o
我们会看到如下内容:
myfile.o: file format elf64-x86-64
myfile.o
architecture: i386:x86-64, flags 0x00000010:
HAS_SYMS
start address 0x0000000000000000
Sections:
Idx Name Size VMA LMA File off Algn
0 .data 0000002c 0000000000000000 0000000000000000 00000040 2**0
CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 g .data 0000000000000000 _binary_myfile_txt_start
000000000000002c g .data 0000000000000000 _binary_myfile_txt_end
000000000000002c g *ABS* 0000000000000000 _binary_myfile_txt_size
默认情况下,它会创建一个.data
包含文件内容的部分,并创建许多可用于引用数据的符号。
_binary_myfile_txt_start
_binary_myfile_txt_end
_binary_myfile_txt_size
这实际上是开始字节的地址、结束字节以及从文件放入对象的数据的大小myfile.txt
。OBJCOPY将基于输入文件名的符号。myfile.txt
被破坏myfile_txt
并用于创建符号。
一个问题是.data
创建了一个读/写/数据部分,如下所示:
Idx Name Size VMA LMA File off Algn
0 .data 0000002c 0000000000000000 0000000000000000 00000040 2**0
CONTENTS, ALLOC, LOAD, DATA
您特别要求一个.rodata
部分也将指定READONLY标志。您可以使用该--rename-section
选项更改.data
为.rodata
并指定所需的标志。您可以将其添加到命令行:
--rename-section .data=.rodata,CONTENTS,ALLOC,LOAD,READONLY,DATA
当然,如果您想调用该部分而不是.rodata
使用与只读部分相同的标志,您可以.rodata
在上面的行中将其更改为您要用于该部分的名称。
应该生成所需对象类型的命令的最终版本是:
objcopy --input binary \
--output elf64-x86-64 \
--binary-architecture i386:x86-64 \
--rename-section .data=.rodata,CONTENTS,ALLOC,LOAD,READONLY,DATA \
myfile.txt myfile.o
既然您有一个目标文件,那么您如何在C代码中使用它(例如)。生成的符号有点不寻常,OS Dev Wiki上有一个合理的解释:
一个常见问题是在尝试使用链接描述文件中定义的值时获取垃圾数据。这通常是因为他们取消了对符号的引用。链接描述文件中定义的符号(例如_ebss = .;)只是一个符号,而不是一个变量。如果您使用 extern uint32_t _ebss 访问符号;然后尝试使用_ebss 代码将尝试从_ebss 指示的地址读取一个32 位整数。
解决这个问题的方法是获取 _ebss 的地址,要么将其用作 &_ebss,要么将其定义为未调整大小的数组 (extern char _ebss[];) 并转换为整数。(数组表示法可防止从 _ebss 意外读取,因为必须显式取消引用数组)
记住这一点,我们可以创建这个C文件,名为main.c
:
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
/* These are external references to the symbols created by OBJCOPY */
extern char _binary_myfile_txt_start[];
extern char _binary_myfile_txt_end[];
extern char _binary_myfile_txt_size[];
int main()
{
char *data_start = _binary_myfile_txt_start;
char *data_end = _binary_myfile_txt_end;
size_t data_size = (size_t)_binary_myfile_txt_size;
/* Print out the pointers and size */
printf ("data_start %p\n", data_start);
printf ("data_end %p\n", data_end);
printf ("data_size %zu\n", data_size);
/* Print out each byte until we reach the end */
while (data_start < data_end)
printf ("%c", *data_start++);
return 0;
}
您可以编译和链接:
gcc -O3 main.c myfile.o
输出应该类似于:
data_start 0x4006a2
data_end 0x4006ce
data_size 44
the
quick
brown
fox
jumps
over
the
lazy
dog
NASM的使用示例在本质上类似于C代码。下面的汇编程序使用Linux x86-64 System Callsnmain.asm
将相同的字符串写入标准输出:
bits 64
global _start
extern _binary_myfile_txt_start
extern _binary_myfile_txt_end
extern _binary_myfile_txt_size
section .text
_start:
mov eax, 1 ; SYS_Write system call
mov edi, eax ; Standard output FD = 1
mov rsi, _binary_myfile_txt_start ; Address to start of string
mov rdx, _binary_myfile_txt_size ; Length of string
syscall
xor edi, edi ; Return value = 0
mov eax, 60 ; SYS_Exit system call
syscall
这可以组装和链接:
nasm -f elf64 -o nmain.o nmain.asm
gcc -m64 -nostdlib nmain.o myfile.o
输出应显示为:
the
quick
brown
fox
jumps
over
the
lazy
dog