如何将文本文件的内容添加为ELF文件中的一部分?

Dav*_*nes 7 x86 linker nasm elf objcopy

我有一个NASM程序集文件,我正在组装和链接(在Intel-64 Linux上).

有一个文本文件,我希望文本文件的内容出现在生成的二进制文件中(基本上是一个字符串).二进制文件是ELF可执行文件.

我的计划是在ELF文件中创建一个新的只读数据部分(相当于传统.rodata部分).

理想情况下,会有一个工具来逐字添加文件作为elf文件中的新部分,或者是一个逐字包含文件的链接器选项.

这可能吗?

Mic*_*tch 24

这是可能的,并且最容易使用BINUTILS中的OBJCOPY完成.您有效地将数据文件作为二进制输入,然后将其输出为可链接到您的程序的目标文件格式.

OBJCOPY甚至会生成一个开始和结束符号以及数据区域的大小,以便您可以在代码中引用它们.基本的想法是你要告诉它你的输入文件是二进制文件(即使它是文本); 您将瞄准x86-64目标文件; 指定输入文件名和输出文件名.

假设我们有一个myfile.txt使用内容调用的输入文件:

the
quick
brown
fox
jumps
over
the
lazy
dog
Run Code Online (Sandbox Code Playgroud)

这样的事情将是一个起点:

objcopy --input binary \
    --output elf64-x86-64 \
    --binary-architecture i386:x86-64 \
    myfile.txt myfile.o
Run Code Online (Sandbox Code Playgroud)

如果要生成32位对象,可以使用:

objcopy --input binary \
    --output elf32-i386 \
    --binary-architecture i386 \
    myfile.txt myfile.o
Run Code Online (Sandbox Code Playgroud)

输出将是一个名为的目标文件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
Run Code Online (Sandbox Code Playgroud)

默认情况下,它会创建一个.data包含文件内容的部分,并创建一些可用于引用数据的符号.

_binary_myfile_txt_start
_binary_myfile_txt_end
_binary_myfile_txt_size
Run Code Online (Sandbox Code Playgroud)

这实际上是起始字节的地址,结束字节以及从文件放入对象的数据的大小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
Run Code Online (Sandbox Code Playgroud)

您特别要求的.rodata部分也指定了READONLY标志.您可以使用该--rename-section选项可以更改.data.rodata,并指定需要的标志.您可以将其添加到命令行:

--rename-section .data=.rodata,CONTENTS,ALLOC,LOAD,READONLY,DATA
Run Code Online (Sandbox Code Playgroud)

当然,如果你想调用除了.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
Run Code Online (Sandbox Code Playgroud)

现在您有了一个目标文件,如何在C代码中使用它(作为示例).生成的符号有点不寻常,并且在OS Dev Wiki上有一个合理的解释:

一个常见问题是在尝试使用链接描述文件中定义的值时获取垃圾数据.这通常是因为他们取消引用该符号.链接描述文件中定义的符号(例如_ebss =.;)只是一个符号,而不是一个变量.如果使用extern uint32_t _ebss访问该符号; 然后尝试使用_ebss代码将尝试从_ebss指示的地址读取32位整数.

解决方法是将_ebss的地址作为&_ebss使用,或者将其定义为unsized数组(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;
}
Run Code Online (Sandbox Code Playgroud)

您可以编译和链接:

gcc -O3 main.c myfile.o
Run Code Online (Sandbox Code Playgroud)

输出应该类似于:

data_start 0x4006a2
data_end   0x4006ce
data_size  44
the
quick
brown
fox
jumps
over
the
lazy
dog
Run Code Online (Sandbox Code Playgroud)

NASM使用的例子是在性质上相似Ç代码.以下程序集程序nmain.asm使用Linux x86-64系统调用将相同的字符串写入标准输出:

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
Run Code Online (Sandbox Code Playgroud)

这可以组装和链接:

nasm -f elf64 -o nmain.o nmain.asm
gcc -m64 -nostdlib nmain.o myfile.o
Run Code Online (Sandbox Code Playgroud)

输出应显示为:

the
quick
brown
fox
jumps
over
the
lazy
dog
Run Code Online (Sandbox Code Playgroud)

  • 非常有教育意义,是下雨天要储存的“宝石”! (2认同)