xref: /aosp_15_r20/external/pigweed/pw_build_info/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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