I am tying to use objcopy
to include a binary form of a text file into an executable. (At runtime I need the file as a string). This works fine until the linker
Somewhat ironically you can use objcopy
to solve the problem via the --redefine-sym
option that allows renaming of symbols...
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
Another alternative which I have used is to cd
to the source directory and then give objcopy
the basename of the source. In bash
, this would be:
cd $(dirname $SOURCE)
objcopy ... $(basename $SOURCE) $TARGET
This way the symbols generated are always _binary_file_name_xxx
without the path.
Generic method of including raw data into ELF is supported by .incbin assembler directive.
The trick is to create template .S file that could look like this:
.global foo_start
foo_start:
.incbin "foo.raw"
.global foo_end
foo_end:
This file is preprocessed via cpp so we don't have to hardcode file name there, eg. we can write:
.incbin __raw_file_path__
... and then pass it while compiling:
gcc -D__raw_file_path__='"data/foo.png"' foo.S -c -o data/foo.o
Lastly, as we prepare .S file ourself we can add some extra data and/or information. If you include raw "text files" and want these to be available as C strings you can add '0' byte just after raw data:
.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
If you want full-blown flexibility, you can of course pre-process file manually to alter any part of it, eg.
.global @sym@_start
@sym@_start:
.incbin "@file@"
.global @sym@_end
@sym@_end:
... and then compile it:
sed -e "s,@sym@,passwd,g" -e "s,@file@,/etc/passwd," <foo.S.in | gcc -x assembler-with-cpp - -o passwd.o -c
One simple solution is to transform your text file into what could be used to initialize an array of char. So, you'd get 0x41,0x42,0x43,0x30,0x31,0x32 for "ABC012". You can then #include this sequence of bytes. You can also escape all non-ASCII chars instead of converting everything into bytes so that most of the text is still readable in the generated include file.
I had to do this with cmake, and I ended up using /dev/stdin as input to get consistent symbols name, then redefining the symbols thanks to string(MAKE_C_IDENTIFIER ...) And then use objcopy --redefine-sym on the resulting object file.
The resulting function is then :
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()
And you can use it like this:
make_binary_object(index.html)
add_executable(my_server
server.c
${CMAKE_CURRENT_BINARY_DIR}/index.html.obj)