diff --git a/pw_build_info/BUILD.bazel b/pw_build_info/BUILD.bazel index ddb0a8bb30..d7db7fe0bf 100644 --- a/pw_build_info/BUILD.bazel +++ b/pw_build_info/BUILD.bazel @@ -15,6 +15,7 @@ load( "//pw_build:pigweed.bzl", "pw_cc_library", + "pw_cc_test", ) package(default_visibility = ["//visibility:public"]) @@ -30,8 +31,25 @@ pw_cc_library( "public/pw_build_info/build_id.h", ], includes = ["public"], + linkopts = [ + "-Lpw_build_info", + "-T$(location add_build_id_to_default_linker_script.ld)", + "-Wl,--build-id=sha1", + ], deps = [ + ":add_build_id_to_default_linker_script.ld", + ":build_id_linker_snippet.ld", "//pw_preprocessor", "//pw_span", ], ) + +pw_cc_test( + name = "build_id_test", + srcs = ["build_id_test.cc"], + deps = [ + ":build_id", + "//pw_span", + "//pw_unit_test", + ], +) diff --git a/pw_build_info/BUILD.gn b/pw_build_info/BUILD.gn index 3764f53144..10560d84dc 100644 --- a/pw_build_info/BUILD.gn +++ b/pw_build_info/BUILD.gn @@ -31,11 +31,11 @@ config("linker_script") { # default linker script instead of overriding it. ldflags = [ "-T", - rebase_path("add_build_id_to_default_script.ld", root_build_dir), + rebase_path("add_build_id_to_default_linker_script.ld", root_build_dir), ] lib_dirs = [ "." ] - inputs += [ "add_build_id_to_default_script.ld" ] + inputs += [ "add_build_id_to_default_linker_script.ld" ] } visibility = [ ":*" ] } @@ -68,8 +68,21 @@ if (current_os != "mac" && current_os != "win") { pw_doc_group("docs") { sources = [ "docs.rst" ] - inputs = [ "build_id_linker_snippet.ld" ] + inputs = [ + "add_build_id_to_default_linker_script.ld", + "build_id_linker_snippet.ld", + ] } pw_test_group("tests") { + tests = [ ":build_id_test" ] +} + +pw_test("build_id_test") { + enable_if = current_os == "linux" + deps = [ + ":build_id", + "$dir_pw_span", + ] + sources = [ "build_id_test.cc" ] } diff --git a/pw_build_info/build_id_test.cc b/pw_build_info/build_id_test.cc new file mode 100644 index 0000000000..a3c1c06407 --- /dev/null +++ b/pw_build_info/build_id_test.cc @@ -0,0 +1,30 @@ +// Copyright 2022 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. + +#include "pw_build_info/build_id.h" + +#include "gtest/gtest.h" +#include "pw_span/span.h" + +namespace pw::build_info { +namespace { + +TEST(BuildId, BuildIdSize) { + span build_id = BuildId(); + EXPECT_GT(build_id.size(), 0u); + EXPECT_LE(build_id.size(), kMaxBuildIdSizeBytes); +} + +} // namespace +} // namespace pw::build_info diff --git a/pw_build_info/docs.rst b/pw_build_info/docs.rst index 18ac9d7bc8..4e1500fa37 100644 --- a/pw_build_info/docs.rst +++ b/pw_build_info/docs.rst @@ -5,7 +5,7 @@ pw_build_info ============= .. warning:: - This module is under construction and may not be ready for use. + This module is incomplete, but the build ID integration is ready for use. pw_build_info provides tooling, build integration, and libraries for generating, embedding, and parsing build-related information that is embedded into @@ -16,7 +16,7 @@ simplifies the process of integrating rich version metadata to answer more complex questions about compiled binaries. ------------- -GNU Build IDs +GNU build IDs ------------- This module provides C++ and python libraries for reading GNU build IDs generated by the link step of a C++ executable. These build IDs are essentially @@ -28,20 +28,91 @@ Linux executables that depend on the ``build_id`` GN target will automatically generate GNU build IDs. Windows and macOS binaries cannot use this target as the implementation of GNU build IDs depends on the ELF file format. -Embedded targets must first explicitly place the GNU build ID section into a -non-info section of their linker script that is readable by the firmware. The -following linker snippet may be copied into a read-only section (just like the -.rodata or .text sections): +Getting started +=============== +To generate GNU build IDs as part of your firmware image, you'll need to update +your embedded target's linker script. + +Updating your linker script +--------------------------- +If your project has a custom linker scipt, you'll need to update it to include +a section to contain the generated build ID. This section should be placed +alongside the ``.text`` and ``.rodata`` sections, and named +``.note.gnu.build-id``. + +.. code-block:: none + + /* Main executable code. */ + .code : ALIGN(8) + { + . = ALIGN(8); + /* Application code. */ + *(.text) + *(.text*) + KEEP(*(.init)) + KEEP(*(.fini)) + ... + } >FLASH + + /* GNU build ID section. */ + .note.gnu.build-id : + { + . = ALIGN(4); + gnu_build_id_begin = .; + *(.note.gnu.build-id); + } >FLASH + + /* Explicitly initialized global and static data. (.data) */ + .static_init_ram : ALIGN(8) + { + *(.data) + *(.data*) + ... + } >RAM AT> FLASH + + +Alternatively, you can copy the following linker snippet into a pre-existing +section. This makes reading the build ID slower, so whenever possibe prefer +creating a dedicated section for the build ID. .. literalinclude:: build_id_linker_snippet.ld -This snippet may be placed directly into an existing section, as it is not -required to live in its own dedicated section. When opting to create a -dedicated section for the build ID to reside in, Pigweed recommends naming the -section ``.note.gnu.build-id`` as it makes it slightly easier for tools to -parse the build ID out of a binary. After the linker script has been properly -set up, the ``build_id`` GN target may be used to read the build ID at -runtime. +An example of directly inserting a build ID into an existing section is +provided below: + +.. code-block:: none + + /* Main executable code. */ + .code : ALIGN(8) + { + . = ALIGN(8); + /* Application code. */ + *(.text) + *(.text*) + KEEP(*(.init)) + KEEP(*(.fini)) + + . = ALIGN(4); + gnu_build_id_begin = .; + *(.note.gnu.build-id); + + ... + } >FLASH + +If your linker script is auto-generated, you may be able to use the +``INSERT AFTER`` linker script directive to append the build ID as seen in the +Linux host support for pw_build_info's build ID integration: + +.. literalinclude:: add_build_id_to_default_linker_script.ld + +Generating the build ID +----------------------- +When you depend on ``"$dir_pw_build_info:build_id``, a GNU build ID will be +generated at the final link step of any binaries that depend on that library +(whether directly or transitively). Those binaries will be able to read the +build ID by calling ``pw::build_info::BuildId()``. Note that the build ID +is not a string, but raw binary data, so to print it you'll need to convert +it to hex or base64. Python API reference ====================