CMake: Embed ELF into executable - c++

I have a project that needs access to an ELF file embedded into the executable in a special section.
I was handcrafting Makefiles before and simply had a shell script where I used objcopy to copy the target I wanted to embed into an .o file, then link to this file in the executable.
# Create a new section and copy the binary there ($1=input $2=output name)
objcopy --input-target binary --output-target elf64-x86-64 \
--binary-architecture i386 $1 $2.o
Now I want to get rid of the custom Makefiles and use CMake to generate them. However, I don't see an easy way to link to such a file. I am able to create and add this file, but not to link against it:
# Invoke script to package module as a library
add_custom_command(OUTPUT ${PACKAGED_FILE}
COMMAND ./package.sh ${MODULE_FILE} ${PACKAGED_FILE}
WORKING_DIRECTORY ${MODULE_DIR}
DEPENDS ${MODULE_FILE}
COMMENT packaging file into ELF object
VERBATIM
)
add_custom_target(${PACKAGED_NAME} ALL DEPENDS ${PACKAGED_FILE})
I have tried to add it with:
target_link_libraries(binary ${PROJECT_BINARY_DIR}/${PACKAGED_FILE})
However, this fails because the file isn't there yet. It will be, but CMake doesn't know that. Adding the target name as a link library doesn't help either because it can't be found. Adding it as a also dependency doesn't help. Does anyone have an idea how this could be accomplished?

We are doing a similar thing in our project - the following part of our CMakeLists.txt does the trick:
set(PROJECT_EMBED_OBJ_FILES "")
set(PROJECT_EMBED_FILES "file1.elf" "file2.elf")
foreach(FILENAME ${PROJECT_EMBED_FILES})
get_filename_component(FILENAME_ONLY ${FILENAME} NAME)
get_filename_component(FILEPATH_ONLY ${FILENAME} PATH)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME_ONLY}.o
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/${FILEPATH_ONLY}
COMMAND ${CMAKE_OBJCOPY}
ARGS -I binary -O elf64-x86-64 -B i386 ${FILENAME_ONLY} ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME_ONLY}.o )
list(APPEND PROJECT_EMBED_OBJ_FILES ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME_ONLY}.o)
message(STATUS "Objcopy'ing ${FILENAME}")
endforeach(FILENAME)
And then in the call to add_executable:
add_executable(projectname ${PROJECT_SOURCES} ${PROJECT_EMBED_OBJ_FILES})

You may try
add_custom_command(TARGET $(PROJECT_NAME).elf
POST_BUILD
COMMAND ${CMAKE_OBJCOPY} ARGS -O binary ${PROJECT_NAME}.elf \
${PROJECT_NAME}.bin)
Put this after your add_executable().
The POST_BUILD means execute after build.

Related

cmake add_custom_command + Xcode: multi output = multi command invocation

I have custom command that generate several headers at once.
All works fine with make/ninja files generated by cmake.
But if I generate Xcode project via cmake -GXCode,
then instead of once gen.sh was invoked 10 times
and not only that, it also will be invoked every build,
even if timestamps of generated file are younger then gen_in.txt.
How can I fix this?
project(multi_output)
cmake_minimum_required(VERSION 3.17)
set(MANY_HEADERS test0.h test1.h test2.h test3.h test4.h test5.h test6.h test7.h test8.h test9.h)
add_custom_command(
OUTPUT ${MANY_HEADERS}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/gen.sh
DEPENDS gen_in.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_executable(foo main.cpp ${MANY_HEADERS})
where gen.sh is
#!/bin/sh
echo "gen.sh: start"
sleep 1
echo "gen.sh: hard work done"
for i in `seq 0 9`; do
cat gen_in.txt > test$i.h
sed -i bak s/placeholder/$i/g test$i.h
done
In OUTPUT option relative paths are treated as relative to the binary directory.
That is, your add_custom_command actually doesn't produce the files declared as OUTPUT.
This is why it is called more and more: the build tool finds out that an OUTPUT file does not exist, and runs the COMMAND for build that file. (Many build tool don't check whether the OUTPUT file is actually created.)
For files created in the source directory you need to specify their absolute path in OUTPUT option:
set(MANY_HEADERS test0.h test1.h test2.h test3.h test4.h test5.h test6.h test7.h test8.h test9.h)
# This will be a list of _absolute paths_ to the headers
set(MANY_HEADERS_ABS)
foreach(HEADER ${MANY_HEADERS})
list(APPEND MANY_HEADERS_ABS "${CMAKE_CURRENT_SOURCE_DIR}/${HEADER}")
endforeach()
add_custom_command(
OUTPUT ${MANY_HEADERS_ABS}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/gen.sh
DEPENDS gen_in.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
# In add_executable one could use relative paths too
add_executable(foo main.cpp ${MANY_HEADERS})
Note, that add_executable command may accept relative paths to the file both in source and binary trees.
CMake checks whether a file exists or generated (e.g. with add_custom_command) in the source tree, and if it is, the source path is used. Then similar checks are performed for binary tree. (And if this check fails, CMake will emit an error).
Actually, careful inspecting of the make output may give a hint, whether make rebuilds files in the source tree or in the build tree.
This is what is produced by the original code (remember: make is called from the build directory):
[ 33%] Generating test0.h, test1.h, test2.h, test3.h, test4.h, test5.h, test6.h, test7.h, test8.h, test9.h
And this is what is produced when use absolute paths to the source tree. (Out-of-source build, use build/ subdirectory for build.)
[ 33%] Generating ../test0.h, ../test1.h, ../test2.h, ../test3.h, ../test4.h, ../test5.h, ../test6.h, ../test7.h, ../test8.h, ../test9.h

ar command does not produce index when combining static libraries

I am trying to follow the answer given here to combine multiple static libraries into a single archive under MinGW64. Specifically, I use CMake and specify the following command:
add_custom_command(
OUTPUT ${COMBINED_CORE_LIB_NAME}
COMMAND ${AR} -crs ${COMBINED_CORE_LIB_NAME} ${CORE_LIB_TARGET_FILES}
DEPENDS ${DILIGENT_CORE_INSTALL_LIBS_LIST}
COMMENT "Combining core libraries..."
)
Also, following the recommendation from here, I do not use stock ar, but rather cross ar:
find_program(AR NAMES x86_64-w64-mingw32-gcc-ar)
However, no matter what I do, ar refuses to generate index and every time I am trying to link against the produced library, I get this error:
error adding symbols: Archive has no index; run ranlib to add one
Running ranlib as suggested either stock one or x86_64-w64-mingw32-gcc-ranlib makes no difference.
I spent 15 minutes to make this work with MSVC and lib.exe and have been struggling for 8 hours with MinGW. Any suggestion would be highly appreciated.
[Edit]: It turned out that this problem is not really specific to MinGW and also happens on Linux in a very similar fashion.
As it turned out, it is not possible to merge multiple .a files into a single .a library using ar utility: it refuses to generate index. A working solution is to unpack all object files from all static libraries and then repack them into a combined library.
Below is the CMake instructions I ended up with:
set(COMBINED_CORE_LIB_NAME "${CMAKE_STATIC_LIBRARY_PREFIX}DiligentCore${CMAKE_STATIC_LIBRARY_SUFFIX}")
foreach(CORE_LIB ${DILIGENT_CORE_INSTALL_LIBS_LIST})
list(APPEND CORE_LIB_TARGET_FILES $<TARGET_FILE:${CORE_LIB}>)
endforeach(CORE_LIB)
if(MSVC)
add_custom_command(
OUTPUT ${COMBINED_CORE_LIB_NAME}
COMMAND ${LIB_EXE} /OUT:${COMBINED_CORE_LIB_NAME} ${CORE_LIB_TARGET_FILES}
DEPENDS ${DILIGENT_CORE_INSTALL_LIBS_LIST}
COMMENT "Combining core libraries..."
)
add_custom_target(DiligentCore-static ALL DEPENDS ${COMBINED_CORE_LIB_NAME})
else()
if(PLATFORM_WIN32)
# do NOT use stock ar on MinGW
find_program(AR NAMES x86_64-w64-mingw32-gcc-ar)
else()
set(AR ${CMAKE_AR})
endif()
if(AR)
add_custom_command(
OUTPUT ${COMBINED_CORE_LIB_NAME}
# Delete all object files from current directory
COMMAND ${CMAKE_COMMAND} -E remove "*${CMAKE_C_OUTPUT_EXTENSION}"
DEPENDS ${DILIGENT_CORE_INSTALL_LIBS_LIST}
COMMENT "Combining core libraries..."
)
# Unpack all object files from all targets to current directory
foreach(CORE_LIB_TARGET ${CORE_LIB_TARGET_FILES})
add_custom_command(
OUTPUT ${COMBINED_CORE_LIB_NAME}
COMMAND ${AR} -x ${CORE_LIB_TARGET}
APPEND
)
endforeach()
# Pack object files to a combined library and delete them
add_custom_command(
OUTPUT ${COMBINED_CORE_LIB_NAME}
COMMAND ${AR} -crs ${COMBINED_CORE_LIB_NAME} "*${CMAKE_C_OUTPUT_EXTENSION}"
COMMAND ${CMAKE_COMMAND} -E remove "*${CMAKE_C_OUTPUT_EXTENSION}"
APPEND
)
add_custom_target(DiligentCore-static ALL DEPENDS ${COMBINED_CORE_LIB_NAME})
else()
message("ar command is not found")
endif()
endif()
if(TARGET DiligentCore-static)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${COMBINED_CORE_LIB_NAME}"
DESTINATION "${DILIGENT_CORE_INSTALL_DIR}/lib/"
)
set_target_properties(DiligentCore-static PROPERTIES
FOLDER Core
)
else()
message("Unable to find librarian tool. Combined DiligentCore static library will not be produced.")
endif()

How to specify linux binary's rpath in cmake

I have a CMakeLists.txt file that contains such line of code:
link_directories(dir_a dir_b dir_c)
When I build an executable and perform
readelf -d
on it, I see RPATH with dir_a, dir_b dir_c.
What can I do with cmake if I want to create
Executable with empty RPATH?
Executable with some paths, but not specified in link_directories line?
The executable is not supposed to be "installed".
From this cmake-documentation you can read that:
you can use the variable CMAKE_SKIP_RPATH to have cmake not adding any RPATH to a binary ever and
that with the SET_TARGET_PROPERTIES-functions RPATHs can be controlled individually on a per-target base.

Add custom build step in CMake

I'm trying to add a custom build step in CMake that generates some files. I haven't found a description how it works.
I have an project where source, header & implementation files have to be generated by ODB for C++. ODB takes class headers as arguments and generates source files that I want to use in my project.
Right now I have the following command in my CMakeLists.txt:
add_custom_command(TARGET ${PROJECT_NAME}
PRE_BUILD
COMMAND odb -o /home/david/dev/ --std c++11 -I/home/david/dev/ -d sqlite --generate- query --generate-schema ${PROMOTER_LIB_PREFIX}/entities/person.hpp
DEPENDS ${PROJECT_NAME}
VERBATIM
)
For a file person.hpp ODB should generate person-odb.hxx, person-odb.cxx, person-odb.ixx but the CMake command I''ve used doesn't generate anything. In a terminal this command works fine.
What am I doing wrong?
EDIT: The problem can be solved by adding the following lines:
set(FAKE_TARGET fakeTarget)
add_custom_target(fakeTarget
odb -o /home/david/dev/ --std c++11 -I/home/david/dev/ -d sqlite --generate-query --generate-schema ${PROMOTER_LIB_PREFIX}/entities/person.hpp
)
add_dependencies(${PROJECT_NAME} ${FAKE_TARGET})
For me, with something similar, I just use :
add_custom_command(TARGET ${PROJECT_NAME}
PRE_BUILD
COMMAND odb -o /home/david/dev/ --std c++11 -I/home/david/dev/ -d sqlite --generate- query --generate-schema ${PROMOTER_LIB_PREFIX}/entities/person.hpp
)
We don't use DEPENDS or VERBATIM.
The DEPENDS option specify that the command must be executed only after that the project you gave to this option is built.
EDIT :
Note that the PRE_BUILD option is only supported on Visual Studio 7 or later. For all other generators PRE_BUILD will be treated as PRE_LINK.
Maybe that's why it doesn't work for you.
A work around could be (a bit ugly) :
Create a fake project
Add your custom command on it as POST_BUILD
Make you current project dependent on the fake one
Way I'm using it is:
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gen_icinstrtab.hpp
COMMAND xsltproc --output ${CMAKE_CURRENT_BINARY_DIR}/gen_icinstrtab.hpp ${CMAKE_SOURCE_DIR}/xml/genictabc.xslt ${CMAKE_SOURCE_DIR}/xml/icminstr.xml
)
add_executable(
du4
${CMAKE_CURRENT_BINARY_DIR}/gen_icinstrtab.hpp
.
.
.
)
The key was to add even .hpp files into add_executable block.

cmake run xxd as custom command

I've a rc directory I want to run xxd -i filename recursively in that directory.and add the generated files to my project.
I was checking some answer on lua. and I did it in a similar way.
SET(RESOURCE_COMPILER "xxd")
SET(COMPILED_RESOURCES "rc/base.css rc/common.js")
FOREACH(F ${COMPILED_RESOURCES})
ADD_CUSTOM_COMMAND(
OUTPUT "${COMPILED_RESOURCES}/${F}"
COMMAND ${RESOURCE_COMPILER} -i "${COMPILED_RESOURCES}/${F}"
COMMENT "Compiling ${F} to binary")
LIST (APPEND COMPILED_RESOURCES "${COMPILED_RESOURCES}/${F}")
ENDFOREACH()
but I don't see any file generated at all.
There are a couple of reasons you may not be seeing the output files. Your xxd command isn't well formed in the add_custom_command, but also the command will only be executed if the output file is required as a source in another CMake target (exe or lib) in the same CMakeLists.txt.
I'd change your snippet to something like:
SET(RESOURCE_COMPILER xxd)
FILE(GLOB_RECURSE COMPILED_RESOURCES "rc/*")
FOREACH(INPUT_FILE ${COMPILED_RESOURCES})
SET(OUTPUT_FILE ${INPUT_FILE}.hex)
ADD_CUSTOM_COMMAND(
OUTPUT ${OUTPUT_FILE}
COMMAND ${RESOURCE_COMPILER} -i ${INPUT_FILE} ${OUTPUT_FILE}
COMMENT "Compiling ${INPUT_FILE} to binary")
LIST(APPEND COMPILED_RESOURCES ${OUTPUT_FILE})
ENDFOREACH()
Then if you have another target which depends on the ${OUTPUT_FILE}s the command will be executed when that target is being built:
ADD_EXECUTABLE(MY_EXE main.cc ${COMPILED_RESOURCES})