Summary
When res_embed() is used on a target that is later consumed as a static library, embedded resources may be missing at runtime even though the build succeeds and the generated resource objects are present in the archive.
This happens because the generated <resource>.cpp registers the resource through a static initializer:
static AddFile_<hash> addFile_<hash>;
For a static archive, the linker only extracts object files that satisfy unresolved symbols. A static initializer inside an archive member does not create an unresolved symbol by itself, so the generated resource object is not pulled into the final executable. As a result, res::embed::get("payload") returns nullptr.
Minimal reproducer
cmake_minimum_required(VERSION 3.5)
project(res_embed_static_repro C CXX ASM)
option(REPRO_SHARED "Build repro_lib as shared" OFF)
option(BUILD_EXAMPLE "" OFF)
add_subdirectory(res_embed EXCLUDE_FROM_ALL)
if(REPRO_SHARED)
add_library(repro_lib SHARED lib.cpp)
else()
add_library(repro_lib STATIC lib.cpp)
endif()
target_include_directories(repro_lib PRIVATE res_embed/include)
res_embed(TARGET repro_lib NAME payload PATH ${CMAKE_CURRENT_SOURCE_DIR}/payload.txt KEYWORD)
add_executable(repro main.cpp)
target_link_libraries(repro PRIVATE repro_lib)
payload.txt:
lib.cpp:
#include <res_embed.h>
#include <cstddef>
extern "C" const char* repro_get(size_t* size) {
return res::embed::get("payload", size);
}
main.cpp:
#include <cstddef>
#include <cstdio>
#include <cstring>
extern "C" const char* repro_get(size_t* size);
int main() {
size_t size = 0;
const char* data = repro_get(&size);
if (!data) {
std::puts("MISSING");
return 2;
}
std::printf("FOUND %zu %.*s", size, (int)size, data);
return std::memcmp(data, "hello from res_embed\n", size) == 0 ? 0 : 3;
}
Observed behavior
With repro_lib as STATIC:
With repro_lib as SHARED:
FOUND 22 hello from res_embed
Confirmed workarounds
Both of these make the static-library case work:
- Link the archive with
--whole-archive:
target_link_libraries(repro PRIVATE
-Wl,--whole-archive repro_lib -Wl,--no-whole-archive)
- Explicitly call the generated initializer so the linker has an unresolved symbol to pull from the archive:
namespace res { namespace embed { namespace init { void payload(); } } }
res::embed::init::payload();
Expected behavior
res_embed() should either support static-library consumers without requiring --whole-archive/manual init::<name>() calls, or document/generate an explicit initialization mechanism for static-library use cases.
Summary
When
res_embed()is used on a target that is later consumed as a static library, embedded resources may be missing at runtime even though the build succeeds and the generated resource objects are present in the archive.This happens because the generated
<resource>.cppregisters the resource through a static initializer:static AddFile_<hash> addFile_<hash>;For a static archive, the linker only extracts object files that satisfy unresolved symbols. A static initializer inside an archive member does not create an unresolved symbol by itself, so the generated resource object is not pulled into the final executable. As a result,
res::embed::get("payload")returnsnullptr.Minimal reproducer
payload.txt:lib.cpp:main.cpp:Observed behavior
With
repro_libasSTATIC:With
repro_libasSHARED:Confirmed workarounds
Both of these make the static-library case work:
--whole-archive:Expected behavior
res_embed()should either support static-library consumers without requiring--whole-archive/manualinit::<name>()calls, or document/generate an explicit initialization mechanism for static-library use cases.