mirror of
https://github.com/eclipse-threadx/threadx.git
synced 2026-10-06 06:59:08 +08:00
Those three sample scripts compiled crt0.S and reset.S but named neither on the link line, and omitted -nostartfiles, so the toolchain was also free to link its own startup. Under GNU that produced a working image; under an LLVM toolchain picolibc's crt0 was pulled in and wanted __data_start, __data_source, __data_size, __bss_size, __stack, __tls_base and __arm32_tls_tcb_offset, none of which the linker script defines. Defining picolibc's contract in the shared linker script was tried first and abandoned: it reaches into ABI constants that cannot be verified here, and it is unnecessary, because the in-tree crt0.S is already a complete startup for this script. It sets up the stack and zeroes BSS between __bss_start__ and __bss_end__, and .data carries no AT() so there is nothing to copy. So the three scripts now pass -nostartfiles and name crt0.o and reset.o, which is what the four sibling A profile cores already do and what these scripts were evidently compiling those files for. Both the old and the new GNU images contain the in-tree startup, __vectors from reset.S and _mainCRTStartup from crt0.S, so the startup was already being used through implicit startup file resolution rather than an explicit operand. How that resolution happened without the objects being named is not accounted for here, which is itself the argument for naming them: the same implicit behaviour does not hold across toolchains, and that is why the LLVM link failed. Add a readme to each of the three example directories. Their tx_initialize_low_level.S is the generic ARMv7-A skeleton, 300 lines and identical across all three, with no interrupt controller programming, no timer and no vector table installation, where the A5, A7, A8 and A9 examples have all three and ship the matching Versatile Express support files. These three therefore demonstrate that the port builds; they will not receive a timer tick. Nothing in the tree said so, which invites the assumption that they are equivalent. Verified with both toolchains for all three cores. GNU links at 41,276 bytes of text, down from 41,768 because the unused toolchain startup is no longer included, and Arm Toolchain for Embedded links at 37,982 where it previously could not link at all. The resulting image is structurally sound: entry at _start, __vectors at address zero, and a BSS range in RAM. It has not been executed. scripts/check_clang.sh now links eleven of eleven example builds. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
264 lines
10 KiB
Bash
Executable File
264 lines
10 KiB
Bash
Executable File
#!/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 an LLVM based toolchain: every assembly source of
|
|
# every Arm gnu port, and the common C sources for one core per architecture
|
|
# profile. Compilation only, no linking, so no target C library is required
|
|
# profile, then links the example builds that have a script driver.
|
|
#
|
|
# scripts/check_clang.sh # clang from PATH
|
|
# scripts/check_clang.sh --clang /path/to/clang
|
|
# CLANG=/path/to/clang scripts/check_clang.sh
|
|
#
|
|
# Options:
|
|
# --clang <path> Compiler to use; defaults to $CLANG then to clang.
|
|
# --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: the gnu ports are only ever built with GNU tooling, and GNU
|
|
# as accepts several non-canonical forms that LLVM's assembler rejects. Those
|
|
# forms accumulated unnoticed. This check also covers Arm Toolchain for
|
|
# Embedded, which is LLVM based and is the successor to Arm Compiler 6, so the
|
|
# ac6 code paths are exercised here as well.
|
|
#
|
|
# Arm Toolchain for Embedded releases:
|
|
# https://github.com/arm/arm-toolchain/releases
|
|
|
|
set -u
|
|
|
|
cd "$(dirname "$(realpath "$0")")/.."
|
|
|
|
CC="${CLANG:-clang}"
|
|
asm_only=0
|
|
no_examples=0
|
|
quiet=0
|
|
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--clang) [ "$#" -ge 2 ] || { echo "Error: --clang needs a path" >&2; exit 2; }; CC="$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,33p' "$0"; exit 0 ;;
|
|
*) echo "Error: unknown option '$1'" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
say() { [ "$quiet" -eq 1 ] || echo "$@"; }
|
|
fail() { echo " FAIL: $*"; }
|
|
|
|
if ! command -v "$CC" >/dev/null 2>&1 && [ ! -x "$CC" ]; then
|
|
echo "Error: compiler '$CC' not found."
|
|
echo "Pass --clang <path>, set CLANG, or install Arm Toolchain for Embedded:"
|
|
echo " https://github.com/arm/arm-toolchain/releases"
|
|
exit 1
|
|
fi
|
|
|
|
# Resolve to an absolute path. The example stage runs the build scripts from
|
|
# inside their own directories, so a relative compiler path would stop
|
|
# resolving there.
|
|
if [ -e "$CC" ]; then
|
|
CC="$(realpath "$CC")"
|
|
else
|
|
CC="$(command -v "$CC")"
|
|
fi
|
|
|
|
say ""
|
|
say "Using: $CC"
|
|
say " $("$CC" --version | head -1)"
|
|
|
|
# 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.
|
|
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_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"
|
|
)
|
|
|
|
# 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.
|
|
C_CORES="cortex_m0 cortex_m4 cortex_m23 cortex_m33 cortex_m55 cortex_a7 cortex_a53 cortex_r5"
|
|
|
|
# Example builds that are not expected to link, with the reason. Listed
|
|
# explicitly rather than silently skipped, so the gaps stay visible.
|
|
#
|
|
# These fail with the GNU toolchain too, so they are not LLVM problems:
|
|
# arm9 arm11 need newlib multilib variants for those CPUs, which are
|
|
# cortex_r4 cortex_r5 not present in every GNU toolchain packaging.
|
|
EXAMPLES_EXPECTED_TO_FAIL="arm9 arm11 cortex_r4 cortex_r5"
|
|
|
|
failures=0
|
|
skipped=""
|
|
|
|
# --------------------------------------------------------------------------
|
|
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
|
|
[ -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="$*"
|
|
|
|
for src in "$dir"/*.S "$dir"/*.s; do
|
|
[ -f "$src" ] || continue
|
|
total=$((total + 1))
|
|
output="$("$CC" --target="$target" -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' ' ')"
|
|
fi
|
|
|
|
# --------------------------------------------------------------------------
|
|
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="$*"
|
|
|
|
count=0; bad=0
|
|
for src in common/src/*.c; do
|
|
count=$((count + 1))
|
|
output="$("$CC" --target="$target" -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" | grep "error:" | head -3 | 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 lld =="
|
|
|
|
example_ok=0
|
|
example_total=0
|
|
example_known=""
|
|
for dir in ports/*/gnu/example_build; do
|
|
[ -f "$dir/build_threadx.sh" ] && [ -f "$dir/build_threadx_sample.sh" ] || continue
|
|
core="$(echo "$dir" | cut -d/ -f2)"
|
|
case " $EXAMPLES_EXPECTED_TO_FAIL " in
|
|
*" $core "*) example_known="$example_known $core"; continue ;;
|
|
esac
|
|
example_total=$((example_total + 1))
|
|
|
|
rm -f "$dir"/*.o "$dir"/*.a "$dir"/*.out "$dir"/*.map 2>/dev/null || true
|
|
log="$(cd "$dir" && TOOLCHAIN=atfe ATFE_CLANG="$CC" ./build_threadx.sh 2>&1 && \
|
|
TOOLCHAIN=atfe ATFE_CLANG="$CC" ./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
|
|
fi
|
|
# --------------------------------------------------------------------------
|
|
say ""
|
|
if [ "$failures" -eq 0 ]; then
|
|
say "All LLVM toolchain checks passed."
|
|
exit 0
|
|
fi
|
|
|
|
echo "$failures LLVM toolchain check(s) failed."
|
|
exit 1
|