pw_rust: Add Rust support to Bazel build
Change-Id: Ibf957372e9fdf763337ec561b3c269f61583d67c
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/127270
Pigweed-Auto-Submit: Erik Gilling <konkers@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Reviewed-by: Ted Pudlik <tpudlik@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index f991592..dc101b8 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -341,9 +341,7 @@
]
if (pw_rust_ENABLE_EXPERIMENTAL_BUILD) {
- deps += [
- "$dir_pw_rust/example:hello($dir_pigweed/targets/host:host_clang_debug)",
- ]
+ deps += [ "$dir_pw_rust/examples/host_executable:hello($dir_pigweed/targets/host:host_clang_debug)" ]
}
}
diff --git a/WORKSPACE b/WORKSPACE
index 948e95b..eab85b7 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -265,6 +265,72 @@
register_gcc_arm_none_toolchain()
+# Rust Support
+#
+
+git_repository(
+ name = "rules_rust",
+ # Pulls in the main branch with https://github.com/bazelbuild/rules_rust/pull/1803
+ # merged. Once a release is cut with that commit, we should switch to
+ # using a release tarbal.
+ commit = "a5853fd37053b65ee30ba4f8064b9db67c90d53f",
+ remote = "https://github.com/bazelbuild/rules_rust",
+ shallow_since = "1675302817 -0800",
+)
+
+load("@rules_rust//rust:repositories.bzl", "rules_rust_dependencies", "rust_analyzer_toolchain_repository", "rust_repository_set")
+
+rules_rust_dependencies()
+
+# Here we pull in a specific toolchain. Unfortunately `rust_repository_set`
+# does not provide a way to add `target_compatible_with` options which are
+# needed to be compatible with `@bazel_embedded` (specifically
+# `@bazel_embedded//constraints/fpu:none` which is specified in
+# `//platforms`)
+#
+# See `//toolchain:rust_linux_x86_64` for how this is used.
+#
+# Note: This statement creates name mangled remotes of the form:
+# `@{name}__{triplet}_tools`
+# (example: `@rust_linux_x86_64__thumbv7m-none-eabi_tools/`)
+rust_repository_set(
+ name = "rust_linux_x86_64",
+ edition = "2021",
+ exec_triple = "x86_64-unknown-linux-gnu",
+ extra_target_triples = [
+ "thumbv7m-none-eabi",
+ "thumbv6m-none-eabi",
+ ],
+ versions = ["1.67.0"],
+)
+
+# Registers our Rust toolchains that are compatable with `@bazel_embedded`.
+register_toolchains(
+ "//pw_toolchain:thumbv7m_rust_linux_x86_64",
+ "//pw_toolchain:thumbv6m_rust_linux_x86_64",
+)
+
+# Allows creation of a `rust-project.json` file to allow rust analyzer to work.
+load("@rules_rust//tools/rust_analyzer:deps.bzl", "rust_analyzer_dependencies")
+
+# Since we do not use rust_register_toolchains, we need to define a
+# rust_analyzer_toolchain.
+register_toolchains(rust_analyzer_toolchain_repository(
+ name = "rust_analyzer_toolchain",
+ # This should match the currently registered toolchain.
+ version = "1.67.0",
+))
+
+rust_analyzer_dependencies()
+
+# Vendored third party rust crates.
+git_repository(
+ name = "rust_crates",
+ commit = "c39c1d1d4e4bdf2d8145beb8882af6f6e4e6dbbc",
+ remote = "https://pigweed.googlesource.com/third_party/rust_crates",
+ shallow_since = "1675359057 +0000",
+)
+
# Registers platforms for use with toolchain resolution
register_execution_platforms("//pw_build/platforms:all")
diff --git a/pw_build/constraints/board/BUILD.bazel b/pw_build/constraints/board/BUILD.bazel
index 4696f87..58f637f 100644
--- a/pw_build/constraints/board/BUILD.bazel
+++ b/pw_build/constraints/board/BUILD.bazel
@@ -28,3 +28,8 @@
name = "mimxrt595_evk",
constraint_setting = ":board",
)
+
+constraint_value(
+ name = "microbit",
+ constraint_setting = ":board",
+)
diff --git a/pw_build/constraints/chipset/BUILD.bazel b/pw_build/constraints/chipset/BUILD.bazel
index 3a2a2ea..e292906 100644
--- a/pw_build/constraints/chipset/BUILD.bazel
+++ b/pw_build/constraints/chipset/BUILD.bazel
@@ -28,3 +28,8 @@
name = "lm3s6965evb",
constraint_setting = ":chipset",
)
+
+constraint_value(
+ name = "nrf52833",
+ constraint_setting = ":chipset",
+)
diff --git a/pw_build/platforms/BUILD.bazel b/pw_build/platforms/BUILD.bazel
index 7bbc6a3..bd87ad1 100644
--- a/pw_build/platforms/BUILD.bazel
+++ b/pw_build/platforms/BUILD.bazel
@@ -99,9 +99,21 @@
parents = [":cortex_m3"],
)
+platform(
+ name = "nrf52833",
+ constraint_values = ["//pw_build/constraints/chipset:nrf52833"],
+ parents = [":cortex_m0"],
+)
+
# --- Boards ---
platform(
name = "stm32f429i-disc1",
constraint_values = ["//pw_build/constraints/board:stm32f429i-disc1"],
parents = [":stm32f429"],
)
+
+platform(
+ name = "microbit",
+ constraint_values = ["//pw_build/constraints/board:microbit"],
+ parents = [":nrf52833"],
+)
diff --git a/pw_rust/docs.rst b/pw_rust/docs.rst
index 89e9efb..b917b54 100644
--- a/pw_rust/docs.rst
+++ b/pw_rust/docs.rst
@@ -3,13 +3,42 @@
=======
pw_rust
=======
-Rust support in pigweed is ***highly*** experimental. Currently only building
-a single host binary using the standard libraries is supported. Only GN builds
-are supported and building on Windows is currently unsupported.
+Rust support in pigweed is **highly** experimental. Currently functionality
+is split between Bazel and GN support.
---------
+-----
+Bazel
+-----
+Bazel support is based on `rules_rust <https://github.com/bazelbuild/rules_rust>`_
+and supports a rich set of targets for both host and target builds.
+
+Building and Running the Embedded Example
+=========================================
+The ``embedded_hello`` example can be built for both the ``lm3s6965evb``
+and ``microbit`` QEMU machines. The example can be built and run using
+the following commands where ``PLATFORM`` is one of ``lm3s6965evb`` or
+``microbit``.
+
+.. code:: bash
+
+ $ bazel build //pw_rust/examples/embedded_hello:hello \
+ --platforms //pw_build/platforms:${PLATFORM} \
+
+ $ qemu-system-arm \
+ -machine ${PLATFORM} \
+ -nographic \
+ -semihosting-config enable=on,target=native \
+ -kernel ./bazel-bin/pw_rust/examples/embedded_hello/hello
+ Hello, Pigweed!
+
+--
+GN
+--
+In GN, currently only building a single host binary using the standard
+libraries is supported. Windows builds are currently unsupported.
+
Building
---------
+========
To build the sample rust targets, you need to enable
``pw_rust_ENABLE_EXPERIMENTAL_BUILD``:
@@ -22,5 +51,13 @@
.. code:: bash
$ ninja -C out host_clang_debug/obj/pw_rust/example/bin/hello
- $ ./out/host_clang_debug/obj/pw_rust/example/bin/hello
+ $ ./out/host_clang_debug/obj/pw_rust/examples/host_executable/bin/hello
Hello, Pigweed!
+
+------------------
+Third Party Crates
+------------------
+Thrid party crates are vendored in the
+`third_party/rust_crates <https://pigweed.googlesource.com/third_party/rust_crates>`_
+respository. Currently referencing these is only supported through the bazel
+build.
diff --git a/pw_rust/examples/embedded_hello/BUILD.bazel b/pw_rust/examples/embedded_hello/BUILD.bazel
new file mode 100644
index 0000000..e830965
--- /dev/null
+++ b/pw_rust/examples/embedded_hello/BUILD.bazel
@@ -0,0 +1,39 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+load("@rules_rust//rust:defs.bzl", "rust_binary")
+
+package(default_visibility = ["//visibility:public"])
+
+rust_binary(
+ name = "hello",
+ srcs = ["src/main.rs"],
+ edition = "2021",
+ linker_script = select({
+ "//pw_build/constraints/board:microbit": "qemu-rust-nrf51822.ld",
+ "//pw_build/constraints/chipset:lm3s6965evb": "qemu-rust-lm3s6965.ld",
+ }),
+ target_compatible_with = select({
+ "@platforms//os:linux": ["@platforms//:incompatible"],
+ "@platforms//os:macos": ["@platforms//:incompatible"],
+ "//pw_build/constraints/board:microbit": [],
+ "//pw_build/constraints/chipset:lm3s6965evb": [],
+ }),
+ deps = [
+ "@rust_crates//crates:cortex-m",
+ "@rust_crates//crates:cortex-m-rt",
+ "@rust_crates//crates:cortex-m-semihosting",
+ "@rust_crates//crates:panic-halt",
+ ],
+)
diff --git a/pw_rust/examples/embedded_hello/qemu-rust-lm3s6965.ld b/pw_rust/examples/embedded_hello/qemu-rust-lm3s6965.ld
new file mode 100644
index 0000000..7e65d25
--- /dev/null
+++ b/pw_rust/examples/embedded_hello/qemu-rust-lm3s6965.ld
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2023 The Pigweed Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/* This relatively simplified linker script will work with many ARMv7-M and
+ * ARMv8-M cores that have on-board memory-mapped RAM and FLASH. For more
+ * complex projects and devices, it's possible this linker script will not be
+ * sufficient as-is.
+ *
+ * This linker script is likely not suitable for a project with a bootloader.
+ */
+
+/* Note: This technically doesn't set the firmware's entry point. Setting the
+ * firmware entry point is done by setting vector_table[1]
+ * (Reset_Handler). However, this DOES tell the compiler how to optimize
+ * when --gc-sections is enabled.
+ */
+ENTRY(Reset)
+
+MEMORY
+{
+ /* TODO(b/234892223): Make it possible for projects to freely customize
+ * memory regions.
+ */
+
+ /* Vector Table (typically in flash) */
+ VECTOR_TABLE(rx) : ORIGIN = 0x00000000, LENGTH = 1024
+ /* Internal Flash */
+ FLASH(rx) : ORIGIN = 0x00000400, LENGTH = 255K
+ /* Internal SRAM */
+ RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 64K
+
+ /* Each memory region above has an associated .*.unused_space section that
+ * overlays the unused space at the end of the memory segment. These segments
+ * are used by pw_bloat.bloaty_config to create the utilization data source
+ * for bloaty size reports.
+ *
+ * These sections MUST be located immediately after the last section that is
+ * placed in the respective memory region or lld will issue a warning like:
+ *
+ * warning: ignoring memory region assignment for non-allocatable section
+ * '.VECTOR_TABLE.unused_space'
+ *
+ * If this warning occurs, it's also likely that LLD will have created quite
+ * large padded regions in the ELF file due to bad cursor operations. This
+ * can cause ELF files to balloon from hundreds of kilobytes to hundreds of
+ * megabytes.
+ *
+ * Attempting to add sections to the memory region AFTER the unused_space
+ * section will cause the region to overflow.
+ */
+}
+
+SECTIONS
+{
+ /* This is the link-time vector table. If used, the VTOR (Vector Table Offset
+ * Register) MUST point to this memory location in order to be used. This can
+ * be done by ensuring this section exists at the default location of the VTOR
+ * so it's used on reset, or by explicitly setting the VTOR in a bootloader
+ * manually to point to &pw_boot_vector_table_addr before interrupts are
+ * enabled.
+ *
+ * The ARMv7-M architecture requires this is at least aligned to 128 bytes,
+ * and aligned to a power of two that is greater than 4 times the number of
+ * supported exceptions. 512 has been selected as it accommodates most
+ * devices' vector tables.
+ */
+ .vector_table : ALIGN(512)
+ {
+ LONG(pw_boot_stack_high_addr);
+ pw_boot_vector_table_addr = .;
+ KEEP(*(.vector_table))
+ KEEP(*(.vector_table.reset_vector))
+ KEEP(*(.vector_table.exceptions))
+ KEEP(*(.vector_table.interrupts))
+ } >VECTOR_TABLE
+
+ /* Represents unused space in the VECTOR_TABLE segment. This MUST be the last
+ * section assigned to the VECTOR_TABLE region.
+ */
+ .VECTOR_TABLE.unused_space (NOLOAD) : ALIGN(4)
+ {
+ . = ABSOLUTE(ORIGIN(VECTOR_TABLE) + LENGTH(VECTOR_TABLE));
+ } >VECTOR_TABLE
+
+ /* Main executable code. */
+ .code : ALIGN(4)
+ {
+ . = ALIGN(4);
+ /* cortex-m-rt expects these to be nearby each other because short branches
+ * are used.
+ */
+ *(.PreResetTrampoline);
+ *(.Reset);
+ *(.HardFaultTrampoline);
+ *(.HardFault.*);
+
+ /* Application code. */
+ *(.text)
+ *(.text*)
+ KEEP(*(.init))
+ KEEP(*(.fini))
+
+ . = ALIGN(4);
+ /* Constants.*/
+ *(.rodata)
+ *(.rodata*)
+
+ /* .preinit_array, .init_array, .fini_array are used by libc.
+ * Each section is a list of function pointers that are called pre-main and
+ * post-exit for object initialization and tear-down.
+ * Since the region isn't explicitly referenced, specify KEEP to prevent
+ * link-time garbage collection. SORT is used for sections that have strict
+ * init/de-init ordering requirements. */
+ . = ALIGN(4);
+ PROVIDE_HIDDEN(__preinit_array_start = .);
+ KEEP(*(.preinit_array*))
+ PROVIDE_HIDDEN(__preinit_array_end = .);
+
+ PROVIDE_HIDDEN(__init_array_start = .);
+ KEEP(*(SORT(.init_array.*)))
+ KEEP(*(.init_array*))
+ PROVIDE_HIDDEN(__init_array_end = .);
+
+ PROVIDE_HIDDEN(__fini_array_start = .);
+ KEEP(*(SORT(.fini_array.*)))
+ KEEP(*(.fini_array*))
+ PROVIDE_HIDDEN(__fini_array_end = .);
+ } >FLASH
+
+ /* Used by unwind-arm/ */
+ .ARM : ALIGN(4) {
+ __exidx_start = .;
+ *(.ARM.exidx*)
+ __exidx_end = .;
+ } >FLASH
+
+ /* Explicitly initialized global and static data. (.data)*/
+ .static_init_ram : ALIGN(4)
+ {
+ *(.data)
+ *(.data*)
+ . = ALIGN(4);
+ } >RAM AT> FLASH
+
+ /* Represents unused space in the FLASH segment. This MUST be the last section
+ * assigned to the FLASH region.
+ */
+ .FLASH.unused_space (NOLOAD) : ALIGN(4)
+ {
+ . = ABSOLUTE(ORIGIN(FLASH) + LENGTH(FLASH));
+ } >FLASH
+
+ /* The .zero_init_ram, .heap, and .stack sections below require (NOLOAD)
+ * annotations for LLVM lld, but not GNU ld, because LLVM's lld intentionally
+ * interprets the linker file differently from ld:
+ *
+ * https://discourse.llvm.org/t/lld-vs-ld-section-type-progbits-vs-nobits/5999/3
+ *
+ * Zero initialized global/static data (.bss) is initialized in
+ * pw_boot_Entry() via memset(), so the section doesn't need to be loaded from
+ * flash. The .heap and .stack sections don't require any initialization,
+ * as they only represent allocated memory regions, so they also do not need
+ * to be loaded.
+ */
+ .zero_init_ram (NOLOAD) : ALIGN(4)
+ {
+ *(.bss)
+ *(.bss*)
+ *(COMMON)
+ . = ALIGN(4);
+ } >RAM
+
+ .heap (NOLOAD) : ALIGN(4)
+ {
+ pw_boot_heap_low_addr = .;
+ . = . + 0;
+ . = ALIGN(4);
+ pw_boot_heap_high_addr = .;
+ } >RAM
+
+ /* Link-time check for stack overlaps.
+ *
+ * The ARMv7-M architecture may require 8-byte alignment of the stack pointer
+ * rather than 4 in some contexts and implementations, so this region is
+ * 8-byte aligned (see ARMv7-M Architecture Reference Manual DDI0403E
+ * section B1.5.7).
+ */
+ .stack (NOLOAD) : ALIGN(8)
+ {
+ /* Set the address that the main stack pointer should be initialized to. */
+ pw_boot_stack_low_addr = .;
+ HIDDEN(_stack_size = ORIGIN(RAM) + LENGTH(RAM) - .);
+ /* Align the stack to a lower address to ensure it isn't out of range. */
+ HIDDEN(_stack_high = (. + _stack_size) & ~0x7);
+ ASSERT(_stack_high - . >= 1K,
+ "Error: Not enough RAM for desired minimum stack size.");
+ . = _stack_high;
+ pw_boot_stack_high_addr = .;
+ } >RAM
+
+ /* Represents unused space in the RAM segment. This MUST be the last section
+ * assigned to the RAM region.
+ */
+ .RAM.unused_space (NOLOAD) : ALIGN(4)
+ {
+ . = ABSOLUTE(ORIGIN(RAM) + LENGTH(RAM));
+ } >RAM
+
+ /* Discard unwind info. */
+ .ARM.extab 0x0 (INFO) :
+ {
+ KEEP(*(.ARM.extab*))
+ }
+}
+
+/* Symbols used by core_init.c: */
+/* Start of .static_init_ram in FLASH. */
+_pw_static_init_flash_start = LOADADDR(.static_init_ram);
+
+/* Region of .static_init_ram in RAM. */
+_pw_static_init_ram_start = ADDR(.static_init_ram);
+_pw_static_init_ram_end = _pw_static_init_ram_start + SIZEOF(.static_init_ram);
+
+/* Region of .zero_init_ram. */
+_pw_zero_init_ram_start = ADDR(.zero_init_ram);
+_pw_zero_init_ram_end = _pw_zero_init_ram_start + SIZEOF(.zero_init_ram);
+
+/* Symbols needed for the Rust cortex-m-rt crate. */
+__sbss = _pw_zero_init_ram_start;
+__ebss = _pw_zero_init_ram_end;
+__sdata = _pw_static_init_ram_start;
+__edata = _pw_static_init_ram_end;
+__sidata = LOADADDR(.static_init_ram);
+__pre_init = DefaultPreInit;
+DefaultHandler = DefaultHandler_;
+NonMaskableInt = DefaultHandler;
+MemoryManagement = DefaultHandler;
+BusFault = DefaultHandler;
+UsageFault = DefaultHandler;
+SVCall = DefaultHandler;
+DebugMonitor = DefaultHandler;
+PendSV = DefaultHandler;
+SysTick = DefaultHandler;
+HardFault = HardFault_;
+
+/* arm-none-eabi expects `end` symbol to point to start of heap for sbrk. */
+PROVIDE(end = _pw_zero_init_ram_end);
+
+/* These symbols are used by pw_bloat.bloaty_config to create the memoryregions
+ * data source for bloaty in this format (where the optional _N defaults to 0):
+ * pw_bloat_config_memory_region_NAME_{start,end}{_N,} */
+pw_bloat_config_memory_region_VECTOR_TABLE_start = ORIGIN(VECTOR_TABLE);
+pw_bloat_config_memory_region_VECTOR_TABLE_end =
+ ORIGIN(VECTOR_TABLE) + LENGTH(VECTOR_TABLE);
+pw_bloat_config_memory_region_FLASH_start = ORIGIN(FLASH);
+pw_bloat_config_memory_region_FLASH_end = ORIGIN(FLASH) + LENGTH(FLASH);
+pw_bloat_config_memory_region_RAM_start = ORIGIN(RAM);
+pw_bloat_config_memory_region_RAM_end = ORIGIN(RAM) + LENGTH(RAM);
diff --git a/pw_rust/examples/embedded_hello/qemu-rust-nrf51822.ld b/pw_rust/examples/embedded_hello/qemu-rust-nrf51822.ld
new file mode 100644
index 0000000..f8366ac
--- /dev/null
+++ b/pw_rust/examples/embedded_hello/qemu-rust-nrf51822.ld
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2023 The Pigweed Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/* This relatively simplified linker script will work with many ARMv7-M and
+ * ARMv8-M cores that have on-board memory-mapped RAM and FLASH. For more
+ * complex projects and devices, it's possible this linker script will not be
+ * sufficient as-is.
+ *
+ * This linker script is likely not suitable for a project with a bootloader.
+ */
+
+/* Note: This technically doesn't set the firmware's entry point. Setting the
+ * firmware entry point is done by setting vector_table[1]
+ * (Reset_Handler). However, this DOES tell the compiler how to optimize
+ * when --gc-sections is enabled.
+ */
+ENTRY(Reset)
+
+MEMORY
+{
+ /* TODO(b/234892223): Make it possible for projects to freely customize
+ * memory regions.
+ */
+
+ /* Vector Table (typically in flash) */
+ VECTOR_TABLE(rx) : ORIGIN = 0x00000000, LENGTH = 1024
+ /* Internal Flash */
+ FLASH(rx) : ORIGIN = 0x00000400, LENGTH = 255K
+ /* Internal SRAM */
+ RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 16K
+
+ /* Each memory region above has an associated .*.unused_space section that
+ * overlays the unused space at the end of the memory segment. These segments
+ * are used by pw_bloat.bloaty_config to create the utilization data source
+ * for bloaty size reports.
+ *
+ * These sections MUST be located immediately after the last section that is
+ * placed in the respective memory region or lld will issue a warning like:
+ *
+ * warning: ignoring memory region assignment for non-allocatable section
+ * '.VECTOR_TABLE.unused_space'
+ *
+ * If this warning occurs, it's also likely that LLD will have created quite
+ * large padded regions in the ELF file due to bad cursor operations. This
+ * can cause ELF files to balloon from hundreds of kilobytes to hundreds of
+ * megabytes.
+ *
+ * Attempting to add sections to the memory region AFTER the unused_space
+ * section will cause the region to overflow.
+ */
+}
+
+SECTIONS
+{
+ /* This is the link-time vector table. If used, the VTOR (Vector Table Offset
+ * Register) MUST point to this memory location in order to be used. This can
+ * be done by ensuring this section exists at the default location of the VTOR
+ * so it's used on reset, or by explicitly setting the VTOR in a bootloader
+ * manually to point to &pw_boot_vector_table_addr before interrupts are
+ * enabled.
+ *
+ * The ARMv7-M architecture requires this is at least aligned to 128 bytes,
+ * and aligned to a power of two that is greater than 4 times the number of
+ * supported exceptions. 512 has been selected as it accommodates most
+ * devices' vector tables.
+ */
+ .vector_table : ALIGN(512)
+ {
+ LONG(pw_boot_stack_high_addr);
+ pw_boot_vector_table_addr = .;
+ KEEP(*(.vector_table))
+ KEEP(*(.vector_table.reset_vector))
+ KEEP(*(.vector_table.exceptions))
+ KEEP(*(.vector_table.interrupts))
+ } >VECTOR_TABLE
+
+ /* Represents unused space in the VECTOR_TABLE segment. This MUST be the last
+ * section assigned to the VECTOR_TABLE region.
+ */
+ .VECTOR_TABLE.unused_space (NOLOAD) : ALIGN(4)
+ {
+ . = ABSOLUTE(ORIGIN(VECTOR_TABLE) + LENGTH(VECTOR_TABLE));
+ } >VECTOR_TABLE
+
+ /* Main executable code. */
+ .code : ALIGN(4)
+ {
+ . = ALIGN(4);
+ /* cortex-m-rt expects these to be nearby each other because short branches
+ * are used.
+ */
+ *(.PreResetTrampoline);
+ *(.Reset);
+ *(.HardFaultTrampoline);
+ *(.HardFault.*);
+
+ /* Application code. */
+ *(.text)
+ *(.text*)
+ KEEP(*(.init))
+ KEEP(*(.fini))
+
+ . = ALIGN(4);
+ /* Constants.*/
+ *(.rodata)
+ *(.rodata*)
+
+ /* .preinit_array, .init_array, .fini_array are used by libc.
+ * Each section is a list of function pointers that are called pre-main and
+ * post-exit for object initialization and tear-down.
+ * Since the region isn't explicitly referenced, specify KEEP to prevent
+ * link-time garbage collection. SORT is used for sections that have strict
+ * init/de-init ordering requirements. */
+ . = ALIGN(4);
+ PROVIDE_HIDDEN(__preinit_array_start = .);
+ KEEP(*(.preinit_array*))
+ PROVIDE_HIDDEN(__preinit_array_end = .);
+
+ PROVIDE_HIDDEN(__init_array_start = .);
+ KEEP(*(SORT(.init_array.*)))
+ KEEP(*(.init_array*))
+ PROVIDE_HIDDEN(__init_array_end = .);
+
+ PROVIDE_HIDDEN(__fini_array_start = .);
+ KEEP(*(SORT(.fini_array.*)))
+ KEEP(*(.fini_array*))
+ PROVIDE_HIDDEN(__fini_array_end = .);
+ } >FLASH
+
+ /* Used by unwind-arm/ */
+ .ARM : ALIGN(4) {
+ __exidx_start = .;
+ *(.ARM.exidx*)
+ __exidx_end = .;
+ } >FLASH
+
+ /* Explicitly initialized global and static data. (.data)*/
+ .static_init_ram : ALIGN(4)
+ {
+ *(.data)
+ *(.data*)
+ . = ALIGN(4);
+ } >RAM AT> FLASH
+
+ /* Represents unused space in the FLASH segment. This MUST be the last section
+ * assigned to the FLASH region.
+ */
+ .FLASH.unused_space (NOLOAD) : ALIGN(4)
+ {
+ . = ABSOLUTE(ORIGIN(FLASH) + LENGTH(FLASH));
+ } >FLASH
+
+ /* The .zero_init_ram, .heap, and .stack sections below require (NOLOAD)
+ * annotations for LLVM lld, but not GNU ld, because LLVM's lld intentionally
+ * interprets the linker file differently from ld:
+ *
+ * https://discourse.llvm.org/t/lld-vs-ld-section-type-progbits-vs-nobits/5999/3
+ *
+ * Zero initialized global/static data (.bss) is initialized in
+ * pw_boot_Entry() via memset(), so the section doesn't need to be loaded from
+ * flash. The .heap and .stack sections don't require any initialization,
+ * as they only represent allocated memory regions, so they also do not need
+ * to be loaded.
+ */
+ .zero_init_ram (NOLOAD) : ALIGN(4)
+ {
+ *(.bss)
+ *(.bss*)
+ *(COMMON)
+ . = ALIGN(4);
+ } >RAM
+
+ .heap (NOLOAD) : ALIGN(4)
+ {
+ pw_boot_heap_low_addr = .;
+ . = . + 0;
+ . = ALIGN(4);
+ pw_boot_heap_high_addr = .;
+ } >RAM
+
+ /* Link-time check for stack overlaps.
+ *
+ * The ARMv7-M architecture may require 8-byte alignment of the stack pointer
+ * rather than 4 in some contexts and implementations, so this region is
+ * 8-byte aligned (see ARMv7-M Architecture Reference Manual DDI0403E
+ * section B1.5.7).
+ */
+ .stack (NOLOAD) : ALIGN(8)
+ {
+ /* Set the address that the main stack pointer should be initialized to. */
+ pw_boot_stack_low_addr = .;
+ HIDDEN(_stack_size = ORIGIN(RAM) + LENGTH(RAM) - .);
+ /* Align the stack to a lower address to ensure it isn't out of range. */
+ HIDDEN(_stack_high = (. + _stack_size) & ~0x7);
+ ASSERT(_stack_high - . >= 1K,
+ "Error: Not enough RAM for desired minimum stack size.");
+ . = _stack_high;
+ pw_boot_stack_high_addr = .;
+ } >RAM
+
+ /* Represents unused space in the RAM segment. This MUST be the last section
+ * assigned to the RAM region.
+ */
+ .RAM.unused_space (NOLOAD) : ALIGN(4)
+ {
+ . = ABSOLUTE(ORIGIN(RAM) + LENGTH(RAM));
+ } >RAM
+
+ /* Discard unwind info. */
+ .ARM.extab 0x0 (INFO) :
+ {
+ KEEP(*(.ARM.extab*))
+ }
+}
+
+/* Symbols used by core_init.c: */
+/* Start of .static_init_ram in FLASH. */
+_pw_static_init_flash_start = LOADADDR(.static_init_ram);
+
+/* Region of .static_init_ram in RAM. */
+_pw_static_init_ram_start = ADDR(.static_init_ram);
+_pw_static_init_ram_end = _pw_static_init_ram_start + SIZEOF(.static_init_ram);
+
+/* Region of .zero_init_ram. */
+_pw_zero_init_ram_start = ADDR(.zero_init_ram);
+_pw_zero_init_ram_end = _pw_zero_init_ram_start + SIZEOF(.zero_init_ram);
+
+/* Symbols needed for the Rust cortex-m-rt crate. */
+__sbss = _pw_zero_init_ram_start;
+__ebss = _pw_zero_init_ram_end;
+__sdata = _pw_static_init_ram_start;
+__edata = _pw_static_init_ram_end;
+__sidata = LOADADDR(.static_init_ram);
+__pre_init = DefaultPreInit;
+DefaultHandler = DefaultHandler_;
+NonMaskableInt = DefaultHandler;
+MemoryManagement = DefaultHandler;
+BusFault = DefaultHandler;
+UsageFault = DefaultHandler;
+SVCall = DefaultHandler;
+DebugMonitor = DefaultHandler;
+PendSV = DefaultHandler;
+SysTick = DefaultHandler;
+HardFault = HardFault_;
+
+/* arm-none-eabi expects `end` symbol to point to start of heap for sbrk. */
+PROVIDE(end = _pw_zero_init_ram_end);
+
+/* These symbols are used by pw_bloat.bloaty_config to create the memoryregions
+ * data source for bloaty in this format (where the optional _N defaults to 0):
+ * pw_bloat_config_memory_region_NAME_{start,end}{_N,} */
+pw_bloat_config_memory_region_VECTOR_TABLE_start = ORIGIN(VECTOR_TABLE);
+pw_bloat_config_memory_region_VECTOR_TABLE_end =
+ ORIGIN(VECTOR_TABLE) + LENGTH(VECTOR_TABLE);
+pw_bloat_config_memory_region_FLASH_start = ORIGIN(FLASH);
+pw_bloat_config_memory_region_FLASH_end = ORIGIN(FLASH) + LENGTH(FLASH);
+pw_bloat_config_memory_region_RAM_start = ORIGIN(RAM);
+pw_bloat_config_memory_region_RAM_end = ORIGIN(RAM) + LENGTH(RAM);
diff --git a/pw_rust/examples/embedded_hello/src/main.rs b/pw_rust/examples/embedded_hello/src/main.rs
new file mode 100644
index 0000000..51028c4
--- /dev/null
+++ b/pw_rust/examples/embedded_hello/src/main.rs
@@ -0,0 +1,32 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#![no_main]
+#![no_std]
+
+// Panic handler that halts the CPU on panic.
+use panic_halt as _;
+
+// Cortex M runtime entry macro.
+use cortex_m_rt::entry;
+
+// Semihosting support which is well supported for QEMU targets.
+use cortex_m_semihosting::{debug, hprintln};
+
+#[entry]
+fn main() -> ! {
+ hprintln!("Hello, Pigweed!");
+ debug::exit(debug::EXIT_SUCCESS);
+ loop {}
+}
diff --git a/pw_rust/example/BUILD.gn b/pw_rust/examples/host_executable/BUILD.gn
similarity index 100%
rename from pw_rust/example/BUILD.gn
rename to pw_rust/examples/host_executable/BUILD.gn
diff --git a/pw_rust/example/main.rs b/pw_rust/examples/host_executable/main.rs
similarity index 100%
rename from pw_rust/example/main.rs
rename to pw_rust/examples/host_executable/main.rs
diff --git a/pw_toolchain/BUILD.bazel b/pw_toolchain/BUILD.bazel
index 15510fd..5f160c1 100644
--- a/pw_toolchain/BUILD.bazel
+++ b/pw_toolchain/BUILD.bazel
@@ -17,6 +17,7 @@
"pw_cc_library",
"pw_cc_test",
)
+load(":rust_toolchain.bzl", "pw_rust_toolchain")
package(default_visibility = ["//visibility:public"])
@@ -43,3 +44,22 @@
linkopts = ["-Wl,--wrap=abort"],
deps = ["//pw_assert"],
)
+
+# Define rust toolchains that are compatable with @bazel_embedded.
+pw_rust_toolchain(
+ name = "thumbv7m_rust_linux_x86_64",
+ exec_cpu = "x86_64",
+ exec_os = "linux",
+ exec_triple = "x86_64-unknown-linux-gnu",
+ rust_target_triple = "thumbv7m-none-eabi",
+ target_cpu = "armv7-m",
+)
+
+pw_rust_toolchain(
+ name = "thumbv6m_rust_linux_x86_64",
+ exec_cpu = "x86_64",
+ exec_os = "linux",
+ exec_triple = "x86_64-unknown-linux-gnu",
+ rust_target_triple = "thumbv6m-none-eabi",
+ target_cpu = "armv6-m",
+)
diff --git a/pw_toolchain/rust_toolchain.bzl b/pw_toolchain/rust_toolchain.bzl
new file mode 100644
index 0000000..32b5ce4
--- /dev/null
+++ b/pw_toolchain/rust_toolchain.bzl
@@ -0,0 +1,48 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Utilities for declaring Rust toolchains that are compatible with @bazel_embedded"""
+
+load("@rules_rust//rust:toolchain.bzl", "rust_toolchain")
+
+def pw_rust_toolchain(name, exec_cpu, exec_os, target_cpu, rust_target_triple, exec_triple):
+ proxy_toolchain = "@rust_{}_{}__{}__stable_tools".format(exec_os, exec_cpu, rust_target_triple)
+
+ rust_toolchain(
+ name = "{}_impl".format(name),
+ binary_ext = "",
+ dylib_ext = ".so",
+ exec_triple = exec_triple,
+ os = "none",
+ rust_doc = "{}//:rustdoc".format(proxy_toolchain),
+ rust_std = "{}//:rust_std-{}".format(proxy_toolchain, rust_target_triple),
+ rustc = "{}//:rustc".format(proxy_toolchain),
+ rustc_lib = "{}//:rustc_lib".format(proxy_toolchain),
+ staticlib_ext = ".a",
+ stdlib_linkflags = [],
+ target_triple = rust_target_triple,
+ )
+
+ native.toolchain(
+ name = name,
+ exec_compatible_with = [
+ "@platforms//cpu:{}".format(exec_cpu),
+ "@platforms//os:{}".format(exec_os),
+ ],
+ target_compatible_with = [
+ "@platforms//cpu:{}".format(target_cpu),
+ "@bazel_embedded//constraints/fpu:none",
+ ],
+ toolchain = ":{}_impl".format(name),
+ toolchain_type = "@rules_rust//rust:toolchain",
+ )