Skip to content

Add support for the CHERI-LLVM toolchain for RV32 baremetal apps #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions config/registration.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

load("@crt//toolchains/gcc_arm_none_eabi:repository.bzl", "gcc_arm_none_eabi_repos")
load("@crt//toolchains/lowrisc_rv32imcb:repository.bzl", "lowrisc_rv32imcb_repos")
load("@crt//toolchains/cheri_llvm:repository.bzl", "cheri_llvm_repos")
load("@crt//toolchains/gcc_mxe_mingw64:repository.bzl", "gcc_mxe_mingw64_repos")
load("@crt//toolchains/cc65:repository.bzl", "cc65_repos")

def crt_register_toolchains(
arm = False,
m6502 = False,
riscv32 = False,
cheri_llvm = False,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be named something like riscv32_cheri.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CHERI LLVM toolchain can also generate riscv64 binaries, and even mips64 binaries (although this is almost never used nowadays) so I would hesitate to tie the name to riscv32.
The steps described in the README for building the archive specifically build libraries etc. that target riscv32+cheri, but they could be extended to also build the requirements for riscv64+cheri baremetal, mips64+cheri baremetal or even vanilla riscv32/64 and the built archive could then be used for any of those targets, so I think a more generic toolchain name is more appropriate.
Let me know your thoughts.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What we're adding in this PR is a particular toolchain configuration that informs bazel about how to select and run the compiler to emit riscv32 code with cheri features.

When/if we get to the point of having riscv64+cheri or mips64+cheri or x86_64+cheri, we'll need to add those as separate configurations (note that these configurations could indeed be backed by a single compiler and appropriate constraints to select the proper feature set per configuration).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the risk of annoying your half-to-death with requests to change the directory structure, it sounds like it might be appropriate to change platforms/riscv32/cheri/... to platforms/cheri/riscv32/.... That would allow for the creation of a riscv64, mips64 and whatever else under the platforms/cheri directory.

I still think the appropriate name here (in crt_register_toolchains) is a combination of cheri and the architecture name. Even if you did eventually support a bunch of architectures with cheri options, downstream projects would probably only want to enable the configuration that was appropriate to their CPU architecture.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, this makes sense. I've committed changes to rename the argument cheri_llvm to cheri_riscv32 (the toolchain is still called cheri_llvm) and I've moved platforms/riscv32/cheri -> platforms/cheri/riscv32.

win64 = False):
native.register_execution_platforms("@local_config_platform//:host")
if arm:
Expand All @@ -28,6 +30,11 @@ def crt_register_toolchains(
native.register_execution_platforms("@crt//platforms/riscv32:all")
native.register_toolchains("@crt//toolchains/lowrisc_rv32imcb:all")

if cheri_llvm:
cheri_llvm_repos()
native.register_execution_platforms("@crt//platforms/riscv32-cheri:all")
native.register_toolchains("@crt//toolchains/cheri_llvm:all")

if win64:
gcc_mxe_mingw64_repos()
native.register_execution_platforms("@crt//platforms/x86_64:all")
Expand Down
11 changes: 11 additions & 0 deletions platforms/riscv32-cheri/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package(default_visibility = ["//visibility:public"])

load("//config:execution.bzl", "exec_config")

platform(
name = "riscv32-cheri",
constraint_values = [
"@platforms//cpu:riscv32",
"//platforms/riscv32-cheri/extension:cheri",
],
)
20 changes: 20 additions & 0 deletions platforms/riscv32-cheri/devices.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
load("//config:device.bzl", "device_config")

DEVICES = [
device_config(
name = "riscv32-cheri",
architecture = "rv32imcxcheri",
feature_set = "//platforms/riscv32-cheri/features:rv32imcxcheri",
constraints = [
"@platforms//cpu:riscv32",
"//platforms/riscv32-cheri/extension:cheri",
],
substitutions = {
"ARCHITECTURE": "rv32imcxcheri",
"ABI": "il32pc64",
"CMODEL": "medany",
"ENDIAN": "little",
"[STACK_PROTECTOR]": "",
},
),
]
8 changes: 8 additions & 0 deletions platforms/riscv32-cheri/extension/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package(default_visibility = ["//visibility:public"])

constraint_setting(name = "extension")

constraint_value(
name = "cheri",
constraint_setting = ":extension",
)
67 changes: 67 additions & 0 deletions platforms/riscv32-cheri/features/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
load(
"//config:features.bzl",
"CPP_ALL_COMPILE_ACTIONS",
"C_ALL_COMPILE_ACTIONS",
"LD_ALL_ACTIONS",
"feature",
"feature_set",
"flag_group",
"flag_set",
)

package(default_visibility = ["//visibility:public"])

feature(
name = "architecture",
enabled = True,
flag_sets = [
flag_set(
actions = CPP_ALL_COMPILE_ACTIONS + C_ALL_COMPILE_ACTIONS + LD_ALL_ACTIONS,
flag_groups = [
flag_group(
flags = [
"-march=ARCHITECTURE",
"-mabi=ABI",
"-mcmodel=CMODEL",
"-mENDIAN-endian",
"--target=riscv32-unknown-elf",
"-mno-relax",
# tell the loader where the base libraries are
"-Lexternal/cheri_llvm_files/sdk/baremetal/baremetal-riscv32-purecap/riscv32-unknown-elf/lib/",
],
),
],
),
],
)

feature(
name = "fastbuild",
enabled = False,
flag_sets = [
flag_set(
actions = CPP_ALL_COMPILE_ACTIONS + C_ALL_COMPILE_ACTIONS,
flag_groups = [
flag_group(
flags = [
"-Os",
"-g",
],
),
],
),
],
provides = ["compilation_mode"],
)

feature_set(
name = "rv32imcxcheri",
base = [
"//features/common",
"//features/embedded",
],
feature = [
":architecture",
":fastbuild",
],
)
41 changes: 41 additions & 0 deletions toolchains/cheri_llvm/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
load("//config:compiler.bzl", "setup")
load("//platforms/riscv32-cheri:devices.bzl", "DEVICES")

package(default_visibility = ["//visibility:public"])

SYSTEM_INCLUDE_PATHS = [
"external/cheri_llvm_files/sdk/lib/clang/13.0.0/include",
"external/cheri_llvm_files/sdk/baremetal/baremetal-riscv32-purecap/riscv32-unknown-elf/include/",
]

filegroup(
name = "compiler_components",
srcs = [
"//toolchains/cheri_llvm/wrappers:all",
"@cheri_llvm_files//:all",
],
)

[setup(
name = device.name,
architecture = device.architecture,
artifact_naming = device.artifact_naming,
compiler_components = ":compiler_components",
constraints = device.constraints,
feature_set = device.feature_set,
include_directories = SYSTEM_INCLUDE_PATHS,
params = {
"compiler": "clang",
},
substitutions = device.substitutions,
tools = {
"ar": "wrappers/ar",
"cpp": "wrappers/cpp",
"gcc": "wrappers/clang",
"ld": "wrappers/ld",
"nm": "wrappers/nm",
"objcopy": "wrappers/objcopy",
"objdump": "wrappers/objdump",
"strip": "wrappers/strip",
},
) for device in DEVICES]
1 change: 1 addition & 0 deletions toolchains/cheri_llvm/archive/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package(default_visibility = ["//visibility:public"])
23 changes: 23 additions & 0 deletions toolchains/cheri_llvm/archive/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
### Instructions for building the CHERI-LLVM toolchain archive for purecap riscv32

This will use [cheribuild](https://github.com/CTSRD-CHERI/cheribuild) to build
the CHERI-LLVM RISC-V 32bit "purecap" toolchain for embedded development.
By default, the sources and toolchain will be fetched and built in `~/cheri/`.
This can be changed using the `--source-root`, `--output-root`, `--build-root`,
and `--tools-root` arguments. `-d` ensures required steps are executed (i.e.
builds LLVM & Clang before compiling the libraries). `-j` controls the number
of threads to be used for compilation.

Clone the cheribuild repository anywhere (its directory will not be used for building), then:

`./cheribuild.py newlib-baremetal-riscv32-purecap -d` (for libc, libm, libg)
`./cheribuild.py compiler-rt-builtins-baremetal-riscv32-purecap -d` (for builtins)

Then `cd` to wherever the sdk folder is (`~/cheri/output/` by default,
or within either of the directories pointed to when passing
`--tool-root` or `--output-root`) and tar the `sdk` folder:
`tar -cvzf cheri_llvm_sdk.tar.gz sdk/`

Lastly, copy `cheri_llvm_sdk.tar.gz` to this folder. The name must match the
filename in the `archive` field in `../repository.bzl`
Comment on lines +21 to +22
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should discuss how to allow you to provide the compiler archive without requiring it to be in this repository or requiring the user to take a repository modifying action (e.g. copying the file into this subdir). Modifying the repository as a build step is "bad".

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been thinking about how to allow you to supply the compiler archive without needing to ask the user to modify the repository. There are two mechanisms I can think of:

  1. Supply the repository yourself and forego the call to cheri_llvm_repos() in crt_register_toolchains. In your WORKSPACE, you could then do something like this:
load("//third_party/crt:repos.bzl", "crt_repos")
crt_repos()
load("@crt//:repos.bzl", "crt_repos")
crt_repos()
load("@crt//:deps.bzl", "crt_deps")
crt_deps()

load("@crt//config:repo.bzl", "compiler_repository")
compiler_repository(
    name = "cheri_llvm_files",
    archive = "/path/to/your/cheri_llvm_sdk.tar.gz",
    exports = [ ... ],
)

load("@crt//config:registration.bzl", "crt_register_toolchains")
crt_register_toolchains(riscv32_cheri = True)
  1. Modify the interface to crt_register_toolchains to allow you to pass the path to the compiler archive. Your WORKSPACE would then have something like:
crt_register_toolchains(
    riscv32_cheri = "/path/to/your/cheri_llvm_sdk.tar.gz",
)

crt_register_toolchains can then pass the archive location on to cheri_llvm_repos() which will instantiate the compiler_repository.

I've been considering something like (2) for a while to enable end users to pass in alternate compilers, but I haven't yet had a need for it and I'd probably want to plumb things up a bit more to make the archive properties more configurable.


8 changes: 8 additions & 0 deletions toolchains/cheri_llvm/repository.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
load("@crt//config:repo.bzl", "compiler_repository")

def cheri_llvm_repos():
compiler_repository(
name = "cheri_llvm_files",
archive = "@crt//toolchains/cheri_llvm/archive:cheri_llvm_sdk.tar.gz",
exports = ["sdk/bin/**"],
)
17 changes: 17 additions & 0 deletions toolchains/cheri_llvm/wrappers/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package(default_visibility = ["//visibility:public"])

exports_files(glob(["*"]))

filegroup(
name = "all",
srcs = [
"ar",
"clang",
"cpp",
"ld",
"nm",
"objcopy",
"objdump",
"strip",
],
)
1 change: 1 addition & 0 deletions toolchains/cheri_llvm/wrappers/ar
1 change: 1 addition & 0 deletions toolchains/cheri_llvm/wrappers/clang
1 change: 1 addition & 0 deletions toolchains/cheri_llvm/wrappers/cpp
13 changes: 13 additions & 0 deletions toolchains/cheri_llvm/wrappers/driver.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash --norc

PROG=${0##*/}
TOOLCHAIN="cheri_llvm_files"
VERSION="13.0.0"

ARGS=()
POSTARGS=()

exec "external/${TOOLCHAIN}/sdk/bin/${PROG}" \
"${ARGS[@]}" \
"$@"\
"${POSTARGS[@]}"
1 change: 1 addition & 0 deletions toolchains/cheri_llvm/wrappers/ld
1 change: 1 addition & 0 deletions toolchains/cheri_llvm/wrappers/nm
1 change: 1 addition & 0 deletions toolchains/cheri_llvm/wrappers/objcopy
1 change: 1 addition & 0 deletions toolchains/cheri_llvm/wrappers/objdump
1 change: 1 addition & 0 deletions toolchains/cheri_llvm/wrappers/strip