【问题标题】:How do I add contents of text file as a section in an ELF file?如何将文本文件的内容添加为 ELF 文件中的一个部分?
【发布时间】:2017-07-03 06:36:40
【问题描述】:

我有一个 NASM 汇编文件,我正在汇编和链接(在 Intel-64 Linux 上)。

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

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

理想情况下,应该有一个工具可以将文件逐字添加为 elf 文件中的新部分,或者有一个链接器选项来逐字包含文件。

这可能吗?

【问题讨论】:

    标签: linker x86 nasm elf objcopy


    【解决方案1】:

    这可以使用 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 的目标文件。如果我们使用 OBJDUMPobjdump -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,因为数组必须显式取消引用)

    记住这一点,我们可以创建这个名为main.cC文件:

    #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 代码。以下名为nmain.asm 的汇编程序使用Linux x86-64 System Calls 将相同的字符串写入标准输出:

    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
    

    【讨论】:

    • 非常有教育意义,是为下雨天储存的“宝石”!
    • 我不明白为什么 _size 参数出来这么大,按照这个方法我原来的 .dat 文件是 973 字节,objcopy o 文件是 1584 字节,(size_t)_binary_myfile_txt_size 是 94570554139597 :\ (_end - _start) 是 973 的正确大小。我误会了什么?
    • @ThorSummoner :你能把你的整个 C 代码放在一个 pastebin 里吗? pastebin.com
    猜你喜欢
    • 2010-11-08
    • 2014-05-25
    • 1970-01-01
    • 2012-05-12
    • 1970-01-01
    • 2013-11-09
    • 1970-01-01
    • 2021-12-10
    • 1970-01-01
    相关资源
    最近更新 更多