Skip to content

Embedded resources are missing when res_embed target is linked as a static library #12

Description

@dmikushin

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:

hello from res_embed

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:

MISSING

With repro_lib as SHARED:

FOUND 22 hello from res_embed

Confirmed workarounds

Both of these make the static-library case work:

  1. Link the archive with --whole-archive:
target_link_libraries(repro PRIVATE
  -Wl,--whole-archive repro_lib -Wl,--no-whole-archive)
  1. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions