diff --git a/.github/workflows/clang_check.yml b/.github/workflows/clang_check.yml index 405033f0b..2c90c790b 100644 --- a/.github/workflows/clang_check.yml +++ b/.github/workflows/clang_check.yml @@ -10,7 +10,17 @@ on: paths: - ".github/workflows/clang_check.yml" - "scripts/check_clang.sh" + # CMakeLists.txt, cmake/ and common_smp/ were missing until 26 Aug 2026, + # so this check did not run when the files it reads changed. The + # ports_smp example builds compile common_smp/src and the CMake stage + # reads the toolchain file and the top-level project. utility/ is + # deliberately absent: the FreeRTOS and POSIX layers under it are not + # enabled by any target this workflow builds. Keep this list and + # gcc_check.yml's the same. + - "CMakeLists.txt" + - "cmake/**" - "common/**" + - "common_smp/**" - "ports/**" - "ports_arch/**" - "ports_module/**" diff --git a/.github/workflows/gcc_check.yml b/.github/workflows/gcc_check.yml new file mode 100644 index 000000000..31e1ec346 --- /dev/null +++ b/.github/workflows/gcc_check.yml @@ -0,0 +1,175 @@ +name: gcc_check + +# Builds the Arm ports with the Arm GNU toolchain, in five stages: assemble +# every assembly source of every Arm gnu port, assemble again the parts guarded +# by feature macros, compile the common C sources for one core per architecture +# profile, then link the example builds, both the script-driven ones and those +# driven by CMake. +# +# Why this exists: GCC is the project's declared default compiler (AGENTS.md, +# "The default compiler for the project is GCC 14 on Linux") and until this +# workflow landed, nothing in CI compiled a line of any port with it. The only +# cross-compilation check that ran was clang_check, so the LLVM path was better +# guarded than the GNU one, on ports whose directory is literally named gnu. +# +# What it covers: 840 assembly sources across 40 port families, 469 of them +# again behind feature macros, common/src for nine cores, 42 script-driven +# example links and the five Cortex-R52 CMake images. Every skip is printed by +# name with a reason -- run scripts/check_gcc.sh --help, or read its header. +# +# What it does not cover: it compiles and links and **executes nothing**. The +# Cortex-R52 FVP ctest suite is not part of it. RISC-V, MIPS, RX and ARC are +# outside it entirely -- RISC-V deliberately, as both its ports do assemble and +# adding them widens the toolchain download for a family that is not regressing. +# +# It does not supersede cortex_m. That workflow builds four ports *through +# CMake*, which is the only thing exercising cmake/cortex_m*.cmake and the +# top-level CMakeLists for the M profile; this script's CMake stage covers +# cortex_r52 only. The overlap is the assembly and the C sources, not the build +# system. +on: + # push as well as pull_request, so dev's own history has a baseline and a bad + # squash-merge is caught rather than waiting for the next PR to notice. dev is + # named because it is the integration branch: a workflow that triggers only on + # master does not gate any pull request anybody opens, which is the defect + # ports_arch_check.yml carries a comment about and which cost cortex_m three + # months of failing in seven seconds unnoticed. + push: + branches: [ master, dev ] + paths: + - ".github/workflows/gcc_check.yml" + - "scripts/check_gcc.sh" + - "CMakeLists.txt" + - "cmake/**" + - "common/**" + - "common_smp/**" + - "ports/**" + - "ports_arch/**" + - "ports_module/**" + - "ports_smp/**" + pull_request: + branches: [ master, dev ] + # The two lists are duplicated rather than shared through a YAML anchor. + # GitHub Actions' workflow parser does not dependably honour anchors, and + # the failure mode is the whole workflow refusing to parse -- which is the + # cortex_m failure again, a job that dies before it does anything. Ten + # duplicated lines are cheaper than that. **Edit both.** + # + # common_smp, CMakeLists.txt and cmake/ are here and are absent from + # clang_check.yml's otherwise identical list, which is a gap in that file + # and is fixed alongside this one: the ports_smp example builds compile + # common_smp/src, and the CMake stage reads the toolchain file and the + # top-level project. utility/ is deliberately absent -- the FreeRTOS and + # POSIX layers under it are not enabled by any target this workflow builds. + paths: + - ".github/workflows/gcc_check.yml" + - "scripts/check_gcc.sh" + - "CMakeLists.txt" + - "cmake/**" + - "common/**" + - "common_smp/**" + - "ports/**" + - "ports_arch/**" + - "ports_module/**" + - "ports_smp/**" + +jobs: + gnu: + runs-on: ubuntu-24.04 + + env: + # Pinned deliberately, as the runner image is: a toolchain upgrade should + # be a reviewable commit rather than something that changes underneath the + # ports. 14.3.rel1 matches AGENTS.md's GCC 14 default and the version + # cortex_m already pins. + # Releases: https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads + GCC_VERSION: 14.3.rel1 + + steps: + # Actions are pinned to a commit SHA, with the version in the trailing + # comment. A tag can be moved; a SHA cannot, so this is what makes "which + # code ran in CI" answerable from the repository. Dependabot moves these + # pins and rewrites the comment with them -- see .github/dependabot.yml. + - name: Check out the repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + # Two toolchains, because Arm ships AArch32 and AArch64 as separate + # downloads and scripts/check_gcc.sh needs both -- every port maps to one of + # exactly those two triples. One job with two cache steps rather than two + # jobs, so the checks list stays short and a single script invocation sees + # both compilers. + # + # The AArch32 path and key match cortex_m's exactly, so the two workflows + # share one cache entry rather than each holding its own copy of the same + # 500MB archive. Change them together or the sharing silently stops and the + # only symptom is a slower run. + - name: Cache the AArch32 Arm GNU toolchain + id: cache-arm32 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: toolchain + key: arm-gnu-toolchain-${{ env.GCC_VERSION }}-x86_64-arm-none-eabi + + - name: Cache the AArch64 Arm GNU toolchain + id: cache-arm64 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: toolchain64 + key: arm-gnu-toolchain-${{ env.GCC_VERSION }}-x86_64-aarch64-none-elf + + # The checksum suffix is .sha256asc and not .sha256, and that is not a + # stylistic choice. Arm publishes both for this release, and for + # arm-none-eabi the .sha256 file contains a 32-character MD5 rather than a + # SHA-256 -- verified 26 Aug 2026 -- so sha256sum -c on it fails with "no + # properly formatted checksum lines found". .sha256asc is a plain + # sha256sum-format line for both triples. Do not "simplify" the suffix. + - name: Install the AArch32 Arm GNU toolchain + if: steps.cache-arm32.outputs.cache-hit != 'true' + run: | + set -eu + base="https://developer.arm.com/-/media/Files/downloads/gnu/${GCC_VERSION}/binrel" + archive="arm-gnu-toolchain-${GCC_VERSION}-x86_64-arm-none-eabi.tar.xz" + mkdir -p toolchain && cd toolchain + curl -fsSLO "$base/$archive" + curl -fsSLO "$base/$archive.sha256asc" + sha256sum -c "$archive.sha256asc" + tar xf "$archive" + rm -f "$archive" + + - name: Install the AArch64 Arm GNU toolchain + if: steps.cache-arm64.outputs.cache-hit != 'true' + run: | + set -eu + base="https://developer.arm.com/-/media/Files/downloads/gnu/${GCC_VERSION}/binrel" + archive="arm-gnu-toolchain-${GCC_VERSION}-x86_64-aarch64-none-elf.tar.xz" + mkdir -p toolchain64 && cd toolchain64 + curl -fsSLO "$base/$archive" + curl -fsSLO "$base/$archive.sha256asc" + sha256sum -c "$archive.sha256asc" + tar xf "$archive" + rm -f "$archive" + + # Only reaches apt if the runner image has stopped shipping ninja, which + # the CMake stage needs. This repository has already paid for unguarded apt + # calls: scripts/install.sh carries a long comment about apt-get update + # stalling for over two hours and taking whole regression runs with it. Do + # not turn this into an unconditional install. + - name: Ensure ninja is available + run: | + set -eu + if command -v ninja >/dev/null 2>&1; then + ninja --version + else + sudo apt-get update + sudo apt-get install -y --no-install-recommends ninja-build + fi + + # The script is told where both toolchains are rather than being left to + # find them on PATH, so nothing about the runner image can decide which + # compiler is used. The script prints both versions it resolved, which is + # what makes "was this really 14.3.rel1?" answerable from the log. + - name: Build the Arm ports with GCC + run: | + scripts/check_gcc.sh \ + --arm-none-eabi "$GITHUB_WORKSPACE/toolchain/arm-gnu-toolchain-${GCC_VERSION}-x86_64-arm-none-eabi/bin" \ + --aarch64-none-elf "$GITHUB_WORKSPACE/toolchain64/arm-gnu-toolchain-${GCC_VERSION}-x86_64-aarch64-none-elf/bin" diff --git a/ports/cortex_a34/gnu/example_build/build_threadx_sample.sh b/ports/cortex_a34/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports/cortex_a34/gnu/example_build/build_threadx_sample.sh +++ b/ports/cortex_a34/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports/cortex_a35/gnu/example_build/build_threadx_sample.sh b/ports/cortex_a35/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports/cortex_a35/gnu/example_build/build_threadx_sample.sh +++ b/ports/cortex_a35/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports/cortex_a53/gnu/example_build/build_threadx_sample.sh b/ports/cortex_a53/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports/cortex_a53/gnu/example_build/build_threadx_sample.sh +++ b/ports/cortex_a53/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports/cortex_a55/gnu/example_build/build_threadx_sample.sh b/ports/cortex_a55/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports/cortex_a55/gnu/example_build/build_threadx_sample.sh +++ b/ports/cortex_a55/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports/cortex_a57/gnu/example_build/build_threadx_sample.sh b/ports/cortex_a57/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports/cortex_a57/gnu/example_build/build_threadx_sample.sh +++ b/ports/cortex_a57/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports/cortex_a65/gnu/example_build/build_threadx_sample.sh b/ports/cortex_a65/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports/cortex_a65/gnu/example_build/build_threadx_sample.sh +++ b/ports/cortex_a65/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports/cortex_a65ae/gnu/example_build/build_threadx_sample.sh b/ports/cortex_a65ae/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports/cortex_a65ae/gnu/example_build/build_threadx_sample.sh +++ b/ports/cortex_a65ae/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports/cortex_a72/gnu/example_build/build_threadx_sample.sh b/ports/cortex_a72/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports/cortex_a72/gnu/example_build/build_threadx_sample.sh +++ b/ports/cortex_a72/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports/cortex_a73/gnu/example_build/build_threadx_sample.sh b/ports/cortex_a73/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports/cortex_a73/gnu/example_build/build_threadx_sample.sh +++ b/ports/cortex_a73/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports/cortex_a75/gnu/example_build/build_threadx_sample.sh b/ports/cortex_a75/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports/cortex_a75/gnu/example_build/build_threadx_sample.sh +++ b/ports/cortex_a75/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports/cortex_a76/gnu/example_build/build_threadx_sample.sh b/ports/cortex_a76/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports/cortex_a76/gnu/example_build/build_threadx_sample.sh +++ b/ports/cortex_a76/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports/cortex_a76ae/gnu/example_build/build_threadx_sample.sh b/ports/cortex_a76ae/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports/cortex_a76ae/gnu/example_build/build_threadx_sample.sh +++ b/ports/cortex_a76ae/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports/cortex_a77/gnu/example_build/build_threadx_sample.sh b/ports/cortex_a77/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports/cortex_a77/gnu/example_build/build_threadx_sample.sh +++ b/ports/cortex_a77/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_arch/ARMv8-A/threadx/ports/gnu/example_build/build_threadx_sample.sh b/ports_arch/ARMv8-A/threadx/ports/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_arch/ARMv8-A/threadx/ports/gnu/example_build/build_threadx_sample.sh +++ b/ports_arch/ARMv8-A/threadx/ports/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_module/cortex_a7/gnu/example_build/tx_initialize_low_level.s b/ports_module/cortex_a7/gnu/example_build/tx_initialize_low_level.S similarity index 100% rename from ports_module/cortex_a7/gnu/example_build/tx_initialize_low_level.s rename to ports_module/cortex_a7/gnu/example_build/tx_initialize_low_level.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_context_restore.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_context_restore.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_context_restore.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_context_restore.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_context_save.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_context_save.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_context_save.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_context_save.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_context_restore.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_context_restore.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_context_restore.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_context_restore.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_context_save.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_context_save.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_context_save.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_context_save.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_nesting_end.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_nesting_end.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_nesting_end.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_nesting_end.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_nesting_start.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_nesting_start.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_nesting_start.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_fiq_nesting_start.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_interrupt_control.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_interrupt_control.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_interrupt_control.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_interrupt_control.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_interrupt_disable.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_interrupt_disable.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_interrupt_disable.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_interrupt_disable.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_interrupt_restore.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_interrupt_restore.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_interrupt_restore.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_interrupt_restore.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_irq_nesting_end.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_irq_nesting_end.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_irq_nesting_end.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_irq_nesting_end.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_irq_nesting_start.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_irq_nesting_start.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_irq_nesting_start.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_irq_nesting_start.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_schedule.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_schedule.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_schedule.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_schedule.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_stack_build.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_stack_build.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_stack_build.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_stack_build.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_system_return.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_system_return.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_system_return.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_system_return.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_vectored_context_save.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_thread_vectored_context_save.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_thread_vectored_context_save.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_thread_vectored_context_save.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/tx_timer_interrupt.s b/ports_module/cortex_a7/gnu/module_manager/src/tx_timer_interrupt.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/tx_timer_interrupt.s rename to ports_module/cortex_a7/gnu/module_manager/src/tx_timer_interrupt.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_thread_stack_build.s b/ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_thread_stack_build.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_thread_stack_build.s rename to ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_thread_stack_build.S diff --git a/ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_user_mode_entry.s b/ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_user_mode_entry.S similarity index 100% rename from ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_user_mode_entry.s rename to ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_user_mode_entry.S diff --git a/ports_module/cortex_m23/gnu/module_manager/src/tx_initialize_low_level.S b/ports_module/cortex_m23/gnu/module_manager/src/tx_initialize_low_level.S index 28c37f367..5e356aeb9 100644 --- a/ports_module/cortex_m23/gnu/module_manager/src/tx_initialize_low_level.S +++ b/ports_module/cortex_m23/gnu/module_manager/src/tx_initialize_low_level.S @@ -9,6 +9,8 @@ * SPDX-License-Identifier: MIT **************************************************************************/ +// Some portions generated by Claude Code (Opus 5). + /**************************************************************************/ /**************************************************************************/ @@ -105,7 +107,7 @@ _tx_initialize_low_level: /* Configure SysTick. */ LDR r0, =0xE000E000 // Build address of NVIC registers - MOV r1, #0 // Build value for SysTick reset + MOVS r1, #0 // Build value for SysTick reset STR r1, [r0, #0x10] // Reset SysTick Control STR r1, [r0, #0x18] // Reset SysTick Counter Value LDR r1, =SYSTICK_CYCLES diff --git a/ports_module/cortex_m23/gnu/module_manager/src/tx_thread_context_restore.S b/ports_module/cortex_m23/gnu/module_manager/src/tx_thread_context_restore.S index 2f9d7165b..f08f68fb0 100644 --- a/ports_module/cortex_m23/gnu/module_manager/src/tx_thread_context_restore.S +++ b/ports_module/cortex_m23/gnu/module_manager/src/tx_thread_context_restore.S @@ -9,6 +9,8 @@ * SPDX-License-Identifier: MIT **************************************************************************/ +// Some portions generated by Claude Code (Opus 5). + /**************************************************************************/ /**************************************************************************/ @@ -69,7 +71,14 @@ _tx_thread_context_restore: /* Call the ISR exit function to indicate an ISR is complete. */ PUSH {r0, lr} // Save return address BL _tx_execution_isr_exit // Call the ISR exit function - POP {r0, lr} // Recover return address + POP {r0, r1} // Recover r0 and the return address + MOV lr, r1 // This core cannot POP into LR: the + // 16-bit Thumb POP takes r0-r7 and + // pc only. MOV to a high register + // is permitted, so restore LR from + // a scratch register. r1 is free -- + // the BL above may clobber r0-r3, + // which is why r0 is saved at all. #endif BX lr diff --git a/ports_module/cortex_m23/gnu/module_manager/src/tx_thread_context_save.S b/ports_module/cortex_m23/gnu/module_manager/src/tx_thread_context_save.S index f51cff714..b43467f7b 100644 --- a/ports_module/cortex_m23/gnu/module_manager/src/tx_thread_context_save.S +++ b/ports_module/cortex_m23/gnu/module_manager/src/tx_thread_context_save.S @@ -9,6 +9,8 @@ * SPDX-License-Identifier: MIT **************************************************************************/ +// Some portions generated by Claude Code (Opus 5). + /**************************************************************************/ /**************************************************************************/ @@ -69,7 +71,14 @@ _tx_thread_context_save: /* Call the ISR enter function to indicate an ISR is starting. */ PUSH {r0, lr} // Save return address BL _tx_execution_isr_enter // Call the ISR enter function - POP {r0, lr} // Recover return address + POP {r0, r1} // Recover r0 and the return address + MOV lr, r1 // This core cannot POP into LR: the + // 16-bit Thumb POP takes r0-r7 and + // pc only. MOV to a high register + // is permitted, so restore LR from + // a scratch register. r1 is free -- + // the BL above may clobber r0-r3, + // which is why r0 is saved at all. #endif BX lr diff --git a/ports_module/cortex_m23/gnu/module_manager/src/tx_thread_secure_stack_initialize.S b/ports_module/cortex_m23/gnu/module_manager/src/tx_thread_secure_stack_initialize.S index ae66a5c58..44cc33962 100644 --- a/ports_module/cortex_m23/gnu/module_manager/src/tx_thread_secure_stack_initialize.S +++ b/ports_module/cortex_m23/gnu/module_manager/src/tx_thread_secure_stack_initialize.S @@ -9,6 +9,8 @@ * SPDX-License-Identifier: MIT **************************************************************************/ +// Some portions generated by Claude Code (Opus 5). + /**************************************************************************/ /**************************************************************************/ @@ -67,7 +69,7 @@ _tx_thread_secure_stack_initialize: SVC 3 CPSID i // Disable interrupts #else - MOV r0, #0xFF // Feature not enabled + MOVS r0, #0xFF // Feature not enabled #endif BX lr .end diff --git a/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_context_restore.s b/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_context_restore.S similarity index 100% rename from ports_module/cortex_m33/gnu/module_manager/src/tx_thread_context_restore.s rename to ports_module/cortex_m33/gnu/module_manager/src/tx_thread_context_restore.S diff --git a/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_context_save.s b/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_context_save.S similarity index 100% rename from ports_module/cortex_m33/gnu/module_manager/src/tx_thread_context_save.s rename to ports_module/cortex_m33/gnu/module_manager/src/tx_thread_context_save.S diff --git a/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_interrupt_control.s b/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_interrupt_control.S similarity index 100% rename from ports_module/cortex_m33/gnu/module_manager/src/tx_thread_interrupt_control.s rename to ports_module/cortex_m33/gnu/module_manager/src/tx_thread_interrupt_control.S diff --git a/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_interrupt_disable.s b/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_interrupt_disable.S similarity index 100% rename from ports_module/cortex_m33/gnu/module_manager/src/tx_thread_interrupt_disable.s rename to ports_module/cortex_m33/gnu/module_manager/src/tx_thread_interrupt_disable.S diff --git a/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_interrupt_restore.s b/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_interrupt_restore.S similarity index 100% rename from ports_module/cortex_m33/gnu/module_manager/src/tx_thread_interrupt_restore.s rename to ports_module/cortex_m33/gnu/module_manager/src/tx_thread_interrupt_restore.S diff --git a/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_stack_build.s b/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_stack_build.S similarity index 100% rename from ports_module/cortex_m33/gnu/module_manager/src/tx_thread_stack_build.s rename to ports_module/cortex_m33/gnu/module_manager/src/tx_thread_stack_build.S diff --git a/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_system_return.s b/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_system_return.S similarity index 100% rename from ports_module/cortex_m33/gnu/module_manager/src/tx_thread_system_return.s rename to ports_module/cortex_m33/gnu/module_manager/src/tx_thread_system_return.S diff --git a/ports_module/cortex_m33/gnu/module_manager/src/txm_module_manager_thread_stack_build.s b/ports_module/cortex_m33/gnu/module_manager/src/txm_module_manager_thread_stack_build.S similarity index 100% rename from ports_module/cortex_m33/gnu/module_manager/src/txm_module_manager_thread_stack_build.s rename to ports_module/cortex_m33/gnu/module_manager/src/txm_module_manager_thread_stack_build.S diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/build_threadx_sample.sh b/ports_smp/cortex_a34_smp/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_smp/cortex_a34_smp/gnu/example_build/build_threadx_sample.sh +++ b/ports_smp/cortex_a34_smp/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_smp/cortex_a35_smp/gnu/example_build/build_threadx_sample.sh b/ports_smp/cortex_a35_smp/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_smp/cortex_a35_smp/gnu/example_build/build_threadx_sample.sh +++ b/ports_smp/cortex_a35_smp/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/build_threadx_sample.sh b/ports_smp/cortex_a53_smp/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_smp/cortex_a53_smp/gnu/example_build/build_threadx_sample.sh +++ b/ports_smp/cortex_a53_smp/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/build_threadx_sample.sh b/ports_smp/cortex_a55_smp/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_smp/cortex_a55_smp/gnu/example_build/build_threadx_sample.sh +++ b/ports_smp/cortex_a55_smp/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/build_threadx_sample.sh b/ports_smp/cortex_a57_smp/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_smp/cortex_a57_smp/gnu/example_build/build_threadx_sample.sh +++ b/ports_smp/cortex_a57_smp/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_smp/cortex_a5_smp/gnu/example_build/tx_initialize_low_level.s b/ports_smp/cortex_a5_smp/gnu/example_build/tx_initialize_low_level.S similarity index 100% rename from ports_smp/cortex_a5_smp/gnu/example_build/tx_initialize_low_level.s rename to ports_smp/cortex_a5_smp/gnu/example_build/tx_initialize_low_level.S diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/build_threadx_sample.sh b/ports_smp/cortex_a65_smp/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_smp/cortex_a65_smp/gnu/example_build/build_threadx_sample.sh +++ b/ports_smp/cortex_a65_smp/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/build_threadx_sample.sh b/ports_smp/cortex_a65ae_smp/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_smp/cortex_a65ae_smp/gnu/example_build/build_threadx_sample.sh +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/build_threadx_sample.sh b/ports_smp/cortex_a72_smp/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_smp/cortex_a72_smp/gnu/example_build/build_threadx_sample.sh +++ b/ports_smp/cortex_a72_smp/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/build_threadx_sample.sh b/ports_smp/cortex_a73_smp/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_smp/cortex_a73_smp/gnu/example_build/build_threadx_sample.sh +++ b/ports_smp/cortex_a73_smp/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/build_threadx_sample.sh b/ports_smp/cortex_a75_smp/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_smp/cortex_a75_smp/gnu/example_build/build_threadx_sample.sh +++ b/ports_smp/cortex_a75_smp/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/build_threadx_sample.sh b/ports_smp/cortex_a76_smp/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_smp/cortex_a76_smp/gnu/example_build/build_threadx_sample.sh +++ b/ports_smp/cortex_a76_smp/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/build_threadx_sample.sh b/ports_smp/cortex_a76ae_smp/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_smp/cortex_a76ae_smp/gnu/example_build/build_threadx_sample.sh +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/build_threadx_sample.sh b/ports_smp/cortex_a77_smp/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_smp/cortex_a77_smp/gnu/example_build/build_threadx_sample.sh +++ b/ports_smp/cortex_a77_smp/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/build_threadx_sample.sh b/ports_smp/cortex_a78_smp/gnu/example_build/build_threadx_sample.sh index fa5b92ec3..94248a888 100755 --- a/ports_smp/cortex_a78_smp/gnu/example_build/build_threadx_sample.sh +++ b/ports_smp/cortex_a78_smp/gnu/example_build/build_threadx_sample.sh @@ -44,6 +44,23 @@ case "${TOOLCHAIN}" in # initialise_monitor_handles that startup.S calls. SYSCALL_LIB="--specs=rdimon.specs" SEMIHOST_STUB="" + # crti.o defines _init and _fini; crtn.o closes them. The link below + # passes -nostartfiles, which is right for a port with its own reset + # path but also drops these two, and startup.S calls + # __libc_init_array, whose newlib implementation calls _init. Without + # them every AArch64 sample failed to link: + # + # libg.a(libc_a-init.o): in function `__libc_init_array': + # undefined reference to `_init' + # relocation truncated to fit: R_AARCH64_CALL26 against undefined + # symbol `_init' + # + # crti.o must precede every other .init contribution and crtn.o must + # follow all of them, which is why they bracket the object list rather + # than sitting with the other flags. The AArch32 scripts need none of + # this: they use nosys.specs and never reach __libc_init_array. + CRT_BEGIN="$("${CC}" -mcpu="${cpu}" -print-file-name=crti.o)" + CRT_END="$("${CC}" -mcpu="${cpu}" -print-file-name=crtn.o)" ;; atfe) CC="${ATFE_CLANG:-clang}" @@ -52,6 +69,11 @@ case "${TOOLCHAIN}" in # picolibc has no initialise_monitor_handles, and neither does the # toolchain's semihosting library, so the weak stub stands in for it. SEMIHOST_STUB="sample_threadx/semihost_stub.S" + # Deliberately empty. picolibc's __libc_init_array does not call _init, + # so these images link today and adding crti.o would change a working + # link for no reason. + CRT_BEGIN="" + CRT_END="" ;; *) echo "Unknown TOOLCHAIN '${TOOLCHAIN}'; expected gnu or atfe" >&2 @@ -80,7 +102,7 @@ done "${CC}" ${TARGET_FLAGS} -g -mcpu="${cpu}" -nostartfiles \ -T sample_threadx/sample_threadx.ld ${SYSCALL_LIB} \ -o sample_threadx.out -Wl,-Map=sample_threadx.map \ - ${objects} tx.a + ${CRT_BEGIN} ${objects} tx.a ${CRT_END} rm -f ${objects} diff --git a/ports_smp/cortex_a7_smp/gnu/src/tx_thread_smp_unprotect.s b/ports_smp/cortex_a7_smp/gnu/src/tx_thread_smp_unprotect.S similarity index 100% rename from ports_smp/cortex_a7_smp/gnu/src/tx_thread_smp_unprotect.s rename to ports_smp/cortex_a7_smp/gnu/src/tx_thread_smp_unprotect.S diff --git a/scripts/check_clang.sh b/scripts/check_clang.sh index ca646f312..f8ced1dd0 100755 --- a/scripts/check_clang.sh +++ b/scripts/check_clang.sh @@ -211,7 +211,13 @@ say "" say "== Assembly sources of every Arm gnu port ==" total=0 -for dir in ports/*/gnu/src ports_smp/*/gnu/src ports_module/*/gnu/src; do +# The module ports keep their assembly in module_manager/src, not src. This +# glob read ports_module/*/gnu/src until 26 Aug 2026; that directory does not +# exist, the [ -d ] guard below skipped it in silence, and 116 files across +# nine Arm module ports were assembled by no check with either compiler. The +# count went from 724 to 840 when the path was corrected, and three of the new +# files did not assemble. +for dir in ports/*/gnu/src ports_smp/*/gnu/src ports_module/*/gnu/module_manager/src; do [ -d "$dir" ] || continue core="$(echo "$dir" | cut -d/ -f2)" spec="${PORT_TARGET[$core]:-}" @@ -246,7 +252,13 @@ say "== Assembly behind feature macros ==" for macro in $FEATURE_MACROS; do macro_total=0 macro_bad=0 + # The module ports are named here for the same reason as in the stage + # above: they were absent from this list until 26 Aug 2026 and so read as + # covered. Adding them found the Cortex-M23 module manager carrying the + # very POP {r0, lr} this comment describes, six months after the same fix + # landed in its non-module sibling. for src in $(grep -rl "$macro" ports/*/gnu/src/*.S ports_smp/*/gnu/src/*.S \ + ports_module/*/gnu/module_manager/src/*.S \ 2>/dev/null | sort); do core="$(echo "$src" | cut -d/ -f2)" spec="${PORT_TARGET[$core]:-}" diff --git a/scripts/check_gcc.sh b/scripts/check_gcc.sh new file mode 100755 index 000000000..6b2cb8d5c --- /dev/null +++ b/scripts/check_gcc.sh @@ -0,0 +1,571 @@ +#!/bin/bash +############################################################################## +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5). +# The AI-generated portions may be considered public domain (CC0-1.0) +# and not subject to the project's licence. The human contributor has +# reviewed and verified that the code is correct. +# +# SPDX-License-Identifier: MIT and CC0-1.0 +############################################################################## + +# Builds the Arm ports with the GNU toolchain, in five stages: assemble every +# assembly source of every Arm gnu port, assemble again the parts guarded by +# feature macros, compile the common C sources for one core per architecture +# profile, then link the example builds, both the script-driven ones and those +# driven by CMake. Only the linking stages need a target C library. +# +# scripts/check_gcc.sh # both drivers from PATH +# scripts/check_gcc.sh --arm-none-eabi /path/to/toolchain/bin \ +# --aarch64-none-elf /path/to/toolchain/bin +# +# Options: +# --arm-none-eabi arm-none-eabi-gcc, or the directory holding it. +# Defaults to $ARM_NONE_EABI_GCC then to PATH. +# --aarch64-none-elf aarch64-none-elf-gcc, or its directory. +# Defaults to $AARCH64_NONE_ELF_GCC then to PATH. +# --asm-only Skip the C sources and the example builds. +# --no-examples Skip the example builds. +# --quiet Print only failures and the summary. +# +# Exit status is 0 when everything builds and 1 otherwise. +# +# Why this exists: GCC is the project's declared default compiler (AGENTS.md, +# "The default compiler for the project is GCC 14 on Linux"), it is what the +# gnu ports exist for, and it is what nearly every downstream user builds with +# -- and until this script landed, nothing in CI compiled a line of any port +# with it. The only cross-compilation check that ran was the LLVM one, so the +# ATfE path was better guarded than the GNU one, on ports whose directory is +# literally named gnu. +# +# This is the companion to scripts/check_clang.sh and deliberately mirrors it +# stage for stage. They are two scripts rather than one with a --toolchain flag +# because the flag surface differs (a prefixed driver against --target=), the +# C library differs, and the set of examples that can link differs. Folding +# them together makes it easy to weaken one check while working on the other. +# +# It compiles and links; it executes nothing. The Cortex-R52 FVP ctest suite is +# a separate matter, and the RISC-V, MIPS, RX and ARC families are outside it +# entirely -- every skip is printed by name below. +# +# Arm GNU toolchain releases: +# https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads + +set -u + +cd "$(dirname "$(realpath "$0")")/.." + +# Two toolchains, not one. Arm ships arm-none-eabi (AArch32: every M and R +# profile core and the A32 A-profile ports) and aarch64-none-elf as separate +# downloads, and PORT_TARGET below maps every port to one of exactly those two +# triples. Both are required: making a missing one a soft skip would let a run +# cover half the tree and still say "all checks passed", which is the failure +# this script was written to end. +CC_ARM="${ARM_NONE_EABI_GCC:-}" +CC_AARCH64="${AARCH64_NONE_ELF_GCC:-}" +asm_only=0 +no_examples=0 +quiet=0 + +while [ "$#" -gt 0 ]; do + case "$1" in + --arm-none-eabi) + [ "$#" -ge 2 ] || { echo "Error: --arm-none-eabi needs a path" >&2; exit 2; } + CC_ARM="$2"; shift 2 ;; + --aarch64-none-elf) + [ "$#" -ge 2 ] || { echo "Error: --aarch64-none-elf needs a path" >&2; exit 2; } + CC_AARCH64="$2"; shift 2 ;; + --asm-only) asm_only=1; no_examples=1; shift ;; + --no-examples) no_examples=1; shift ;; + --quiet) quiet=1; shift ;; + -h|--help) sed -n '17,36p' "$0"; exit 0 ;; + *) echo "Error: unknown option '$1'" >&2; exit 2 ;; + esac +done + +say() { [ "$quiet" -eq 1 ] || echo "$@"; } +fail() { echo " FAIL: $*"; } + +# Accept either the driver itself or the directory holding it, since a +# toolchain is unpacked as a tree and naming its bin directory is the natural +# thing to reach for. Resolve to an absolute path: the example stages run the +# build scripts from inside their own directories, so a relative path would +# stop resolving there. +resolve_gcc() { + triple="$1" + given="$2" + + if [ -z "$given" ]; then + command -v "${triple}-gcc" >/dev/null 2>&1 || return 1 + command -v "${triple}-gcc" + return 0 + fi + if [ -d "$given" ]; then + for candidate in "$given/${triple}-gcc" "$given/bin/${triple}-gcc"; do + [ -x "$candidate" ] && { realpath "$candidate"; return 0; } + done + return 1 + fi + [ -x "$given" ] || return 1 + realpath "$given" +} + +missing="" +CC_ARM="$(resolve_gcc arm-none-eabi "$CC_ARM")" || missing="$missing arm-none-eabi" +CC_AARCH64="$(resolve_gcc aarch64-none-elf "$CC_AARCH64")" || missing="$missing aarch64-none-elf" + +if [ -n "$missing" ]; then + echo "Error: compiler(s) not found:$missing" + echo "Pass --arm-none-eabi and --aarch64-none-elf, set ARM_NONE_EABI_GCC and" + echo "AARCH64_NONE_ELF_GCC, or put both drivers on PATH. Downloads:" + echo " https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads" + exit 1 +fi + +say "" +say "Using: $CC_ARM" +say " $("$CC_ARM" --version | head -1)" +say " $CC_AARCH64" +say " $("$CC_AARCH64" --version | head -1)" + +# The example build scripts and the CMake toolchain file call the drivers by +# their bare prefixed names, so both bin directories go on PATH for the linking +# stages. Done once, here, rather than per stage: a second toolchain arriving +# on PATH halfway through a run is exactly the kind of difference that makes a +# failure irreproducible. +PATH="$(dirname "$CC_ARM"):$(dirname "$CC_AARCH64"):$PATH" +export PATH + +# Pick the driver for a port from its triple, so the map below stays the single +# place a port's architecture is decided. +cc_for() { + case "$1" in + aarch64-none-elf) echo "$CC_AARCH64" ;; + *) echo "$CC_ARM" ;; + esac +} + +# Each port directory is mapped explicitly to a target triple and CPU. Do not +# replace this with prefix matching: cortex_a5* also matches cortex_a53 and +# cortex_a55, which are AArch64, and assembling those as ARM32 produces a flood +# of misleading errors. +# +# Copied verbatim from scripts/check_clang.sh. Keep the two identical -- a port +# covered by one check and not the other is worse than one covered by neither, +# because the checks list implies parity that does not exist. +declare -A PORT_TARGET=( + [cortex_m0]="arm-none-eabi cortex-m0 -mthumb" + [cortex_m0+]="arm-none-eabi cortex-m0plus -mthumb" + [cortex_m3]="arm-none-eabi cortex-m3 -mthumb" + [cortex_m4]="arm-none-eabi cortex-m4 -mthumb" + [cortex_m7]="arm-none-eabi cortex-m7 -mthumb" + [cortex_m23]="arm-none-eabi cortex-m23 -mthumb" + [cortex_m33]="arm-none-eabi cortex-m33 -mthumb" + [cortex_m52]="arm-none-eabi cortex-m52 -mthumb -mfloat-abi=hard" + [cortex_m55]="arm-none-eabi cortex-m55 -mthumb -mfloat-abi=hard" + [cortex_m85]="arm-none-eabi cortex-m85 -mthumb -mfloat-abi=hard" + [cortex_a5]="arm-none-eabi cortex-a5" + [cortex_a7]="arm-none-eabi cortex-a7" + [cortex_a8]="arm-none-eabi cortex-a8" + [cortex_a9]="arm-none-eabi cortex-a9" + [cortex_a12]="arm-none-eabi cortex-a12" + [cortex_a15]="arm-none-eabi cortex-a15" + [cortex_a17]="arm-none-eabi cortex-a17" + [cortex_a5_smp]="arm-none-eabi cortex-a5" + [cortex_a7_smp]="arm-none-eabi cortex-a7" + [cortex_a9_smp]="arm-none-eabi cortex-a9" + [cortex_r4]="arm-none-eabi cortex-r4" + [cortex_r5]="arm-none-eabi cortex-r5" + [cortex_r52]="arm-none-eabi cortex-r52" + [cortex_a34]="aarch64-none-elf cortex-a34" + [cortex_a35]="aarch64-none-elf cortex-a35" + [cortex_a53]="aarch64-none-elf cortex-a53" + [cortex_a55]="aarch64-none-elf cortex-a55" + [cortex_a57]="aarch64-none-elf cortex-a57" + [cortex_a65]="aarch64-none-elf cortex-a65" + [cortex_a65ae]="aarch64-none-elf cortex-a65ae" + [cortex_a72]="aarch64-none-elf cortex-a72" + [cortex_a73]="aarch64-none-elf cortex-a73" + [cortex_a75]="aarch64-none-elf cortex-a75" + [cortex_a76]="aarch64-none-elf cortex-a76" + [cortex_a76ae]="aarch64-none-elf cortex-a76ae" + [cortex_a77]="aarch64-none-elf cortex-a77" + [cortex_a34_smp]="aarch64-none-elf cortex-a34" + [cortex_a35_smp]="aarch64-none-elf cortex-a35" + [cortex_a53_smp]="aarch64-none-elf cortex-a53" + [cortex_a55_smp]="aarch64-none-elf cortex-a55" + [cortex_a57_smp]="aarch64-none-elf cortex-a57" + [cortex_a65_smp]="aarch64-none-elf cortex-a65" + [cortex_a65ae_smp]="aarch64-none-elf cortex-a65ae" + [cortex_a72_smp]="aarch64-none-elf cortex-a72" + [cortex_a73_smp]="aarch64-none-elf cortex-a73" + [cortex_a75_smp]="aarch64-none-elf cortex-a75" + [cortex_a76_smp]="aarch64-none-elf cortex-a76" + [cortex_a76ae_smp]="aarch64-none-elf cortex-a76ae" + [cortex_a77_smp]="aarch64-none-elf cortex-a77" + [cortex_a78_smp]="aarch64-none-elf cortex-a78" +) + +# Assembly guarded by a feature macro is invisible to the stage above, which +# assembles with default flags and so lets the preprocessor discard every #ifdef +# block before the assembler sees it. These are the macros a user can turn on; +# each file carrying one is assembled again with it defined. +# +# This is not hypothetical, and it is not only clang's finding. The Cortex-M23 +# module manager carried "POP {r0, lr}" in exactly these paths -- invalid on +# Armv8-M Baseline, where the 16-bit POP takes r0-r7 and pc only -- for six +# months after the identical fix landed in its non-module sibling, because no +# glob in either script reached ports_module until #672. +FEATURE_MACROS="TX_ENABLE_VFP_SUPPORT TX_ENABLE_FIQ_SUPPORT TX_LOW_POWER + TX_ENABLE_EXECUTION_CHANGE_NOTIFY" + +# TX_ENABLE_IRQ_NESTING and TX_ENABLE_FIQ_NESTING are deliberately not here. +# They guard no assembly in the trees this script walks: the nesting start and +# end routines are separate files compiled unconditionally, and the macros only +# feed the TX_PORT_SPECIFIC_BUILD_OPTIONS bitfield in tx_port.h. Adding them +# would assemble nothing new and imply coverage that does not exist. + +# Extra flags for the VFP paths, per core. +# +# check_clang.sh's equivalent map has one entry and a comment saying "Do not +# extend this to the A profile ports ... their defaults are already correct." +# That is true of clang and false of GCC, so do not copy that comment here. +# arm-none-eabi-gcc defaults to -mfloat-abi=soft, which disables the FPU +# outright, and every VFP file then fails with +# +# selected processor does not support 'vmrs r1,FPSCR' in ARM mode +# +# -mfloat-abi=hard alone is the fix, and it is the right one: it selects the +# core's own default FPU rather than naming one. Naming a -d16 FPU is the trap +# the clang script warns about -- the A-profile paths save D16-D31, which exist +# only on a 32-register FPU, so a -d16 choice turns 28 working files into +# "register expected". +# +# Cortex-R4 is the exception, in both scripts and for the same reason: its FPU +# is an option rather than part of the core, so -mfloat-abi=hard alone gives +# "selected architecture lacks an FPU" and an explicit -mfpu is required. The +# value matches check_clang.sh's, so the two scripts say the same thing about +# the same port. +# +# Every entry below was measured against arm-gnu-toolchain 14.3.rel1. A core +# that acquires a VFP-guarded file without an entry here fails loudly rather +# than silently, which is the intended behaviour. +declare -A VFP_EXTRA=( + [cortex_a5]="-mfloat-abi=hard" + [cortex_a7]="-mfloat-abi=hard" + [cortex_a8]="-mfloat-abi=hard" + [cortex_a9]="-mfloat-abi=hard" + [cortex_a12]="-mfloat-abi=hard" + [cortex_a15]="-mfloat-abi=hard" + [cortex_a17]="-mfloat-abi=hard" + [cortex_a5_smp]="-mfloat-abi=hard" + [cortex_a7_smp]="-mfloat-abi=hard" + [cortex_a9_smp]="-mfloat-abi=hard" + [cortex_r4]="-mfpu=vfpv3-d16 -mfloat-abi=softfp" + [cortex_r5]="-mfloat-abi=hard" + [cortex_r52]="-mfloat-abi=hard" +) + +# One core per architecture profile for the C sources. Compiling all of them +# for every core would multiply the run time without adding coverage, since the +# port headers differ by profile rather than by core. +# +# cortex_r52 earns a slot of its own next to cortex_r5 because Armv8-R AArch32 +# is a separate profile rather than a variant of Armv7-R. That port is written +# by hand instead of generated from ports_arch, and its tx_port.h differs +# accordingly, so cortex_r5 does not stand in for it. +C_CORES="cortex_m0 cortex_m4 cortex_m23 cortex_m33 cortex_m55 cortex_a7 cortex_a53 cortex_r5 cortex_r52" + +# Example builds driven by CMake rather than by a build_threadx.sh pair. These +# are covered by their own stage below, so the script-driven loop passes over +# them without reporting them as a gap. +CMAKE_EXAMPLE_CORES="cortex_r52" + +# Example builds that are not expected to link, with the reason. Named by +# their port directory, which covers both ports/ and ports_smp/. Listed +# explicitly rather than silently skipped, so the gaps stay visible. +# +# The same four as check_clang.sh, and the reason there is stated in terms of +# GNU tooling because that is where it was reproduced: +# +# their linker scripts define the .init and .fini sections but not the _init +# and _fini symbols. Those come from crti.o and crtn.o, which -nostartfiles +# leaves out, so newlib's fini.c cannot resolve them and the link ends with +# "undefined reference to `_fini'". +# +# The 27 AArch64 examples failed the same way until #673, which links crti.o +# and crtn.o back. They are deliberately absent from this list: they link. +EXAMPLES_EXPECTED_TO_FAIL="arm9 arm11 cortex_r4 cortex_r5" + +failures=0 +skipped="" + +# -------------------------------------------------------------------------- +say "" +say "== Assembly sources of every Arm gnu port ==" + +total=0 +# The module ports keep their assembly in module_manager/src, not src. +# check_clang.sh read ports_module/*/gnu/src until #672; that directory does +# not exist, the [ -d ] guard skipped it in silence, and 116 files across nine +# Arm module ports were assembled by no check with either compiler. This script +# has never had that hole and must not acquire it. +for dir in ports/*/gnu/src ports_smp/*/gnu/src ports_module/*/gnu/module_manager/src; do + [ -d "$dir" ] || continue + core="$(echo "$dir" | cut -d/ -f2)" + spec="${PORT_TARGET[$core]:-}" + if [ -z "$spec" ]; then + skipped="$skipped $core" + continue + fi + # shellcheck disable=SC2086 + set -- $spec + target="$1"; cpu="$2"; shift 2; extra="$*" + CC="$(cc_for "$target")" + + for src in "$dir"/*.S "$dir"/*.s; do + [ -f "$src" ] || continue + total=$((total + 1)) + output="$("$CC" -mcpu="$cpu" $extra -c "$src" -o /dev/null 2>&1)" + if [ -n "$output" ]; then + fail "$src" + echo "$output" | sed 's/^/ /' + failures=$((failures + 1)) + fi + done +done +say " $((total - failures)) of $total assembled" +if [ -n "$skipped" ]; then + say " not Arm, skipped:$(echo $skipped | tr ' ' '\n' | sort -u | tr '\n' ' ')" + say " (arm9 and arm11 are Arm; they are skipped for having no PORT_TARGET entry)" +fi + +# -------------------------------------------------------------------------- +say "" +say "== Assembly behind feature macros ==" + +for macro in $FEATURE_MACROS; do + macro_total=0 + macro_bad=0 + for src in $(grep -rl "$macro" ports/*/gnu/src/*.S ports_smp/*/gnu/src/*.S \ + ports_module/*/gnu/module_manager/src/*.S \ + 2>/dev/null | sort); do + core="$(echo "$src" | cut -d/ -f2)" + spec="${PORT_TARGET[$core]:-}" + [ -n "$spec" ] || continue + # shellcheck disable=SC2086 + set -- $spec + target="$1"; cpu="$2"; shift 2; extra="$*" + CC="$(cc_for "$target")" + + # The FPU flags apply to the VFP paths only; the other macros guard no + # floating-point code and do not need them. + fpu="" + if [ "$macro" = "TX_ENABLE_VFP_SUPPORT" ]; then + fpu="${VFP_EXTRA[$core]:-}" + fi + + macro_total=$((macro_total + 1)) + output="$("$CC" -mcpu="$cpu" $extra $fpu \ + -D"$macro" -c "$src" -o /dev/null 2>&1)" + if [ -n "$output" ]; then + fail "$src with -D$macro" + # Not filtered on "error": GCC prefixes its diagnostics with an + # "Assembler messages:" line and a missing tool says nothing of the + # kind, so filtering would hide the case worth seeing. + echo "$output" | head -4 | sed 's/^/ /' + macro_bad=$((macro_bad + 1)) + failures=$((failures + 1)) + fi + done + if [ "$macro_total" -eq 0 ]; then + say " $macro: no assembly is guarded by it" + else + say " $macro: $((macro_total - macro_bad)) of $macro_total assembled" + fi +done + +# -------------------------------------------------------------------------- +if [ "$asm_only" -eq 0 ]; then + say "" + say "== Common C sources, one core per architecture profile ==" + + for core in $C_CORES; do + spec="${PORT_TARGET[$core]:-}" + [ -n "$spec" ] || continue + # shellcheck disable=SC2086 + set -- $spec + target="$1"; cpu="$2"; shift 2; extra="$*" + CC="$(cc_for "$target")" + + count=0; bad=0 + for src in common/src/*.c; do + count=$((count + 1)) + output="$("$CC" -mcpu="$cpu" $extra \ + -Iports/"$core"/gnu/inc -Icommon/inc -c "$src" -o /dev/null 2>&1)" + if [ -n "$output" ]; then + fail "$core: $src" + echo "$output" | head -4 | sed 's/^/ /' + bad=$((bad + 1)); failures=$((failures + 1)) + fi + done + say " $core: $((count - bad)) of $count compiled" + done +fi + +# -------------------------------------------------------------------------- +if [ "$no_examples" -eq 0 ]; then + say "" + say "== Example builds, linked with GNU ld ==" + + example_ok=0 + example_total=0 + example_known="" + example_nodriver="" + example_nosample="" + for dir in ports/*/gnu/example_build ports_smp/*/gnu/example_build; do + [ -d "$dir" ] || continue + core="$(echo "$dir" | cut -d/ -f2)" + + # Anything on the expected-to-fail list is reported before any other + # filter is applied, so a name placed there can never drop out of the + # output. arm9 and arm11 are the cases that matter: they are Arm ports + # with example drivers, but they carry no PORT_TARGET entry, so the + # Arm test below would discard them. + case " $EXAMPLES_EXPECTED_TO_FAIL " in + *" $core "*) example_known="$example_known $core"; continue ;; + esac + + # Arm ports only, the same rule the assembly stage applies. Naming + # linux or mips32 as a gap here would be noise, not information. + [ -n "${PORT_TARGET[$core]:-}" ] || continue + + # Covered by the CMake stage below rather than here. + case " $CMAKE_EXAMPLE_CORES " in + *" $core "*) continue ;; + esac + + # A driverless example is not covered by this stage, so say so rather + # than dropping out in silence. A port that is simply absent from the + # count reads as covered. + if [ ! -f "$dir/build_threadx.sh" ]; then + example_nodriver="$example_nodriver $core" + continue + fi + if [ ! -f "$dir/build_threadx_sample.sh" ]; then + example_nosample="$example_nosample $core" + continue + fi + example_total=$((example_total + 1)) + + # Clean on both sides. A stale sample_threadx.out from a previous + # toolchain makes a failed link report success, because the success + # test below is the existence of the output file rather than the exit + # status of the driver. + rm -f "$dir"/*.o "$dir"/*.a "$dir"/*.out "$dir"/*.map 2>/dev/null || true + + # TOOLCHAIN is *unset*, not set: the build scripts already default to + # GNU with `: "${TOOLCHAIN:=gnu}"`, and a stray TOOLCHAIN=atfe from a + # developer's shell or an earlier command would otherwise make this + # stage silently check the other compiler. + log="$(cd "$dir" && unset TOOLCHAIN && ./build_threadx.sh 2>&1 && \ + ./build_threadx_sample.sh 2>&1)" || true + + if [ -f "$dir/sample_threadx.out" ]; then + example_ok=$((example_ok + 1)) + else + fail "$core: example build produced no image" + # Not filtered on "error": a missing tool reports "command not + # found" or "Permission denied", and filtering hid exactly that. + echo "$log" | tail -6 | sed 's/^/ /' + failures=$((failures + 1)) + fi + rm -f "$dir"/*.o "$dir"/*.a "$dir"/*.out "$dir"/*.map 2>/dev/null || true + done + say " $example_ok of $example_total example builds linked" + if [ -n "$example_known" ]; then + say " known not to link, see the list at the top of this script:$example_known" + fi + if [ -n "$example_nosample" ]; then + say " has build_threadx.sh but no build_threadx_sample.sh, so not linked:$example_nosample" + fi + if [ -n "$example_nodriver" ]; then + say " no script driver, so outside this stage:$example_nodriver" + fi +fi + +# -------------------------------------------------------------------------- +# The Cortex-R52 examples are built by CMake, so they need a toolchain file +# rather than the build_threadx.sh pair. Same compiler, same purpose as the +# stage above. +if [ "$no_examples" -eq 0 ]; then + say "" + say "== CMake example builds, linked with GNU ld ==" + + if ! command -v cmake >/dev/null 2>&1 || ! command -v ninja >/dev/null 2>&1; then + say " skipped: cmake and ninja are both required" + else + for core in $CMAKE_EXAMPLE_CORES; do + build_dir="$(mktemp -d)" + # ARM_TOOLCHAIN_PATH follows --arm-none-eabi, so this stage uses + # the same compiler as every other stage rather than the absolute + # path cmake/cortex_r52.cmake pins by default. + if cmake -S . -B "$build_dir" -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE="cmake/${core}.cmake" \ + -DARM_TOOLCHAIN_PATH="$(dirname "$CC_ARM")" \ + -DTX_R52_BUILD_FVP_EXAMPLE=ON \ + -DTX_R52_ENABLE_MPU=ON >"$build_dir/configure.log" 2>&1; then + # Read the image list from the generated graph instead of + # repeating it here, so adding a target cannot silently escape + # this check. The images are EXCLUDE_FROM_ALL, so "ninja" alone + # would build none of them. + # + # The cmake_object_order_depends_target_* entries are CMake's + # own ordering phonies, one per real image and named after it. + # Counting those doubled the total and reported ten images built + # where there are five. + images="$(ninja -C "$build_dir" -t targets all 2>/dev/null \ + | grep -oE '^[A-Za-z0-9_]+\.elf' \ + | grep -v '^cmake_' | sort -u)" + if [ -z "$images" ]; then + fail "$core: no .elf targets found in the CMake graph" + failures=$((failures + 1)) + else + built=0; total=0 + for image in $images; do + total=$((total + 1)) + if ninja -C "$build_dir" "$image" \ + >"$build_dir/$image.log" 2>&1; then + built=$((built + 1)) + else + fail "$core: $image did not build" + tail -6 "$build_dir/$image.log" | sed 's/^/ /' + failures=$((failures + 1)) + fi + done + say " $core: $built of $total images linked" + fi + else + fail "$core: CMake configure failed" + tail -6 "$build_dir/configure.log" | sed 's/^/ /' + failures=$((failures + 1)) + fi + rm -rf "$build_dir" + done + fi +fi + +# -------------------------------------------------------------------------- +say "" +if [ "$failures" -eq 0 ]; then + say "All GNU toolchain checks passed." + exit 0 +fi + +echo "$failures GNU toolchain check(s) failed." +exit 1 diff --git a/scripts/check_ports.sh b/scripts/check_ports.sh index b177a7ef6..1ba13ca03 100755 --- a/scripts/check_ports.sh +++ b/scripts/check_ports.sh @@ -39,6 +39,11 @@ # body in ports/cortex_m4/ac6/inc/tx_port.h, which placed statements # outside any function. See issue 569. # +# 4. Lowercase .s under a gnu tree. GAS preprocesses .S and not .s, so a +# .s file's #ifdef blocks are assembled whichever way the macro is set. +# Twenty-nine files were in that state, including one non-module SMP +# kernel port, and only one of them failed to assemble. +# # The last section reports, without failing, on port families that have no copy # script and so cannot be checked for reproducibility. # @@ -197,7 +202,56 @@ done < <(find ports ports_arch ports_module ports_smp -name "*.h" -type f \ [ "$orphans" -eq 0 ] && say " ok: no port header carries code at file scope" # -------------------------------------------------------------------------- -# 4. Report only: families with no copy script. +# 4. GNU assembly that uses the preprocessor must be named .S, not .s. +# -------------------------------------------------------------------------- +# GAS runs the C preprocessor on .S and not on .s. In a .s file every line +# beginning with # is just a comment, so nothing fails and nothing is +# substituted: a #define constant reaches the assembler as an undefined +# symbol, an #ifdef block is assembled whatever the macro says, and an +# #if/#else pair emits *both* branches. The port silently ignores its own +# feature macros. +# +# Measured 26 Aug 2026, when a corrected glob in check_clang.sh first offered +# the module ports to a compiler. Twenty-nine files were affected, and the +# damage was not only cosmetic: +# +# ports_smp/cortex_a7_smp/gnu/src/tx_thread_smp_unprotect.s -- the only .s +# in a directory of twenty-one .S -- wrote the caller's LR into the +# protection structure on every unprotect, a store guarded by +# TX_MPCORE_DEBUG_ENABLE, and returned through both BX lr and MOV pc, lr. +# +# ports_module/cortex_m33/.../tx_thread_stack_build.s emitted both arms of +# an #ifdef TX_SINGLE_MODE_SECURE, so the second LR value overwrote the +# first and the secure build got the non-secure one. +# +# ports_module/cortex_a7/.../tx_thread_schedule.s did fail to assemble, on +# GCC 14.3 as well as on LLVM, because #define SYS_MODE was never expanded. +# That is the only one of the twenty-nine any compiler complained about. +# +# Only the gnu trees are checked. The IAR, Arm Compiler 5 and Keil assemblers +# preprocess .s themselves, so the same combination is correct there, and +# around three hundred files in this repository depend on it. +say "" +say "== GNU assembly using the preprocessor is named .S ==" + +lowercase=0 +while IFS= read -r f; do + first="$(grep -nE '^[ \t]*#[ \t]*(define|include|if|ifdef|ifndef|else|elif|endif|undef)\b' \ + "$f" | head -1)" + if [ -n "$first" ]; then + fail "$f: preprocessor directive in a .s file, which GAS does not preprocess" + echo " line $first" + echo " Rename the file to .S. Check the callers first: a build script" + echo " that already names it .S is the usual sign of how this happened." + lowercase=$((lowercase + 1)) + fi +done < <(find ports ports_arch ports_module ports_smp -name "*.s" -type f \ + -path "*/gnu/*" 2>/dev/null | sort) + +[ "$lowercase" -eq 0 ] && say " ok: no .s file under a gnu tree uses the preprocessor" + +# -------------------------------------------------------------------------- +# 5. Report only: families with no copy script. # -------------------------------------------------------------------------- # These are maintained by hand, so a fix applied to one toolchain can silently # miss the others. Nothing here fails the run; it is a prompt to look.