154 lines
4.7 KiB
ReStructuredText
154 lines
4.7 KiB
ReStructuredText
.. _module-pw_build_info:
|
|
|
|
=============
|
|
pw_build_info
|
|
=============
|
|
|
|
.. warning::
|
|
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
|
|
binaries. Simple numeric version numbering doesn't typically express things
|
|
like where the binary originated, what devices it's compatible with, whether
|
|
local changes were present when the binary was built, and more. pw_build_info
|
|
simplifies the process of integrating rich version metadata to answer more
|
|
complex questions about compiled binaries.
|
|
|
|
-------------
|
|
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
|
|
hashes of the final linked binary, meaning two identical binaries will have
|
|
identical build IDs. This can be used to accurately identify matching
|
|
binaries.
|
|
|
|
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.
|
|
|
|
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(4)
|
|
{
|
|
. = ALIGN(4);
|
|
/* 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(4)
|
|
{
|
|
*(.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
|
|
|
|
An example of directly inserting a build ID into an existing section is
|
|
provided below:
|
|
|
|
.. code-block:: none
|
|
|
|
/* Main executable code. */
|
|
.code : ALIGN(4)
|
|
{
|
|
. = ALIGN(4);
|
|
/* 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
|
|
====================
|
|
|
|
.. py:function:: read_build_id_from_section(elf_file: BinaryIO) -> \
|
|
Optional[bytes]
|
|
|
|
Reads a GNU build ID from an ELF binary by searching for a
|
|
``.note.gnu.build-id`` section.
|
|
|
|
.. py:function:: read_build_id_from_symbol(elf_file: BinaryIO) -> \
|
|
Optional[bytes]
|
|
|
|
Reads a GNU build ID from an ELF binary by searching for a
|
|
``gnu_build_id_begin`` symbol. This can be a rather slow operation.
|
|
|
|
.. py:function:: read_build_id(elf_file: BinaryIO) -> Optional[bytes]
|
|
|
|
Reads a GNU build ID from an ELF binary, first checking for a GNU build ID
|
|
section and then falling back to search for a ``gnu_build_id_begin`` symbol.
|
|
|
|
.. py:function:: find_matching_elf(uuid: bytes, search_dir: Path) -> \
|
|
Optional[Path]
|
|
|
|
Recursively searches a directory for an ELF file with a matching UUID.
|
|
|
|
Warning: This can take on the order of several seconds.
|
|
|
|
Python utility
|
|
==============
|
|
GNU build IDs can be parsed out of ELF files using the ``build_id`` python tool.
|
|
Simply point the tool to a binary with a GNU build ID and the build ID will be
|
|
printed out if it is found.
|
|
|
|
.. code-block:: sh
|
|
|
|
$ python -m pw_build_info.build_id my_device_image.elf
|
|
d43cce74f18522052f77a1fa3fb7a25fe33f40dd
|