Skip to content

Made the example builds work with LLVM, and fixed four that were broken on Linux - #594

Merged
fdesbiens merged 3 commits into
eclipse-threadx:devfrom
fdesbiens:feature/atfe-example-builds
Aug 10, 2026
Merged

Made the example builds work with LLVM, and fixed four that were broken on Linux#594
fdesbiens merged 3 commits into
eclipse-threadx:devfrom
fdesbiens:feature/atfe-example-builds

Conversation

@fdesbiens

Copy link
Copy Markdown
Contributor

Makes the example builds work with LLVM, and fixes four that could not run on Linux at all.

Why the gnu examples rather than the ac6 ones

The ac6 examples were evaluated as the place to add LLVM support. Measurement shows the gnu examples are already the LLVM path, and the ac6 ones would have to be rebuilt from scratch:

gnu example ac6 example
Linker input GNU ld script, which ATfE consumes directly scatter file, which ATfE does not
Startup crt0.S and vectors, in tree depends on __main and Image$$ARM_LIB_STACKHEAP$$ZI$$Limit from armclib and armlink
Build driver shell and batch scripts none; 83 Arm DS project files

Porting the ac6 examples means writing GNU linker scripts, replacing the startup path and writing build drivers, which reproduces the gnu example that already exists for every one of those cores. This change teaches the gnu scripts to drive either toolchain instead.

Toolchain selection

Each script picks its toolchain from TOOLCHAIN, defaulting to gnu, so every existing invocation behaves exactly as before:

TOOLCHAIN=atfe ATFE_CLANG=/path/to/clang ./build_threadx_sample.sh

The differences are four values: the compiler, the target triple, -Wl,--entry= instead of the driver's -e, and -lsemihost instead of --specs=nosys.specs. That last one matters: --specs= is a GCC spec-file mechanism with no LLVM equivalent, and picolibc leaves _exit to the application, so using the toolchain's own semihosting library avoids adding a syscall stub source to every example and suits an example that runs on a model.

Parameterised, not duplicated. Duplicate build scripts drift the first time one side is edited, which is the failure #590, #591 and #592 addressed.

Four scripts that could not run on Linux

The A-profile sample scripts referenced filenames that do not exist:

Script References On disk
A5, A9 MP_GIC.s MP_GIC.S
A5, A7, A8, A9 MP_PrivateTimer.s MP_PrivateTimer.S
A5, A7, A8, A9 V7.s, and V7.o on the link line v7.s

Case-insensitive on Windows, fatal on Linux. Corrected to match the files, which is why the Cortex-A5, A7, A8 and A9 examples now build on Linux where before they could not.

Results: every example, both toolchains

Core GNU ATfE
Cortex-M3 ok 21,456 B ok 22,650 B
Cortex-M4 ok 21,624 B ok 22,810 B
Cortex-M7 ok 21,540 B ok 22,842 B
Cortex-A5 ok 42,100 B ok 40,098 B
Cortex-A7 ok 41,820 B ok 39,326 B
Cortex-A8 ok 41,860 B ok 39,906 B
Cortex-A9 ok 42,124 B ok 40,162 B
Cortex-A12, A15, A17 ok 41,768 B fails
Cortex-M0, arm9, arm11, Cortex-R4, R5 fails fails

check_clang.sh now links the examples as well as compiling the sources, since compiling proves the sources parse while only linking exercises entry symbols, linker scripts and the C library together. The eight that do not link are listed in the script with their reasons, so the gaps stay visible instead of being silently skipped.

Two defects this surfaced, neither fixed here

The Cortex-M0 example cannot link with any toolchain. cortexm0_crt0.S references __text_load_start__, __text_start__ and __text_end__, and its linker script never defines them; the Cortex-M4 script defines the equivalents. It fails identically on dev before this change, so it is pre-existing rather than introduced here.

Cortex-A12, A15 and A17 fail only under LLVM, for a specific reason: their link line omits -nostartfiles, so the toolchain's own crt0.o is linked, and ATfE's wants picolibc's __data_start, __data_source, __data_size and __bss_size, which their linker script does not define. Defining those symbols is the proper fix, but they drive a data-copy loop that cannot be validated without executing the image, so it is left to separate work rather than guessed at.

The arm9, arm11, Cortex-R4 and Cortex-R5 failures are environmental: those scripts need newlib multilib variants that are not present in every GNU toolchain packaging. They also fail on dev.

Not in scope

The .bat equivalents are untouched. ATfE on Windows has not been validated, and changing those scripts without it would imply support that has not been demonstrated.

…en on Linux

The gnu example builds are the natural LLVM path: Arm Toolchain for Embedded is
LLVM based, consumes GNU ld linker scripts, and needs none of the scatter files
or Arm DS projects the ac6 examples carry. So rather than port the ac6 examples,
teach the gnu scripts to drive either toolchain.

Each script now selects the toolchain from a TOOLCHAIN variable that defaults to
gnu, so every existing invocation behaves exactly as before. TOOLCHAIN=atfe
switches the compiler, adds the target triple, passes the entry symbol through
to the linker rather than to the driver, and links the toolchain's own
semihosting library in place of --specs=nosys.specs, which is a GCC spec file
mechanism with no LLVM equivalent. That last choice avoids adding a syscall stub
source to every example.

The scripts are parameterised rather than duplicated. Copies of build scripts
would drift the first time one side was edited, which is the failure this
repository has just spent several changes recovering from.

Four sample scripts could not run on a case-sensitive filesystem at all. They
compiled MP_PrivateTimer.s and V7.s while the files on disk are
MP_PrivateTimer.S and v7.s, and the Cortex-A5 and A9 scripts named MP_GIC.s
where the file is MP_GIC.S. The link lines named V7.o accordingly. Corrected to
match the files, which is why the Cortex-A5, A7, A8 and A9 examples now build on
Linux where before they could not.

Extend scripts/check_clang.sh to link the example builds as well as compile the
sources, since compiling proves the sources parse while only linking exercises
entry symbols, linker scripts and the C library together.

Eight examples are listed as not expected to link, each with its reason, so the
gaps stay visible rather than being silently skipped. Five of them fail with the
GNU toolchain too and are therefore not LLVM problems: the Cortex-M0 example's
crt0 references __text_load_start__, __text_start__ and __text_end__, which its
linker script never defines, while the Cortex-M4 script defines the equivalents;
the arm9, arm11, Cortex-R4 and Cortex-R5 examples need newlib multilib variants
that are not present in every GNU toolchain packaging. The Cortex-A12, A15 and
A17 examples fail only with LLVM, because their link line omits -nostartfiles so
the toolchain's own crt0 is linked and wants picolibc's __data_start,
__data_source, __data_size and __bss_size, which their linker script does not
define.

Verified by building every example with both toolchains. Seven link with both:
Cortex-A5, A7, A8, A9, M3, M4 and M7. The GNU results are unchanged where they
worked before, and now also succeed for the four scripts with the case bug.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
The example stage runs each build script from inside its own directory, so a
relative path passed with --clang stopped resolving there and every example
build failed instantly. Local runs passed an absolute path and did not show it;
the CI job passes a path relative to the workspace root, which did.

Resolve the compiler to an absolute path once, before any directory change, and
print it so the toolchain in use is visible in the log.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
…ures

The toolchain selection covered the compiler but not arm-none-eabi-ar, which the
scripts invoke 628 times to assemble the library archive. A machine with an LLVM
toolchain and no GNU one therefore could not build any example, which is exactly
the situation in CI: the runner has no Arm GNU toolchain installed. Local runs
had one, so this only appeared once the job ran.

Select the archiver alongside the compiler, taking llvm-ar from beside clang in
the toolchain. The four scripts that call arm-none-eabi-ld directly are left
alone: they are arm9, arm11, Cortex-R4 and Cortex-R5, all already listed as not
expected to link, and their invocations are specific to GNU ld in ways that
parameterising would not resolve.

The check reported "example build produced no image" and then filtered the log
for lines containing "error", which hid the actual cause, since a missing tool
reports "command not found" or "No such file or directory". It now prints the
tail of the log. That filtering cost two CI round trips to diagnose something
the first run already knew.

Verified by shadowing arm-none-eabi-gcc, arm-none-eabi-ar, arm-none-eabi-ld and
the aarch64 equivalents with stubs that fail loudly, then running the whole
check: all 711 sources assemble, all eight profiles compile and all seven
example builds link without any GNU tool being invoked. The GNU default path
still produces an identical image. The diagnostics were confirmed by pointing
the archiver at a name that does not exist and checking that the reason appears.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
@fdesbiens
fdesbiens merged commit f3ab36d into eclipse-threadx:dev Aug 10, 2026
4 checks passed
@fdesbiens
fdesbiens deleted the feature/atfe-example-builds branch August 10, 2026 19:10
fdesbiens added a commit that referenced this pull request Aug 15, 2026
…stale link lines (#618)

* Removed generated build output and stopped two scripts naming deleted archives

Three kinds of file in the tree are produced by a build rather than written by
hand, and one pair of scripts still links archives that were deleted years ago.

Keil writes ThreadX_Library.plg on every build; the two committed copies are
HTML build logs from someone's machine. Code Composer generates the makefiles
under ports/c667x/ccs/example_build/tx/Release from the project files beside
them, so makefile, objects.mk, sources.mk, subdir_rules.mk, subdir_vars.mk and
ccsObjs.opt are all regenerated output.

The arm9 and arm11 sample builds link libc.a, libgcc.a and, for arm11,
libnosys.a from their own example_build directories. Those archives were removed
in 6.1.10 under "Removal of unneeded files", and libnosys.a in #594, but the link
lines were never updated, so both examples fail immediately with

    arm-none-eabi-ld: cannot find libc.a: No such file or directory

Link through the compiler driver instead, the shape every other example in the
tree uses since #594: the driver supplies libc and libgcc, and SYSCALL_LIB is
already defined in both scripts.

That does not make either example link, and the fix stops short of that on
purpose. With the archives no longer named, both now fail on

    undefined reference to `_fini'

because their linker scripts define the .init and .fini sections but not the
_init and _fini symbols, which live in crti.o and crtn.o and are omitted by
-nostartfiles. Making those two old cores build is a separate question from
removing a stale reference, so they stay in EXAMPLES_EXPECTED_TO_FAIL, with the
comment there corrected: it blamed newlib multilib packaging, which is true of
cortex_r4 and cortex_r5 but was never the reason for arm9 and arm11.

Reproduced throughout with arm-none-eabi-gcc 13.2.1.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

* Removed the Keil per-user state files and ignored them

Every Keil project in the tree carried a second file holding per-user state:
25 .uvoptx beside the 25 .uvprojx, 9 .uvopt beside the 9 .uvproj, and 3 .uvgui
multi-project workspace files. uVision rewrites all of them whenever a project is
opened, so they record whoever last had it open rather than anything about the
port: debugger selection, breakpoints, watch windows, window geometry.

Nothing in the tree references them, and every affected directory keeps its
.uvprojx or .uvproj, which is the file that actually describes the project.

Add ignore rules so they do not come back the next time someone opens a project
and commits.

1.4 MB across 37 files.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
fdesbiens added a commit that referenced this pull request Aug 15, 2026
…ed (#620)

#618 fixed the arm9 and arm11 shell scripts, but not their .bat counterparts,
and did not look at cortex_r4 and cortex_r5 at all. An audit of every build
script against the files actually present in its directory found the rest.

The .bat scripts for arm9, arm11, cortex_r4 and cortex_r5 still linked libc.a,
libgcc.a and, for arm11, libnosys.a from their own example_build directories,
and the cortex_r4 and cortex_r5 shell scripts did too. Those archives went in
6.1.10 under "Removal of unneeded files", so on Windows all four examples failed
exactly as the shell versions did before #618, and on Linux the two R-profile
ones still did.

Link through the compiler driver, as the other examples have since #594.

This does not make the examples link, and the change stops there deliberately.
All four now fail the same way,

    undefined reference to `_fini'

because their linker scripts define the .init and .fini sections but not the
_init and _fini symbols, which live in crti.o and crtn.o and are omitted by
-nostartfiles. Reviving four very old cores is separate work.

Correct the comment on EXAMPLES_EXPECTED_TO_FAIL again. It had cortex_r4 and
cortex_r5 failing for want of newlib multilib variants; they do not. All four
share the single cause above, and the multilib explanation was wrong for the
R-profile pair just as it was for arm9 and arm11.

Verified by running each shell script: cortex_r4 and cortex_r5 fail on _fini
rather than on missing files, matching arm9 and arm11. The .bat changes mirror
link lines proven that way in the same directories; they cannot be run here.

Three scripts are left alone and reported instead, because a blind edit could not
be verified: ports_module/cortex_m3 and cortex_m4 have Windows-only .bat scripts
that also name sources which are absent or differ in case, and
ports_smp/mips32_interaptiv_smp needs a MIPS toolchain.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
akifejaz pushed a commit to akifejaz/threadx that referenced this pull request Aug 17, 2026
…en on Linux (eclipse-threadx#594)

* Made the example builds work with LLVM, and fixed four that were broken on Linux

The gnu example builds are the natural LLVM path: Arm Toolchain for Embedded is
LLVM based, consumes GNU ld linker scripts, and needs none of the scatter files
or Arm DS projects the ac6 examples carry. So rather than port the ac6 examples,
teach the gnu scripts to drive either toolchain.

Each script now selects the toolchain from a TOOLCHAIN variable that defaults to
gnu, so every existing invocation behaves exactly as before. TOOLCHAIN=atfe
switches the compiler, adds the target triple, passes the entry symbol through
to the linker rather than to the driver, and links the toolchain's own
semihosting library in place of --specs=nosys.specs, which is a GCC spec file
mechanism with no LLVM equivalent. That last choice avoids adding a syscall stub
source to every example.

The scripts are parameterised rather than duplicated. Copies of build scripts
would drift the first time one side was edited, which is the failure this
repository has just spent several changes recovering from.

Four sample scripts could not run on a case-sensitive filesystem at all. They
compiled MP_PrivateTimer.s and V7.s while the files on disk are
MP_PrivateTimer.S and v7.s, and the Cortex-A5 and A9 scripts named MP_GIC.s
where the file is MP_GIC.S. The link lines named V7.o accordingly. Corrected to
match the files, which is why the Cortex-A5, A7, A8 and A9 examples now build on
Linux where before they could not.

Extend scripts/check_clang.sh to link the example builds as well as compile the
sources, since compiling proves the sources parse while only linking exercises
entry symbols, linker scripts and the C library together.

Eight examples are listed as not expected to link, each with its reason, so the
gaps stay visible rather than being silently skipped. Five of them fail with the
GNU toolchain too and are therefore not LLVM problems: the Cortex-M0 example's
crt0 references __text_load_start__, __text_start__ and __text_end__, which its
linker script never defines, while the Cortex-M4 script defines the equivalents;
the arm9, arm11, Cortex-R4 and Cortex-R5 examples need newlib multilib variants
that are not present in every GNU toolchain packaging. The Cortex-A12, A15 and
A17 examples fail only with LLVM, because their link line omits -nostartfiles so
the toolchain's own crt0 is linked and wants picolibc's __data_start,
__data_source, __data_size and __bss_size, which their linker script does not
define.

Verified by building every example with both toolchains. Seven link with both:
Cortex-A5, A7, A8, A9, M3, M4 and M7. The GNU results are unchanged where they
worked before, and now also succeed for the four scripts with the case bug.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

* Resolved the compiler path before running the example builds

The example stage runs each build script from inside its own directory, so a
relative path passed with --clang stopped resolving there and every example
build failed instantly. Local runs passed an absolute path and did not show it;
the CI job passes a path relative to the workspace root, which did.

Resolve the compiler to an absolute path once, before any directory change, and
print it so the toolchain in use is visible in the log.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

* Parameterised the archiver as well, and stopped the check hiding failures

The toolchain selection covered the compiler but not arm-none-eabi-ar, which the
scripts invoke 628 times to assemble the library archive. A machine with an LLVM
toolchain and no GNU one therefore could not build any example, which is exactly
the situation in CI: the runner has no Arm GNU toolchain installed. Local runs
had one, so this only appeared once the job ran.

Select the archiver alongside the compiler, taking llvm-ar from beside clang in
the toolchain. The four scripts that call arm-none-eabi-ld directly are left
alone: they are arm9, arm11, Cortex-R4 and Cortex-R5, all already listed as not
expected to link, and their invocations are specific to GNU ld in ways that
parameterising would not resolve.

The check reported "example build produced no image" and then filtered the log
for lines containing "error", which hid the actual cause, since a missing tool
reports "command not found" or "No such file or directory". It now prints the
tail of the log. That filtering cost two CI round trips to diagnose something
the first run already knew.

Verified by shadowing arm-none-eabi-gcc, arm-none-eabi-ar, arm-none-eabi-ld and
the aarch64 equivalents with stubs that fail loudly, then running the whole
check: all 711 sources assemble, all eight profiles compile and all seven
example builds link without any GNU tool being invoked. The GNU default path
still produces an identical image. The diagnostics were confirmed by pointing
the archiver at a name that does not exist and checking that the reason appears.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
akifejaz pushed a commit to akifejaz/threadx that referenced this pull request Aug 17, 2026
…stale link lines (eclipse-threadx#618)

* Removed generated build output and stopped two scripts naming deleted archives

Three kinds of file in the tree are produced by a build rather than written by
hand, and one pair of scripts still links archives that were deleted years ago.

Keil writes ThreadX_Library.plg on every build; the two committed copies are
HTML build logs from someone's machine. Code Composer generates the makefiles
under ports/c667x/ccs/example_build/tx/Release from the project files beside
them, so makefile, objects.mk, sources.mk, subdir_rules.mk, subdir_vars.mk and
ccsObjs.opt are all regenerated output.

The arm9 and arm11 sample builds link libc.a, libgcc.a and, for arm11,
libnosys.a from their own example_build directories. Those archives were removed
in 6.1.10 under "Removal of unneeded files", and libnosys.a in eclipse-threadx#594, but the link
lines were never updated, so both examples fail immediately with

    arm-none-eabi-ld: cannot find libc.a: No such file or directory

Link through the compiler driver instead, the shape every other example in the
tree uses since eclipse-threadx#594: the driver supplies libc and libgcc, and SYSCALL_LIB is
already defined in both scripts.

That does not make either example link, and the fix stops short of that on
purpose. With the archives no longer named, both now fail on

    undefined reference to `_fini'

because their linker scripts define the .init and .fini sections but not the
_init and _fini symbols, which live in crti.o and crtn.o and are omitted by
-nostartfiles. Making those two old cores build is a separate question from
removing a stale reference, so they stay in EXAMPLES_EXPECTED_TO_FAIL, with the
comment there corrected: it blamed newlib multilib packaging, which is true of
cortex_r4 and cortex_r5 but was never the reason for arm9 and arm11.

Reproduced throughout with arm-none-eabi-gcc 13.2.1.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

* Removed the Keil per-user state files and ignored them

Every Keil project in the tree carried a second file holding per-user state:
25 .uvoptx beside the 25 .uvprojx, 9 .uvopt beside the 9 .uvproj, and 3 .uvgui
multi-project workspace files. uVision rewrites all of them whenever a project is
opened, so they record whoever last had it open rather than anything about the
port: debugger selection, breakpoints, watch windows, window geometry.

Nothing in the tree references them, and every affected directory keeps its
.uvprojx or .uvproj, which is the file that actually describes the project.

Add ignore rules so they do not come back the next time someone opens a project
and commits.

1.4 MB across 37 files.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
akifejaz pushed a commit to akifejaz/threadx that referenced this pull request Aug 17, 2026
…ed (eclipse-threadx#620)

eclipse-threadx#618 fixed the arm9 and arm11 shell scripts, but not their .bat counterparts,
and did not look at cortex_r4 and cortex_r5 at all. An audit of every build
script against the files actually present in its directory found the rest.

The .bat scripts for arm9, arm11, cortex_r4 and cortex_r5 still linked libc.a,
libgcc.a and, for arm11, libnosys.a from their own example_build directories,
and the cortex_r4 and cortex_r5 shell scripts did too. Those archives went in
6.1.10 under "Removal of unneeded files", so on Windows all four examples failed
exactly as the shell versions did before eclipse-threadx#618, and on Linux the two R-profile
ones still did.

Link through the compiler driver, as the other examples have since eclipse-threadx#594.

This does not make the examples link, and the change stops there deliberately.
All four now fail the same way,

    undefined reference to `_fini'

because their linker scripts define the .init and .fini sections but not the
_init and _fini symbols, which live in crti.o and crtn.o and are omitted by
-nostartfiles. Reviving four very old cores is separate work.

Correct the comment on EXAMPLES_EXPECTED_TO_FAIL again. It had cortex_r4 and
cortex_r5 failing for want of newlib multilib variants; they do not. All four
share the single cause above, and the multilib explanation was wrong for the
R-profile pair just as it was for arm9 and arm11.

Verified by running each shell script: cortex_r4 and cortex_r5 fail on _fini
rather than on missing files, matching arm9 and arm11. The .bat changes mirror
link lines proven that way in the same directories; they cannot be run here.

Three scripts are left alone and reported instead, because a blind edit could not
be verified: ports_module/cortex_m3 and cortex_m4 have Windows-only .bat scripts
that also name sources which are absent or differ in case, and
ports_smp/mips32_interaptiv_smp needs a MIPS toolchain.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
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