objcopy将目录路径名添加到符号名称前

13

我试图使用objcopy将文本文件的二进制形式包含到可执行文件中。(在运行时,我需要将该文件作为字符串使用)。这很好用,直到链接器需要从符号名称找到引用。问题在于,objcopy在符号名称前面加上了文件的路径名。由于我正在使用GNU Autotools来发布软件包,这个预先添加的路径名会发生更改,因此我不知道在C/C++程序中使用哪个外部链接器符号。

nm libtest.a |grep textfile
textfile.o:
00001d21 D _binary__home_git_textfile_end
00001d21 A _binary__home_git_textfile_size
00000000 D _binary__home_git_textfile_start

libtest.a是使用以下代码生成的(从Makefile.am中提取):

SUFFIXES = .txt
.txt.$(OBJEXT):
    objcopy --input binary --output elf32-i386 --binary-architecture i386 $< $@

我该如何告诉objcopy仅使用文件名的主干作为链接符号?或者是否有其他解决方法?

5个回答

11

使用.incbin汇编指令支持将原始数据包含到ELF中的通用方法。

关键是创建一个模板.S文件,它可能看起来像这样:

        .global foo_start
foo_start:
        .incbin "foo.raw"

        .global foo_end
foo_end:    
这个文件通过cpp进行预处理,所以我们不必在那里硬编码文件名,例如,我们可以这样写:

这个文件通过cpp进行预处理,所以我们不必在那里硬编码文件名,例如,我们可以这样写:

        .incbin __raw_file_path__

...然后在编译时传递它:

gcc -D__raw_file_path__='"data/foo.png"' foo.S -c -o data/foo.o

最后,当我们准备自己的.S文件时,可以添加一些额外的数据和/或信息。如果您包括原始的“文本文件”并希望将其作为C字符串可用,则可以在原始数据后面添加'0'字节:

        .global foo_start
foo_start:
        .incbin "foo.raw"

        .global foo_end
foo_end:    
        .byte 0

        .global foo_size
foo_size:
        .int foo_end - foo_start
如果您想要完全的灵活性,您当然可以手动预处理文件以修改其中的任何部分,例如:
.global @sym@_start
@sym@_start:
       .incbin "@file@"
       .global @sym@_end
@sym@_end:

...然后编译它:

sed -e "s,@sym@,passwd,g" -e "s,@file@,/etc/passwd," <foo.S.in | gcc -x assembler-with-cpp - -o passwd.o -c

10
有点讽刺的是,您可以使用 objcopy 通过 --redefine-sym 选项来解决该问题,该选项允许重命名符号...

If I use objcopy to create an object file from a PNG in another directory:

$ objcopy -I binary -O elf64-x86-64 -B i386 --rename-section .data=.rodata,alloc,load,data,contents,readonly ../../resources/test.png test_png.o

The resulting object has the following symbols:

$readelf -s test_png.o -W

Symbol table '.symtab' contains 5 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
     2: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT    1 _binary_______resources_test_png_start
     3: 0000000000003aaa     0 NOTYPE  GLOBAL DEFAULT    1 _binary_______resources_test_png_end
     4: 0000000000003aaa     0 NOTYPE  GLOBAL DEFAULT  ABS _binary_______resources_test_png_size

These can then be renamed:

$objcopy --redefine-sym _binary_______resources_test_png_start=_binary_test_png_start test_png.o
$objcopy --redefine-sym _binary_______resources_test_png_size=_binary_test_png_size test_png.o
$objcopy --redefine-sym _binary_______resources_test_png_end=_binary_test_png_end test_png.o

Resulting in an object with the symbol names that objcopy would have generated if the PNG had been located in the current directory:

$readelf -s test_png.o -W

Symbol table '.symtab' contains 5 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
     2: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT    1 _binary_test_png_start
     3: 0000000000003aaa     0 NOTYPE  GLOBAL DEFAULT    1 _binary_test_png_end
     4: 0000000000003aaa     0 NOTYPE  GLOBAL DEFAULT  ABS _binary_test_png_size

1
提到--redefine-sym是一个好主意,但似乎不够:objcopy的调用者如何知道如何形成“原始”的符号名称?我注意到,如果objcopy的输入文件是像../../foo/bar.txt这样的东西,那么符号名称就会变成像_binary________foo_bar_txt_start这样可怕的东西。不得不编码将点、斜杠和可能的其他字符(哪些?)转换为下划线的逻辑似乎相当愚蠢。而且奇怪的是,objcopy的--wildcard选项可以帮助我们,但它似乎在--redefine-sym中没有任何效果(我想他们打算用于其他用途)。 - John Zwinck
3
你只需要重建非目录部分,然后使用objdump查找名称并检查哪个以所需名称结尾,最后使用该名称进行重命名。 - PlasmaHH
看代码,所有非字母数字字符都被转换为 _。因此,以下代码将文件名转换为 echo -n "$filename" | tr -c '[A-Za-z0-9]' '_'。在文件名前加上 _binary_,在后面添加 _start 和其他内容。 - Mitar

5

我曾经使用的另一种方法是先cd到源目录,然后再给objcopy提供源文件的基本名称。在bash中,可以这样做:

cd $(dirname $SOURCE)
objcopy ... $(basename $SOURCE) $TARGET

这样生成的符号总是_binary_file_name_xxx,不包括路径。


0

我必须使用cmake来完成这个任务,最后我使用了/dev/stdin作为输入来获取一致的符号名,并通过string(MAKE_C_IDENTIFIER ...)重新定义了这些符号,然后使用objcopy --redefine-sym命令在结果目标文件上进行操作。

最终得到的函数如下:

function(make_binary_object __file)
    get_filename_component(__file_name ${__file} NAME)
    set(__object ${CMAKE_CURRENT_BINARY_DIR}/${__file_name}.obj)
    string(MAKE_C_IDENTIFIER ${__file_name} __file_c_identifier)
    add_custom_command(OUTPUT ${__object}
        COMMAND ${CMAKE_OBJCOPY}
            --input-format binary
            --output-format elf64-x86-64
            --binary-architecture i386:x86-64
            /dev/stdin
            ${__object} < ${__file}
        COMMAND ${CMAKE_OBJCOPY}
            --redefine-sym _binary__dev_stdin_start=_binary_${__file_c_identifier}_start
            --redefine-sym _binary__dev_stdin_end=_binary_${__file_c_identifier}_end
            --redefine-sym _binary__dev_stdin_size=_binary_${__file_c_identifier}_size
            ${__object}
        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
        DEPENDS ${__file})
    set_source_files_properties(${__object} PROPERTIES EXTERNAL_OBJECT TRUE)
endfunction()

你可以像这样使用它:

make_binary_object(index.html)

add_executable(my_server
    server.c
    ${CMAKE_CURRENT_BINARY_DIR}/index.html.obj)

-2
一个简单的解决方案是将文本文件转换为可用于初始化字符数组的格式。因此,您可以得到“ABC012”的0x41、0x42、0x43、0x30、0x31、0x32。然后,您可以#include这个字节序列。您还可以转义所有非ASCII字符,而不是将所有内容转换为字节,以便大部分文本仍然可以在生成的包含文件中读取。

使用 stdinextern 避免存储源代码。 - Alex
@Alex 我不确定我理解你的意思。 - Alexey Frunze
使用-x<语言>-作为gcc/g++的输入。 - Alex
@Alex 那有什么帮助呢?抱歉,我没听懂你的意思。 - Alexey Frunze
2
这是一行关于编程的相关内容:xxd -i input.txt | sed 's/input_txt/test/' | gcc -c -xc - -o obj.o - Alex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接