Skip to content

fix(build): link boot log into eboot_core and make CI run the tests - #41

Open
Divyanshu-2907 wants to merge 1 commit into
embeddedos-org:masterfrom
Divyanshu-2907:fix/host-test-build-and-ci
Open

fix(build): link boot log into eboot_core and make CI run the tests#41
Divyanshu-2907 wants to merge 1 commit into
embeddedos-org:masterfrom
Divyanshu-2907:fix/host-test-build-and-ci

Conversation

@Divyanshu-2907

Copy link
Copy Markdown

master is currently red on this exact error. The
Host Build & Tests job for d3ae185
fails with undefined reference to 'eos_boot_log_append' while linking test_recovery,
and its Run tests step is skipped as a result. This PR fixes that job.

The Cross-compile STM32F4 job on the same commit is also red, but for an unrelated
reason: stage0/jump_stage1.c calls eos_sha256_* without including
eos_crypto_boot.h. This PR does not address that — it needs its own fix.

Problem

The host test build does not link on master. Building with tests enabled fails:

/usr/bin/ld: ../libeboot_core.a(recovery.c.o): in function `recovery_handle_auth':
core/recovery.c:184: undefined reference to `eos_boot_log_append'
core/recovery.c:372: undefined reference to `eos_boot_log_read'
core/recovery.c:380: undefined reference to `eos_boot_log_get_head'
collect2: error: ld returned 1 exit status

core/recovery.c is compiled into eboot_core, but the three boot-log functions it
calls were defined in stage1/boot_log.c, which is compiled only into eboot_stage1.
tests/test_recovery links eboot_core alone, so the symbols never resolve. The same
applies to any consumer that links eboot_core without eboot_stage1.

The calls were introduced in d3ae185 ("Implement authenticated recovery log retrieval",
#31). Two independent CI defects meant nothing caught it.

Root cause of the CI gap

1. The test flag name is wrong. ci.yml passed -DBUILD_TESTS=ON, but the option
declared in CMakeLists.txt is EBLDR_BUILD_TESTS. CMake warns that the variable is
unused, tests are never built, and the ctest step then succeeds with zero tests:

$ cmake -S . -B build -DBUILD_TESTS=ON
--   Tests       : OFF
CMake Warning:  Manually-specified variables were not used by the project: BUILD_TESTS
$ ctest -N
Total Tests: 0

2. The workflow never fired. ci.yml triggered on push: [main, develop] and
pull_request: [main]. This repository's default branch is master, and no main
branch exists, so the workflow did not run at all.

Approach

  • Move boot_log.c from eboot_stage1 into eboot_core. The boot log is a core
    service now that core/recovery.c depends on it, so the library boundary was what
    was wrong, not the call. eboot_stage1 already links eboot_core PUBLIC, so stage-1
    callers still resolve. The file moves unchanged — no code was edited.
  • Correct BUILD_TESTS to EBLDR_BUILD_TESTS in ci.yml.
  • Add master to the ci.yml triggers, matching the convention already used by
    build.yml. Kept main/develop so the workflow still behaves correctly if the
    default branch is renamed.
  • Drop -DENABLE_COVERAGE=ON, which is likewise not an option in this project and was
    silently ignored.

Validation

Ubuntu 24.04, gcc 13.3.0, cmake 3.28.3, valgrind 3.22.0. Clean configure and build
from an empty build directory, using the same flags CI now uses.

Before After
test_recovery link fails passes
ctest result 25/27, 2 failed 27/27, 0 failed
Tests registered with CI's flags 0 27
$ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DEBLDR_BUILD_TESTS=ON
--   Tests       : ON
$ cmake --build build --parallel
$ ctest --output-on-failure --parallel
100% tests passed, 0 tests failed out of 27
Label Time Summary:
    valgrind    =   9.79 sec*proc (13 tests)

All 13 valgrind leak-check targets pass, including valgrind_test_recovery, which
previously could not run because the binary did not exist.

Verifying the stage-1 side

The host test suite cannot prove this half: eboot_stage1 is only linked into an
executable by the eboot_firmware target, which exists solely in the cross-compile
board build. In a host build it is produced as a static archive and never linked, so
its symbols are never required to resolve.

Checked directly against the built archives instead:

$ nm -A build/libeboot_core.a | grep ' T eos_boot_log_'
libeboot_core.a:boot_log.c.o: T eos_boot_log_append
libeboot_core.a:boot_log.c.o: T eos_boot_log_get_head
libeboot_core.a:boot_log.c.o: T eos_boot_log_init
libeboot_core.a:boot_log.c.o: T eos_boot_log_read

$ nm build/libeboot_stage1.a | grep ' U eos_boot_log_' | sort -u
                 U eos_boot_log_append
                 U eos_boot_log_get_head
                 U eos_boot_log_init

Stage-1's undefined set is exactly a subset of what eboot_core now defines. Confirmed
by linking a probe main() against the archives in the order CMake generates from the
declared dependencies:

$ gcc probe.c -leboot_stage1                              # stage1 alone
undefined reference to `eos_boot_log_append'   (boot_scan.c, jump_app.c, main.c)
undefined reference to `eos_boot_log_get_head'
undefined reference to `eos_boot_log_init'

$ gcc probe.c -leboot_stage1 -leboot_core -leboot_hal     # as CMake links it
link succeeded

The first case reproduces the exact failure the move could have introduced; the second
shows the declared eboot_stage1 -> eboot_core -> eboot_hal dependency chain resolves
it. A full ARM cross-compile additionally exercises the board library and linker script,
neither of which this change touches.

Scope and limitations

Deliberately not addressed here, to keep this change reviewable:

  • C-level coverage was never actually collected. The removed ENABLE_COVERAGE
    flag had no implementation behind it; the codecov upload only ever received the
    Python coverage.xml from pytest. Wiring up real C coverage is a feature, not a fix.
  • include/eos_boot_log.h does not match the implementation. It declares
    int eos_boot_log_init(void) and int eos_boot_log_read(eos_boot_log_entry_t *, uint32_t),
    while the implementation provides void eos_boot_log_init(uint32_t) and
    int eos_boot_log_read(uint32_t, eos_boot_log_entry_t *) — note the reversed
    parameter order. Nothing in core/ or stage1/ includes the header; they use local
    extern declarations instead, which bypasses type checking. Reconciling the header
    with the implementation is an API decision worth its own PR.
  • tests/unit/test_boot_log.c does not test the shipped boot log. It includes
    eos_boot_log.h and defines its own local implementations of eos_boot_log_init,
    _append, _read and _clear, so it exercises stubs in the test file rather than
    boot_log.c. This is why it passed while the real implementation was unreachable.

No behavioural change to the bootloader itself: the only non-CI change is which static
library a translation unit is compiled into.

core/recovery.c calls eos_boot_log_append(), eos_boot_log_read() and
eos_boot_log_get_head(), which were defined in stage1/boot_log.c and
compiled only into eboot_stage1. tests/test_recovery links eboot_core
alone, so those symbols never resolved and the host test build failed:

    undefined reference to `eos_boot_log_append'
    collect2: error: ld returned 1 exit status

The boot log is a core service now that core/recovery.c depends on it,
so move the translation unit into eboot_core. This removes an undeclared
reverse dependency from core up into stage1. eboot_stage1 already depends
on eboot_core, so stage-1 callers still resolve. The file moves unchanged;
no code was edited.

Two CI defects hid the break:

  * ci.yml passed -DBUILD_TESTS=ON, but the option is EBLDR_BUILD_TESTS.
    CMake warned the variable was unused, tests were never built, and
    ctest then passed with 0 tests registered.
  * ci.yml triggered on main/develop only. This repository's default
    branch is master and no main branch exists, so it never ran.

Also drops -DENABLE_COVERAGE=ON, which is not an option in this project
and was silently ignored.

Verified on Ubuntu 24.04 / gcc 13.3.0 / cmake 3.28.3: clean configure and
build from an empty build directory, ctest 27/27 passing (14 unit + 13
valgrind). Before this change: 25/27, test_recovery failing to link.
@Divyanshu-2907
Divyanshu-2907 force-pushed the fix/host-test-build-and-ci branch from 8f37c7e to 7120546 Compare August 26, 2026 08:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant