1.. _module-pw_build_info: 2 3============= 4pw_build_info 5============= 6.. pigweed-module:: 7 :name: pw_build_info 8 9pw_build_info provides tooling, build integration, and libraries for generating, 10embedding, and parsing build-related information that is embedded into 11binaries. Simple numeric version numbering doesn't typically express things 12like where the binary originated, what devices it's compatible with, whether 13local changes were present when the binary was built, and more. pw_build_info 14simplifies the process of integrating rich version metadata to answer more 15complex questions about compiled binaries. 16 17.. _module-pw_build_info-gnu-build-ids: 18 19------------- 20GNU build IDs 21------------- 22This module provides C++ and python libraries for reading GNU build IDs 23generated by the link step of a C++ executable. These build IDs are essentially 24hashes of the final linked binary, meaning two identical binaries will have 25identical build IDs. This can be used to accurately identify matching 26binaries. 27 28Linux executables that depend on the ``build_id`` GN target will automatically 29generate GNU build IDs. Windows and macOS binaries cannot use this target as 30the implementation of GNU build IDs depends on the ELF file format. 31 32A separate GN target ``build_id_or_noop`` is available which provides an empty 33build ID on platforms where GNU build ID is not available while still providing 34a real GNU build ID where supported. 35 36Getting started 37=============== 38To generate GNU build IDs as part of your firmware image, you'll need to update 39your embedded target's linker script. 40 41Updating your linker script 42--------------------------- 43If your project has a custom linker script, you'll need to update it to include 44a section to contain the generated build ID. This section should be placed 45alongside the ``.text`` and ``.rodata`` sections, and named 46``.note.gnu.build-id``. 47 48.. code-block:: none 49 50 /* Main executable code. */ 51 .code : ALIGN(4) 52 { 53 . = ALIGN(4); 54 /* Application code. */ 55 *(.text) 56 *(.text*) 57 KEEP(*(.init)) 58 KEEP(*(.fini)) 59 ... 60 } >FLASH 61 62 /* GNU build ID section. */ 63 .note.gnu.build-id : 64 { 65 . = ALIGN(4); 66 gnu_build_id_begin = .; 67 *(.note.gnu.build-id); 68 } >FLASH 69 70 /* Explicitly initialized global and static data. (.data) */ 71 .static_init_ram : ALIGN(4) 72 { 73 *(.data) 74 *(.data*) 75 ... 76 } >RAM AT> FLASH 77 78 79Alternatively, you can copy the following linker snippet into a pre-existing 80section. This makes reading the build ID slower, so whenever possible prefer 81creating a dedicated section for the build ID. 82 83.. literalinclude:: build_id_linker_snippet.ld 84 85An example of directly inserting a build ID into an existing section is 86provided below: 87 88.. code-block:: none 89 90 /* Main executable code. */ 91 .code : ALIGN(4) 92 { 93 . = ALIGN(4); 94 /* Application code. */ 95 *(.text) 96 *(.text*) 97 KEEP(*(.init)) 98 KEEP(*(.fini)) 99 100 . = ALIGN(4); 101 gnu_build_id_begin = .; 102 *(.note.gnu.build-id); 103 104 ... 105 } >FLASH 106 107If your linker script is auto-generated, you may be able to use the 108``INSERT AFTER`` linker script directive to append the build ID as seen in the 109Linux host support for pw_build_info's build ID integration: 110 111.. literalinclude:: add_build_id_to_default_linker_script.ld 112 113Generating the build ID 114----------------------- 115When you depend on ``"$dir_pw_build_info:build_id``, a GNU build ID will be 116generated at the final link step of any binaries that depend on that library 117(whether directly or transitively). Those binaries will be able to read the 118build ID by calling ``pw::build_info::BuildId()``. Note that the build ID 119is not a string, but raw binary data, so to print it you'll need to convert 120it to hex or base64. It is possible to call ``pw::build_info::LogBuildId()`` 121function to print it (as hexadecimal). 122 123Python API reference 124==================== 125 126.. py:function:: read_build_id_from_section(elf_file: BinaryIO) -> \ 127 bytes | None 128 129 Reads a GNU build ID from an ELF binary by searching for a 130 ``.note.gnu.build-id`` section. 131 132.. py:function:: read_build_id_from_symbol(elf_file: BinaryIO) -> \ 133 bytes | None 134 135 Reads a GNU build ID from an ELF binary by searching for a 136 ``gnu_build_id_begin`` symbol. This can be a rather slow operation. 137 138.. py:function:: read_build_id(elf_file: BinaryIO) -> bytes | None 139 140 Reads a GNU build ID from an ELF binary, first checking for a GNU build ID 141 section and then falling back to search for a ``gnu_build_id_begin`` symbol. 142 143.. py:function:: find_matching_elf(uuid: bytes, search_dir: Path) -> \ 144 Path | None 145 146 Recursively searches a directory for an ELF file with a matching UUID. 147 148 Warning: This can take on the order of several seconds. 149 150Python utility 151============== 152GNU build IDs can be parsed out of ELF files using the ``build_id`` python tool. 153Simply point the tool to a binary with a GNU build ID and the build ID will be 154printed out if it is found. 155 156.. code-block:: sh 157 158 $ python -m pw_build_info.build_id my_device_image.elf 159 d43cce74f18522052f77a1fa3fb7a25fe33f40dd 160 161--------------- 162Git Commit Info 163--------------- 164If your project uses ``git`` and builds with ``bazel``, you can use the 165auto-generated ``//pw_build_info:git_build_info`` header to embed which git 166commit your binary was built from. This requires a bit of setup to use. 167 168Bazel Workspace Status Command 169============================== 170Bazel supports running a command to inspect the current workspace status each 171build. Add the following to your ``.bazelrc,`` replacing the ``path/to/pigweed`` 172portion with where you have pigweed checked out. 173 174.. code-block:: 175 176 build --workspace_status_command=path/to/pigweed/pw_build_info/git_workspace_status_command.sh 177 178Use ``pw_build_info/git_build_info.h`` Header 179============================================= 180Add generated header directly to the target where you want to use it. 181 182.. code-block:: python 183 184 cc_binary( 185 name = "main", 186 srcs = [ 187 "main.cc", 188 "@pigweed//pw_build_info:git_build_info", 189 ] 190 ) 191 192Include the header. The following constants are available: 193 194* ``pw::build_info::kGitCommit``: The git commit this binary was built from. 195* ``pw::build_info::kGitTreeDirty``: True if there were any uncommitted changes. 196 197.. code-block:: cpp 198 199 #include "pw_build_info/git_build_info.h" 200 #include "pw_string/string.h" 201 #include "pw_log/log.h" 202 203 int main() { 204 PW_LOG_INFO("kGitCommit %s", pw::InlineString<40>(pw::build_info::kGitCommit).c_str()); 205 PW_LOG_INFO("kGitTreeDirty %d", pw::build_info::kGitTreeDirty); 206 return 0; 207 } 208 209More info about `bazel workspace status commands <https://bazel.build/docs/user-manual#workspace-status-command>`__. 210