diff --git a/.github/actions/nanvix-build/action.yml b/.github/actions/nanvix-build/action.yml index a98b6ba640a95..0a9e4005cb9a6 100644 --- a/.github/actions/nanvix-build/action.yml +++ b/.github/actions/nanvix-build/action.yml @@ -9,6 +9,10 @@ description: > its environment. inputs: + target: + description: Nanvix target architecture to build. + required: false + default: i686-nanvix install-location: description: Directory where LLVM (and the staged sysroot) is installed. required: true @@ -41,10 +45,36 @@ runs: shell: bash run: sudo ./z setup + # The pinned Nanvix release currently publishes an i686 sysroot archive. + # Its source tag also supports x86_64, so build the equivalent libc bundle + # from that exact tag until an x86_64 release archive is available. + - name: Build Nanvix x86_64 libc bundle + if: inputs.target == 'x86_64-nanvix' + shell: bash + run: | + set -euo pipefail + nanvix_tag="${Z_NANVIX_RELEASE_TAG:-$( + sed -n 's/^readonly NANVIX_RELEASE_TAG="${Z_NANVIX_RELEASE_TAG:-\([^}]*\)}"$/\1/p' z + )}" + if [[ -z "${nanvix_tag}" ]]; then + echo "Failed to read NANVIX_RELEASE_TAG from z" >&2 + exit 1 + fi + + nanvix_src="${RUNNER_TEMP}/nanvix-${nanvix_tag}-x86_64" + git clone --depth 1 --branch "${nanvix_tag}" \ + https://github.com/nanvix/nanvix.git "${nanvix_src}" + make -C "${nanvix_src}" nanvix-libc-bundle \ + TARGET=x86_64 RELEASE=yes LOG_LEVEL=error -j"$(nproc)" + echo "Z_NANVIX_SRC=${nanvix_src}" >> "${GITHUB_ENV}" + # Stage 0: build and install the Clang/LLD compiler (no runtimes, no libc). - name: Configure LLVM (stage 0) shell: bash - run: ./z configure --install-location=${{ inputs.install-location }} --stage=0 + env: + INSTALL_LOCATION: ${{ inputs.install-location }} + TARGET: ${{ inputs.target }} + run: ./z configure --install-location="${INSTALL_LOCATION}" --target="${TARGET}" --stage=0 - name: Build LLVM (stage 0) shell: bash @@ -65,10 +95,12 @@ runs: env: INSTALL_LOCATION: ${{ inputs.install-location }} RELEASE_NAME: ${{ inputs.release-name }} + TARGET: ${{ inputs.target }} run: | args=( --install-location="${INSTALL_LOCATION}" --sysroot-location="${INSTALL_LOCATION}" + --target="${TARGET}" --stage=1 ) if [[ -n "${RELEASE_NAME}" ]]; then diff --git a/.github/workflows/nanvix-build.yml b/.github/workflows/nanvix-build.yml index c1d926b96a0df..a90c6057a795c 100644 --- a/.github/workflows/nanvix-build.yml +++ b/.github/workflows/nanvix-build.yml @@ -44,7 +44,14 @@ env: jobs: build: + name: Build and test (${{ matrix.target }}) runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + target: + - i686-nanvix + - x86_64-nanvix steps: - name: Checkout code @@ -56,6 +63,7 @@ jobs: - name: Build and test toolchain uses: ./.github/actions/nanvix-build with: + target: ${{ matrix.target }} install-location: ${{ env.INSTALL_LOCATION }} - cache-key: ${{ runner.os }}-sccache - cache-restore-keys: ${{ runner.os }}-sccache + cache-key: ${{ runner.os }}-${{ matrix.target }}-sccache + cache-restore-keys: ${{ runner.os }}-${{ matrix.target }}-sccache diff --git a/.github/workflows/nanvix-release.yml b/.github/workflows/nanvix-release.yml index 08f87241cebaf..beb50b0606233 100644 --- a/.github/workflows/nanvix-release.yml +++ b/.github/workflows/nanvix-release.yml @@ -54,11 +54,17 @@ env: #================================================================================================== jobs: - release: + prepare: + name: Prepare release runs-on: ubuntu-24.04 - permissions: - contents: write + contents: read + + outputs: + exists: ${{ steps.check_release.outputs.exists }} + release_name: ${{ steps.commit_info.outputs.release_name }} + short_sha: ${{ steps.commit_info.outputs.short_sha }} + tag_name: ${{ steps.commit_info.outputs.tag_name }} steps: - name: Checkout code @@ -66,11 +72,6 @@ jobs: with: fetch-depth: 0 - - name: Setup Git configuration - run: | - git config --global user.name "github-actions[bot]" - git config --global user.email "github-actions[bot]@users.noreply.github.com" - - name: Get commit information id: commit_info run: | @@ -92,9 +93,24 @@ jobs: echo "exists=false" >> $GITHUB_OUTPUT fi - #---------------------------------------------------------------------------------------------- - # Build (stage 0 compiler -> stage 1 runtimes + in-source Nanvix libc) - #---------------------------------------------------------------------------------------------- + build: + name: Build release (${{ matrix.target }}) + needs: prepare + if: needs.prepare.outputs.exists == 'false' + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + target: + - i686-nanvix + - x86_64-nanvix + + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 # Build stage 0 + stage 1, install into INSTALL_LOCATION, and run the # build-only smoke tests. The shared build/test sequence lives in the @@ -102,33 +118,59 @@ jobs: # nanvix-build.yml) and runs in this job, so the installed toolchain remains # available to the release/packaging steps below. - name: Build and test toolchain - if: steps.check_release.outputs.exists == 'false' uses: ./.github/actions/nanvix-build with: + target: ${{ matrix.target }} install-location: ${{ env.INSTALL_LOCATION }} - release-name: ${{ steps.commit_info.outputs.release_name }} - cache-key: ${{ runner.os }}-sccache-release-${{ github.sha }} + release-name: ${{ needs.prepare.outputs.release_name }}-${{ matrix.target }} + cache-key: ${{ runner.os }}-${{ matrix.target }}-sccache-release-${{ github.sha }} cache-restore-keys: | - ${{ runner.os }}-sccache-release- - ${{ runner.os }}-sccache - - #---------------------------------------------------------------------------------------------- - # Release Artifact (tarball) - #---------------------------------------------------------------------------------------------- + ${{ runner.os }}-${{ matrix.target }}-sccache-release- + ${{ runner.os }}-${{ matrix.target }}-sccache # `./z release` builds the toolchain and creates the release tarball. - name: Create release artifacts - if: steps.check_release.outputs.exists == 'false' run: ./z release - #---------------------------------------------------------------------------------------------- - # Git Tag + GitHub Release - #---------------------------------------------------------------------------------------------- + - name: Upload release artifact + uses: actions/upload-artifact@v4 + with: + name: nanvix-toolchain-${{ matrix.target }} + path: .nanvix/out/${{ needs.prepare.outputs.release_name }}-${{ matrix.target }}.tar.bz2 + if-no-files-found: error + + publish: + name: Publish release + needs: + - prepare + - build + if: needs.prepare.outputs.exists == 'false' + runs-on: ubuntu-24.04 + + permissions: + contents: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download release artifacts + uses: actions/download-artifact@v4 + with: + pattern: nanvix-toolchain-* + path: .nanvix/out + merge-multiple: true + + - name: Setup Git configuration + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" - name: Create release tag - if: steps.check_release.outputs.exists == 'false' run: | - TAG_NAME="${{ steps.commit_info.outputs.tag_name }}" + TAG_NAME="${{ needs.prepare.outputs.tag_name }}" if git rev-parse -q --verify "refs/tags/${TAG_NAME}" >/dev/null; then echo "Tag ${TAG_NAME} already exists locally; skipping tag creation." else @@ -137,22 +179,23 @@ jobs: fi - name: Create GitHub Release - if: steps.check_release.outputs.exists == 'false' uses: softprops/action-gh-release@v2 with: - tag_name: ${{ steps.commit_info.outputs.tag_name }} - name: "${{ steps.commit_info.outputs.tag_name }}" + tag_name: ${{ needs.prepare.outputs.tag_name }} + name: "${{ needs.prepare.outputs.tag_name }}" body: | Automated release of the Nanvix LLVM ${{ env.LLVM_VERSION }} toolchain. - Built from commit: ${{ steps.commit_info.outputs.short_sha }} + Built from commit: ${{ needs.prepare.outputs.short_sha }} - This toolchain targets the Nanvix operating system (i686-unknown-nanvix) - and bundles the in-source Nanvix C library and headers. + These toolchains target the Nanvix operating system and bundle the + matching in-source Nanvix C library and headers. **Artifacts:** - - Tarball: `${{ steps.commit_info.outputs.release_name }}.tar.bz2` + - i686: `${{ needs.prepare.outputs.release_name }}-i686-nanvix.tar.bz2` + - x86_64: `${{ needs.prepare.outputs.release_name }}-x86_64-nanvix.tar.bz2` files: | - .nanvix/out/${{ steps.commit_info.outputs.release_name }}.tar.bz2 + .nanvix/out/${{ needs.prepare.outputs.release_name }}-i686-nanvix.tar.bz2 + .nanvix/out/${{ needs.prepare.outputs.release_name }}-x86_64-nanvix.tar.bz2 draft: false prerelease: false diff --git a/.nanvix/README.md b/.nanvix/README.md index 0caa9c0ea97af..77cdd2481d719 100644 --- a/.nanvix/README.md +++ b/.nanvix/README.md @@ -6,12 +6,13 @@ and how the toolchain is built. ## Overview -The port turns upstream LLVM into a cross-toolchain that produces static ELF -executables for **Nanvix**, a Unix-like research operating system. The target -triple is: +The port turns upstream LLVM into cross-toolchains that produce static ELF +executables for **Nanvix**, a Unix-like research operating system. The supported +target triples are: ``` i686-unknown-nanvix +x86_64-unknown-nanvix ``` The port has two kinds of changes. Most of the work lives in a small set of @@ -36,8 +37,8 @@ The upstream integration points (kept as small as possible) are: - **Triple registration** — `llvm/lib/TargetParser/Triple.{h,cpp}` add the `Nanvix` OS type and map the `nanvix` triple component to it. - **Clang target info** — `clang/lib/Basic/Targets.cpp`, `Targets/OSTargets.h`, - and `Targets/X86.h` add the Nanvix target (defining `__nanvix__`/`__unix__` - and the x86-32/x86-64 type layouts). + and `Targets/X86.h` add the Nanvix targets (defining + `__nanvix__`/`__unix__` and the x86-32/x86-64 type layouts). - **Clang driver wiring** — `clang/lib/Driver/Driver.cpp` dispatches the triple to the toolchain, `Driver/CMakeLists.txt` builds it, and `Driver/ToolChains/CommonArgs.cpp` and `Lex/InitHeaderSearch.cpp` special-case @@ -56,15 +57,16 @@ The `z` script (see `./z help`) builds the toolchain in two stages: ### Stage 0 — the compiler -Builds Clang and LLD only, with `X86` as the sole target and the default triple -pinned to `i686-unknown-nanvix`. No runtimes and no sysroot are required. The -result is a Clang that already defaults to the Nanvix toolchain logic described -below. +Builds Clang and LLD only, with `X86` as the sole backend and the default triple +selected by `./z configure --target` (`i686-nanvix` by default, or +`x86_64-nanvix`). No runtimes and no sysroot are required. The result defaults +to the selected Nanvix target while remaining able to compile for either triple +explicitly. ### Stage 1 — the runtimes -Uses the freshly built stage 0 Clang to cross-compile the LLVM runtimes for -Nanvix: +Uses the freshly built stage 0 Clang to cross-compile the LLVM runtimes for the +same Nanvix target: - `compiler-rt` (builtins only — baremetal build, no sanitizers/profile/etc.) - `libunwind` (static and shared) @@ -79,7 +81,16 @@ the in-source headers (`include/`), the C/math libraries (`libc.a`, `libm.a`, `libc.so`, `libm.so`), the startup objects (`crt0.o`, `libnvx_crt0.a`), the empty stub archives (`libdl.a`, `libpthread.a`, `librt.a`), and the user linker script (`user.ld`). The libc source is either a local Nanvix checkout -(`Z_NANVIX_SRC`) or a downloaded Nanvix release tarball (`Z_NANVIX_RELEASE_TAG`). +(`Z_NANVIX_SRC`) or a matching Nanvix release tarball +(`Z_NANVIX_RELEASE_TAG`). The script validates `crt0.o`'s ELF class before +staging it, so a 32-bit libc cannot silently enter an x86_64 sysroot (or vice +versa). If the pinned release does not publish an x86_64 archive, build its +source checkout first with: + +```bash +make -C "$Z_NANVIX_SRC" nanvix-libc-bundle \ + TARGET=x86_64 RELEASE=yes LOG_LEVEL=error +``` The in-source Nanvix libc has grown enough C and POSIX coverage that the stage 1 libc++ is now built with its full default feature set — wide characters, @@ -92,6 +103,8 @@ localization, and `` are all enabled. - **Defaults**: LLD is the linker, compiler-rt is the runtime library, libc++ is the C++ standard library, and `math-errno` is off. +- **x86_64 ABI**: the red zone is disabled by default because Nanvix exception + and signal delivery do not preserve it. Explicit `-mred-zone` still wins. - **Sysroot search**: both `$SYSROOT/lib` and `$SYSROOT/usr/lib` are searched so `crt0.o` and the archives resolve regardless of staging prefix. System C headers come from `$SYSROOT/usr/include`; libc++ headers come from @@ -111,7 +124,10 @@ localization, and `` are all enabled. `-lc`. - **Shared objects**: `-shared` links emit no ELF interpreter (Nanvix has no dynamic loader; `.so`s are brought in via `dlopen`) and allow text - relocations. + relocations. Nanvix supplies PIC-built shared libc artifacts for x86_64. +- **ELF64 self-linking**: x86_64 executable links use + `--apply-dynamic-relocs`, preserving ELF64 RELA entries while materializing + their link-time addends for Nanvix startup self-linking. ## The CMake platform module @@ -140,12 +156,12 @@ $INSTALL/ ├── bin/ # clang, clang++, lld, ... └── lib/ ├── libc++.{a,so*}, libc++abi.{a,so*}, libunwind.{a,so*} - └── clang//lib/i686-unknown-nanvix/libclang_rt.builtins*.a + └── clang//lib//libclang_rt.builtins*.a ``` ## Tests -`tests/smoke/` contains build-only smoke tests exercised by `./z test`: +`.nanvix/tests/smoke/` contains build-only smoke tests exercised by `./z test`: - `hello.c` — a minimal C program (libc + compiler-rt). - `hello.cpp` — a minimal C++ program exercising `new`/`delete` (libc++abi) and @@ -155,24 +171,28 @@ $INSTALL/ - `dynamic.cpp` — a C++ executable linked against `libhello.so` and the shared libc++, libc++abi, and libunwind runtimes. -`./z test` compiles and links all four for `i686-unknown-nanvix` using the -installed toolchain (it does not run them). The dynamic test names the versioned -runtime shared objects explicitly and brackets them with `-Bdynamic`/`-Bstatic` -so the driver cannot fall back to archives while the default system libraries -remain static. The executable uses Nanvix's established PIE self-linking flags, -including SysV hashing and no `PT_INTERP`. `./z verify` checks that the expected -static and shared runtime artifacts were installed. +`./z test` compiles and links all four for the configured target using the +installed toolchain (it does not run them). The dynamic test names the +versioned runtime shared objects explicitly and brackets them with +`-Bdynamic`/`-Bstatic` so the driver cannot fall back to archives while the +default system libraries remain static. The executable uses Nanvix's +self-linking flags, including SysV hashing and no `PT_INTERP`; the x86_64 link +also materializes ELF64 RELA addends. `./z verify` checks the configured +target's compiler-rt directory and the expected static/shared runtime artifacts. ## Building the toolchain -From the repository root: +From the repository root, choose `i686-nanvix` or `x86_64-nanvix` and use the +same target for both stages: ```bash +TARGET=x86_64-nanvix + ./z setup # install host build dependencies (Ubuntu) -./z configure --stage=0 # configure the compiler +./z configure --target="$TARGET" --stage=0 ./z build # build Clang + LLD ./z install # install stage 0 -./z configure --stage=1 # configure the runtimes +./z configure --target="$TARGET" --stage=1 ./z build # build the runtimes ./z install # install stage 1 ./z verify # check installed artifacts @@ -221,9 +241,9 @@ release you are building with. - **No system dynamic loader (executables are static ELF only)**. The driver forces `-Bstatic`, emits no ELF interpreter (`--no-dynamic-linker` on `-shared` links), and suppresses `RELRO`/build-id/rosegment. Shared runtime - libraries can be loaded explicitly with `dlopen`, and Nanvix has i686 startup - self-linking of an executable's `DT_NEEDED` dependencies, but no `PT_INTERP` - system dynamic linker. + libraries can be loaded explicitly with `dlopen`, and both x86 ports have + startup self-linking of an executable's `DT_NEEDED` dependencies, but no + `PT_INTERP` system dynamic linker. *Change once*: the Nanvix startup dynamic-linking epic lands ([nanvix/nanvix#2782](https://github.com/nanvix/nanvix/issues/2782), driver item [#2771](https://github.com/nanvix/nanvix/issues/2771)) — then add an diff --git a/.nanvix/tests/smoke/hello.c b/.nanvix/tests/smoke/hello.c index 26ce4451bab89..8150da59c1b88 100644 --- a/.nanvix/tests/smoke/hello.c +++ b/.nanvix/tests/smoke/hello.c @@ -5,8 +5,8 @@ /* * Build smoke test: verify that a C program compiles and links against the - * in-source Nanvix C library and compiler-rt builtins for the - * i686-unknown-nanvix target. + * in-source Nanvix C library and compiler-rt builtins for the configured + * Nanvix target. */ #include diff --git a/.nanvix/tests/smoke/hello.cpp b/.nanvix/tests/smoke/hello.cpp index 5d48bd20d961b..8ec40ccd123b7 100644 --- a/.nanvix/tests/smoke/hello.cpp +++ b/.nanvix/tests/smoke/hello.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. // Build smoke test: verify that a C++ program compiles and links against -// libc++, libc++abi, libunwind, and compiler-rt for the i686-unknown-nanvix +// libc++, libc++abi, libunwind, and compiler-rt for the configured Nanvix // target. Exercises new/delete (libc++abi) and std::printf (libc). #include diff --git a/.nanvix/tests/smoke/shared.c b/.nanvix/tests/smoke/shared.c index bf05fca050fee..490ef24efc209 100644 --- a/.nanvix/tests/smoke/shared.c +++ b/.nanvix/tests/smoke/shared.c @@ -5,7 +5,7 @@ /* * Build smoke test: verify that Clang can emit a shared object for the - * i686-unknown-nanvix target. + * configured Nanvix target. */ int nanvix_smoke_shared_value(void) diff --git a/clang/lib/Driver/ToolChains/Nanvix.cpp b/clang/lib/Driver/ToolChains/Nanvix.cpp index cd27cf910166e..b96350fed1143 100644 --- a/clang/lib/Driver/ToolChains/Nanvix.cpp +++ b/clang/lib/Driver/ToolChains/Nanvix.cpp @@ -82,6 +82,12 @@ void nanvix::Linker::ConstructJob(Compilation &C, const JobAction &JA, if (!IsShared) CmdArgs.push_back("-Bstatic"); + // Nanvix has no system dynamic loader. Materialize ELF64 RELA addends in + // executable images so startup self-linking begins from their link-time + // values while retaining the dynamic relocations it needs to rebind. + if (!IsShared && ToolChain.getTriple().getArch() == llvm::Triple::x86_64) + CmdArgs.push_back("--apply-dynamic-relocs"); + CmdArgs.push_back("--eh-frame-hdr"); CmdArgs.push_back("--gc-sections"); @@ -145,17 +151,18 @@ void nanvix::Linker::ConstructJob(Compilation &C, const JobAction &JA, // the default startup environment, and when the user already passed -T. if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles, options::OPT_r) && - !IsShared && !Args.hasArg(options::OPT_T)) { - // Probe only the toolchain file-search paths (the sysroot lib/ dirs), not - // the broader GetFilePath() locations (-B prefixes, ResourceDir, the - // compiler-rt dir), so nothing but the sysroot's own script is picked up; - // a stray user.ld in the working directory is likewise ignored. When the - // script is absent the link falls back to the clearer crt0/libc missing- - // symbol error instead of gaining a dangling --default-script argument. A - // script supplied via -Wl,-T is invisible to the driver, so this omission - // is intentionally silent. - for (const auto &Dir : ToolChain.getFilePaths()) { - SmallString<128> Script(Dir); + !IsShared && !Args.hasArg(options::OPT_T) && !D.SysRoot.empty()) { + // Probe only the sysroot lib directories, not the broader GetFilePath() + // locations (-B prefixes, ResourceDir, the compiler-rt dir, or the + // installed toolchain lib dir), so nothing but the sysroot's own script is + // picked up; a stray user.ld in the working directory is likewise ignored. + // When the script is absent the link falls back to the clearer crt0/libc + // missing-symbol error instead of gaining a dangling --default-script + // argument. A script supplied via -Wl,-T is invisible to the driver, so + // this omission is intentionally silent. + for (const char *Dir : {"lib", "usr/lib"}) { + SmallString<128> Script(D.SysRoot); + llvm::sys::path::append(Script, Dir); llvm::sys::path::append(Script, "user.ld"); if (ToolChain.getVFS().exists(Script)) { CmdArgs.push_back("--default-script"); @@ -182,6 +189,13 @@ Nanvix::Nanvix(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) getProgramPaths().push_back(getDriver().Dir); + // Stage 1 installs libc++, libc++abi, and libunwind next to the compiler + // under /lib, while the Nanvix C library remains in the sysroot. + SmallString<128> ToolchainLibDir(getDriver().Dir); + llvm::sys::path::append(ToolchainLibDir, "..", "lib"); + if (getVFS().exists(ToolchainLibDir)) + getFilePaths().push_back(std::string(ToolchainLibDir)); + if (!D.SysRoot.empty()) { // Nanvix ships its in-source C library (crt0.o, libc.a, libm.a, the // libdl/libpthread/librt stubs) and the LLVM stage-1 runtimes (libc++, @@ -197,10 +211,31 @@ Nanvix::Nanvix(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) } } +DerivedArgList * +Nanvix::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch, + Action::OffloadKind DeviceOffloadKind) const { + DerivedArgList *DAL = + Generic_ELF::TranslateArgs(Args, BoundArch, DeviceOffloadKind); + + if (getArch() != llvm::Triple::x86_64 || + Args.hasArg(options::OPT_mred_zone, options::OPT_mno_red_zone)) + return DAL; + + if (!DAL) { + DAL = new DerivedArgList(Args.getBaseArgs()); + for (Arg *A : Args) + DAL->append(A); + } + + const OptTable &Opts = getDriver().getOpts(); + DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_mno_red_zone)); + return DAL; +} + std::string Nanvix::getCompilerRTPath() const { // Compiler-rt builtins are installed in a per-target directory - // (e.g., lib/clang/21/lib/i686-unknown-nanvix/) but may use the - // old naming convention (libclang_rt.builtins-i386.a). Point the + // (e.g., lib/clang/21/lib/x86_64-unknown-nanvix/) but may use an old + // arch-suffixed naming convention. Point the // fallback path at the per-target directory so the arch-suffixed // name is found. SmallString<128> Path(getDriver().ResourceDir); diff --git a/clang/lib/Driver/ToolChains/Nanvix.h b/clang/lib/Driver/ToolChains/Nanvix.h index 9f77b8fddb483..7a05562d24d19 100644 --- a/clang/lib/Driver/ToolChains/Nanvix.h +++ b/clang/lib/Driver/ToolChains/Nanvix.h @@ -49,6 +49,10 @@ class LLVM_LIBRARY_VISIBILITY Nanvix : public Generic_ELF { bool HasNativeLLVMSupport() const override { return true; } bool IsMathErrnoDefault() const override { return false; } + llvm::opt::DerivedArgList * + TranslateArgs(const llvm::opt::DerivedArgList &Args, StringRef BoundArch, + Action::OffloadKind DeviceOffloadKind) const override; + std::string getCompilerRTPath() const override; const char *getDefaultLinker() const override { return "ld.lld"; } diff --git a/clang/test/Driver/nanvix.c b/clang/test/Driver/nanvix.c index 2f50d31c8707c..513b72ead206d 100644 --- a/clang/test/Driver/nanvix.c +++ b/clang/test/Driver/nanvix.c @@ -1,4 +1,4 @@ -// Driver tests for the Nanvix toolchain (i686-unknown-nanvix). +// Driver tests for the Nanvix x86 toolchains. // // The Nanvix toolchain drives ld.lld directly (instead of going through a gcc // wrapper) and emits a fixed set of flags required by the Nanvix ELF loader and @@ -20,13 +20,18 @@ // archive-grouped default libraries. // RUN: %clang -### --target=i686-unknown-nanvix \ // RUN: --sysroot=%t %s 2>&1 \ -// RUN: | FileCheck --check-prefix=CHECK-LD %s +// RUN: | FileCheck --check-prefixes=CHECK-LD,CHECK-LD32 %s +// RUN: %clang -### --target=x86_64-unknown-nanvix \ +// RUN: --sysroot=%t %s 2>&1 \ +// RUN: | FileCheck --check-prefixes=CHECK-LD,CHECK-LD64 %s // CHECK-LD: "{{[^"]*}}ld.lld" // CHECK-LD-SAME: "--sysroot={{[^"]+}}" // CHECK-LD-SAME: "--build-id=none" // CHECK-LD-SAME: "--no-rosegment" // CHECK-LD-SAME: "-z" "norelro" // CHECK-LD-SAME: "-Bstatic" +// CHECK-LD64-SAME: "--apply-dynamic-relocs" +// CHECK-LD32-NOT: "--apply-dynamic-relocs" // CHECK-LD-SAME: "--eh-frame-hdr" // CHECK-LD-SAME: "--gc-sections" // CHECK-LD-SAME: "--no-dependent-libraries" @@ -42,8 +47,12 @@ // RUN: %clangxx -### --target=i686-unknown-nanvix \ // RUN: --sysroot=%t %s 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-LDXX %s +// RUN: %clangxx -### --target=x86_64-unknown-nanvix \ +// RUN: --sysroot=%t %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-LDXX %s // CHECK-LDXX: "{{[^"]*}}ld.lld" // CHECK-LDXX-SAME: "{{[^"]*}}crt0.o" +// CHECK-LDXX-SAME: "-L{{[^"]*}}/bin/../lib" // CHECK-LDXX-SAME: "--start-group" // CHECK-LDXX-SAME: "-lc++" "-lc++abi" "-lunwind" // CHECK-LDXX-SAME: "-lm" "-lc" @@ -54,11 +63,15 @@ // RUN: %clang -### --target=i686-unknown-nanvix -shared \ // RUN: --sysroot=%t %s 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-SHARED %s +// RUN: %clang -### --target=x86_64-unknown-nanvix -shared \ +// RUN: --sysroot=%t %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-SHARED %s // CHECK-SHARED: "{{[^"]*}}ld.lld" // CHECK-SHARED-SAME: "-shared" // CHECK-SHARED-SAME: "-z" "notext" // CHECK-SHARED-SAME: "--no-dynamic-linker" // CHECK-SHARED-NOT: "-Bstatic" +// CHECK-SHARED-NOT: "--apply-dynamic-relocs" // CHECK-SHARED-NOT: crt0.o // Relocatable link (-r): forward -r and link nothing else (no crt0, no default @@ -66,11 +79,25 @@ // RUN: %clang -### --target=i686-unknown-nanvix -r \ // RUN: --sysroot=%t %s 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-RELOC %s +// RUN: %clang -### --target=x86_64-unknown-nanvix -r \ +// RUN: --sysroot=%t %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-RELOC %s // CHECK-RELOC: "{{[^"]*}}ld.lld" // CHECK-RELOC-SAME: "-r" // CHECK-RELOC-NOT: crt0.o // CHECK-RELOC-NOT: "--start-group" +// x86_64 Nanvix disables the red zone by default because kernel exception and +// signal delivery do not preserve it. An explicit user choice still wins. +// RUN: %clang -### --target=x86_64-unknown-nanvix -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NO-RED-ZONE %s +// CHECK-NO-RED-ZONE: "-cc1" +// CHECK-NO-RED-ZONE-SAME: "-disable-red-zone" +// RUN: %clang -### --target=x86_64-unknown-nanvix -mred-zone -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-RED-ZONE %s +// CHECK-RED-ZONE: "-cc1" +// CHECK-RED-ZONE-NOT: "-disable-red-zone" + // Default link auto-adds the Nanvix user linker script as a default script // (LLD --default-script/-dT), resolved from the sysroot lib/ path, so a bare // `clang hello.c -o hello` links without an explicit -T. It is added after the @@ -78,6 +105,9 @@ // RUN: %clang -### --target=i686-unknown-nanvix \ // RUN: --sysroot=%t.sysroot %s 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-USERLD %s +// RUN: %clang -### --target=x86_64-unknown-nanvix \ +// RUN: --sysroot=%t.sysroot %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-USERLD %s // CHECK-USERLD: "{{[^"]*}}ld.lld" // CHECK-USERLD-SAME: "--end-group" // CHECK-USERLD-SAME: "--default-script" "{{[^"]*}}user.ld" @@ -87,6 +117,9 @@ // RUN: %clang -### --target=i686-unknown-nanvix \ // RUN: --sysroot=%t.sysroot -T a.ld %s 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-USER-T %s +// RUN: %clang -### --target=x86_64-unknown-nanvix \ +// RUN: --sysroot=%t.sysroot -T a.ld %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-USER-T %s // CHECK-USER-T: "{{[^"]*}}ld.lld" // CHECK-USER-T-NOT: "--default-script" // CHECK-USER-T: "-T" "a.ld" @@ -96,15 +129,27 @@ // RUN: %clang -### --target=i686-unknown-nanvix -nostdlib \ // RUN: --sysroot=%t.sysroot %s 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-NODEFAULT %s +// RUN: %clang -### --target=x86_64-unknown-nanvix -nostdlib \ +// RUN: --sysroot=%t.sysroot %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NODEFAULT %s // RUN: %clang -### --target=i686-unknown-nanvix -nostartfiles \ // RUN: --sysroot=%t.sysroot %s 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-NODEFAULT %s +// RUN: %clang -### --target=x86_64-unknown-nanvix -nostartfiles \ +// RUN: --sysroot=%t.sysroot %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NODEFAULT %s // RUN: %clang -### --target=i686-unknown-nanvix -shared \ // RUN: --sysroot=%t.sysroot %s 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-NODEFAULT %s +// RUN: %clang -### --target=x86_64-unknown-nanvix -shared \ +// RUN: --sysroot=%t.sysroot %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NODEFAULT %s // RUN: %clang -### --target=i686-unknown-nanvix -r \ // RUN: --sysroot=%t.sysroot %s 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-NODEFAULT %s +// RUN: %clang -### --target=x86_64-unknown-nanvix -r \ +// RUN: --sysroot=%t.sysroot %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NODEFAULT %s // CHECK-NODEFAULT: "{{[^"]*}}ld.lld" // CHECK-NODEFAULT-NOT: "--default-script" @@ -118,6 +163,9 @@ // RUN: cd %t.cwd && %clang -### --target=i686-unknown-nanvix \ // RUN: --sysroot=%t %s 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-CWD %s +// RUN: cd %t.cwd && %clang -### --target=x86_64-unknown-nanvix \ +// RUN: --sysroot=%t %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-CWD %s // CHECK-CWD: "{{[^"]*}}ld.lld" // CHECK-CWD-NOT: "--default-script" diff --git a/clang/test/Preprocessor/init-nanvix.c b/clang/test/Preprocessor/init-nanvix.c new file mode 100644 index 0000000000000..d1161ff54cb11 --- /dev/null +++ b/clang/test/Preprocessor/init-nanvix.c @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -E -dM -ffreestanding -triple=i686-unknown-nanvix < /dev/null | FileCheck -match-full-lines -check-prefix=I686 %s +// RUN: %clang_cc1 -E -dM -ffreestanding -triple=x86_64-unknown-nanvix < /dev/null | FileCheck -match-full-lines -check-prefix=X86_64 %s + +// I686-DAG: #define __INTPTR_TYPE__ long int +// I686-DAG: #define __POINTER_WIDTH__ 32 +// I686-DAG: #define __SIZE_TYPE__ long unsigned int +// I686-DAG: #define __nanvix__ 1 +// I686-DAG: #define __unix__ 1 + +// X86_64-DAG: #define _LP64 1 +// X86_64-DAG: #define __INTPTR_TYPE__ long int +// X86_64-DAG: #define __LP64__ 1 +// X86_64-DAG: #define __POINTER_WIDTH__ 64 +// X86_64-DAG: #define __SIZE_TYPE__ long unsigned int +// X86_64-DAG: #define __nanvix__ 1 +// X86_64-DAG: #define __unix__ 1 \ No newline at end of file diff --git a/z b/z index d61615dff0224..30f3d7ac46b10 100755 --- a/z +++ b/z @@ -776,6 +776,8 @@ readonly PARALLEL_BUILD_OPT_NAME="--parallel-build" readonly RELEASE_NAME_OPT_NAME="--release-name" # Name for stage option. readonly STAGE_OPT_NAME="--stage" +# Name for target option. +readonly TARGET_OPT_NAME="--target" # Name for sysroot location option. readonly SYSROOT_LOCATION_OPT_NAME="--sysroot-location" @@ -1294,7 +1296,7 @@ Utility for building and installing ${Z_PROJECT_NAME} for Nanvix. Usage: ./z COMMAND [OPTIONS] ./z ${BUILD_CMD_NAME} - ./z ${CONFIGURE_CMD_NAME} [${PARALLEL_BUILD_OPT_NAME}=N] [${INSTALL_LOCATION_OPT_NAME}=PATH] [${SYSROOT_LOCATION_OPT_NAME}=PATH] [${STAGE_OPT_NAME}=N] + ./z ${CONFIGURE_CMD_NAME} [${PARALLEL_BUILD_OPT_NAME}=N] [${INSTALL_LOCATION_OPT_NAME}=PATH] [${SYSROOT_LOCATION_OPT_NAME}=PATH] [${STAGE_OPT_NAME}=N] [${TARGET_OPT_NAME}=TARGET] ./z ${HELP_CMD_NAME} ./z ${INSTALL_CMD_NAME} ./z ${RELEASE_CMD_NAME} @@ -1318,6 +1320,7 @@ Options: ${RELEASE_NAME_OPT_NAME}=NAME Set the release name to 'NAME' (defaults to '${default_release_name}') ${STAGE_OPT_NAME}=N Set build stage to 'N' (defaults to '${DEFAULT_STAGE}') ${SYSROOT_LOCATION_OPT_NAME}=PATH Set sysroot location to 'PATH' (defaults to '${DEFAULT_SYSROOT_LOCATION}') + ${TARGET_OPT_NAME}=TARGET Set target architecture to 'TARGET' (defaults to '${DEFAULT_TARGET}') EOF } @@ -1359,6 +1362,10 @@ _parse_project_args() { Z_STAGE="${1#*=}" shift ;; + "${TARGET_OPT_NAME}="*) + Z_TARGET="${1#*=}" + shift + ;; "${SYSROOT_LOCATION_OPT_NAME}="*) Z_SYSROOT_LOCATION="${1#*=}" shift @@ -1735,10 +1742,13 @@ BUILD_TARGETS=("all") # Install Targets INSTALL_TARGETS=("install") -# Target triple for the Nanvix toolchain. Kept in a single place so the stage 0 -# default triple, the stage 1 cross-compile target, and the compiler-rt install -# directory stay in sync with the clang driver's getTriple().str(). -readonly NANVIX_TARGET="i686-unknown-nanvix" +# Resolved target settings. Keep these in sync so the stage 0 default triple, +# stage 1 cross-compile target, Nanvix sysroot, and compiler-rt install directory +# always describe the same architecture. +NANVIX_ARCH="" +NANVIX_PROCESSOR="" +NANVIX_RELEASE_PATTERN="" +NANVIX_TARGET="" # Nanvix source tree providing the in-source libc, headers, and user linker # script. Set via Z_NANVIX_SRC. @@ -1748,12 +1758,63 @@ readonly NANVIX_LIBS=(crt0.o libc.a libm.a libc.so libm.so libnvx_crt0.a libdl.a # Nanvix release used in CI when no source tree is present: a tarball whose # root holds include/ + lib/ (libc.a/libm.a/libc.so/libm.so/crt0.o/user.ld...). readonly NANVIX_RELEASE_TAG="${Z_NANVIX_RELEASE_TAG:-v0.23.6}" -readonly NANVIX_RELEASE_PATTERN="nanvix-x86-microvm-standalone-release-*.tar.bz2" #================================================================================================== # Project-Specific Functions #================================================================================================== +# +# Description +# +# Resolves the configured target into the names used by LLVM and Nanvix. +# +resolve_nanvix_target() { + case "${Z_TARGET}" in + i686|x86|i686-nanvix|x86-nanvix|i686-unknown-nanvix) + Z_TARGET="i686-nanvix" + NANVIX_ARCH="x86" + NANVIX_PROCESSOR="i686" + NANVIX_TARGET="i686-unknown-nanvix" + ;; + x86_64|x86_64-nanvix|x86_64-unknown-nanvix) + Z_TARGET="x86_64-nanvix" + NANVIX_ARCH="x86_64" + NANVIX_PROCESSOR="x86_64" + NANVIX_TARGET="x86_64-unknown-nanvix" + ;; + *) + print_error "Unsupported target '${Z_TARGET}'. Expected 'i686-nanvix' or 'x86_64-nanvix'." + return 1 + ;; + esac + + NANVIX_RELEASE_PATTERN="nanvix-${NANVIX_ARCH}-microvm-standalone-release-*.tar.bz2" + return 0 +} + +# +# Description +# +# Verifies that a staged Nanvix ELF object has the configured ELF class. +# +validate_nanvix_elf_class() { + local object_path=$1 + local expected_class=1 + if [[ "${NANVIX_ARCH}" == "x86_64" ]]; then + expected_class=2 + fi + + local actual_class + actual_class=$(od -An -t u1 -j 4 -N 1 "${object_path}" 2>/dev/null | tr -d '[:space:]') + if [[ "${actual_class}" != "${expected_class}" ]]; then + print_error "'${object_path}' does not match ${NANVIX_TARGET}." + print_error "Build Nanvix with 'TARGET=${NANVIX_ARCH}' before configuring stage 1." + return 1 + fi + + return 0 +} + # # Description # @@ -1765,6 +1826,8 @@ readonly NANVIX_RELEASE_PATTERN="nanvix-x86-microvm-standalone-release-*.tar.bz2 # - On failure, this function returns non-zero. # download_steps() { + resolve_nanvix_target || return 1 + # Stage 0 (compiler only): no sysroot needed. if [[ "${Z_STAGE}" == "0" ]]; then print_info "Stage 0: no dependencies to download." @@ -1815,6 +1878,8 @@ download_steps() { return 1 fi + validate_nanvix_elf_class "${nanvix_src}/lib/crt0.o" || return 1 + # C headers. if [[ ! -f "${nanvix_src}/include/stdlib.h" ]]; then print_error "Nanvix headers not found at '${nanvix_src}/include'." @@ -1844,8 +1909,8 @@ download_steps() { # build path for a source checkout. if [[ -f "${nanvix_src}/lib/user.ld" ]]; then install -m 644 "${nanvix_src}/lib/user.ld" "${sysroot_location}/lib/" - elif [[ -f "${nanvix_src}/build/user/linker/x86/user.ld" ]]; then - install -m 644 "${nanvix_src}/build/user/linker/x86/user.ld" "${sysroot_location}/lib/" + elif [[ -f "${nanvix_src}/build/user/linker/${NANVIX_ARCH}/user.ld" ]]; then + install -m 644 "${nanvix_src}/build/user/linker/${NANVIX_ARCH}/user.ld" "${sysroot_location}/lib/" else print_error "Failed to locate user.ld in '${nanvix_src}'." return 1 @@ -1966,8 +2031,12 @@ configure_stage1() { -DLIBUNWIND_ENABLE_SHARED=ON -DLIBUNWIND_USE_COMPILER_RT=ON -DLIBUNWIND_IS_BAREMETAL=ON - -DLIBUNWIND_ADDITIONAL_COMPILE_FLAGS='-mno-sse;-mno-sse2' ) + if [[ "${NANVIX_ARCH}" == "x86" ]]; then + libunwind_options+=( + -DLIBUNWIND_ADDITIONAL_COMPILE_FLAGS='-mno-sse;-mno-sse2' + ) + fi # Config options for libcxxabi. local libcxxabi_options=( @@ -1989,6 +2058,7 @@ configure_stage1() { cmake -S runtimes -B build-runtimes -G Ninja \ -DCMAKE_VERBOSE_MAKEFILE=ON \ -DCMAKE_SYSTEM_NAME=Nanvix \ + -DCMAKE_SYSTEM_PROCESSOR="${NANVIX_PROCESSOR}" \ -DCMAKE_MODULE_PATH="${SCRIPT_DIR}/cmake/nanvix" \ -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \ -DCMAKE_C_COMPILER="${Z_INSTALL_LOCATION}/bin/clang" \ @@ -2022,9 +2092,11 @@ configure_stage1() { # # Usage Example # -# configure_steps "i686-nanvix" "/usr/local" +# configure_steps # configure_steps() { + resolve_nanvix_target || return 1 + if [[ "${Z_STAGE}" == "0" ]]; then configure_stage0 elif [[ "${Z_STAGE}" == "1" ]]; then @@ -2485,6 +2557,8 @@ update_steps() { # ./z verify # verify_artifacts() { + resolve_nanvix_target || return 1 + local install_location="${Z_INSTALL_LOCATION:-${DEFAULT_INSTALL_LOCATION}}" print_info "Verifying toolchain artifacts in '${install_location}'..." @@ -2622,6 +2696,8 @@ _build_shared_smoke_test() { # ./z test # test_build() { + resolve_nanvix_target || return 1 + local install_location="${Z_INSTALL_LOCATION:-${DEFAULT_INSTALL_LOCATION}}" for compiler in clang clang++; do @@ -2631,7 +2707,7 @@ test_build() { fi done - local out_dir="${PROJECT_DIR}/build/tests/smoke" + local out_dir="${PROJECT_DIR}/build/tests/smoke/${NANVIX_PROCESSOR}" mkdir -p "${out_dir}" || { print_error "Failed to create smoke-test output directory '${out_dir}'." return 1 @@ -2642,12 +2718,26 @@ test_build() { local shared_library="${out_dir}/libhello.so" _build_shared_smoke_test "${PROJECT_DIR}/.nanvix/tests/smoke/shared.c" "${shared_library}" || return 1 + local pie_option="-fPIE" + local linker_option="-pie" + if [[ "${NANVIX_ARCH}" == "x86_64" ]]; then + # The installed x86_64 crt0/libc archives use the static relocation + # model, so keep the executable fixed-address while retaining its + # DT_NEEDED dependencies. LLD must materialize ELF64 RELA addends because + # Nanvix deliberately has no system dynamic loader to do so. + pie_option="-fno-PIE" + linker_option="--apply-dynamic-relocs" + fi + local dynamic_args=( + "${pie_option}" + -nostdlib++ + "-Wl,--export-dynamic,${linker_option},--hash-style=sysv,-z,notext,-z,norelro,-Bdynamic" + ) + _build_one_smoke_test clang++ \ "${PROJECT_DIR}/.nanvix/tests/smoke/dynamic.cpp" \ "${out_dir}/hello_dynamic.elf" \ - -fPIE \ - -nostdlib++ \ - -Wl,-pie,--export-dynamic,--hash-style=sysv,-z,notext,-z,norelro,-Bdynamic \ + "${dynamic_args[@]}" \ "${shared_library}" \ "${install_location}/lib/libc++.so.1" \ "${install_location}/lib/libc++abi.so.1" \