| .. _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(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 |
| |
| 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 |
| ==================== |
| |
| .. 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 |