Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: A high-level mechanism for embedding raw data from binary files #86204

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

hIAhmad
Copy link
Contributor

@hIAhmad hIAhmad commented Feb 24, 2025

Adding CMake functions generate_binary_embed_inc_* to introduce a portable
high-level build mechanism for embedding raw binary data.

These functions enable including raw data from one or more binary files,
along with desired symbol names for corresponding data being embedded.
Controlling aspects such as address alignment, linker section, inclusion
as read-only (default) or as writable, data compression and including only
a subset of data from the specified binary files, is supported through
named optional parameters.

These functions provide a higher-level embedding mechanism compared to
generate_inc_* functions, enabling advanced use cases like:

  • Embedding of data from more than one files, where the list and number
    of files being included is determined at build time, e.g. driven by
    some device-tree fields or config options. generate_inc_* functions
    cannot handle such use-cases without a fair amount of additional custom
    support code added to corresponding CMake build files.
    (See drivers: ramdisk: Add support for embedded disk images as backing store of RAM disks #82603
    for an example use-case that will be greatly simplified, if this proposed
    generic mechanism becomes available).

  • generate_inc_* functions embed binary data by first converting it
    into C-source compilable form; which is fine for small binary files
    but for larger files this can increase build time and resources usage
    significantly. Whereas these new functions can transparently make use
    of GNU-style "incbin" directive, when available (and usable), for much
    faster inclusion of binary files, automatically falling back to using
    generate_inc_* functions internally, for other cases.

Testing:

  • Tested on following platforms:
    native_sim
    qemu_x86
    qemu_cortex_a9
    qemu_cortex_a53
    qemu_cortex_m3
    qemu_cortex_r5
    qemu_riscv32
    qemu_riscv64
    qemu_xtensa/dc233c
    qemu_arc/qemu_arc_hs
    
  • Tested both with and without Zephyr CMake linker generator feature (on qemu_cortex_m3).

Related PRs:

Note:
Changes to include/zephyr/toolchain/common.h and scripts/build/file2hex.py are not part of this PR - they are coming from the separate PRs that this PR depends on. I've added them here to get early feedback on rest of the feature changeset. These changes will be removed from this PR (through rebase) once the respective PRs get approved and merged to main.

" */\n")

if(USE_INCBIN)
file(WRITE ${GENERATED_FILE}.S "/*\n"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 space indent for cmake

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

# ALIGN - Alignment for data being embedded in bytes. Default is 4.
# SECTION - Name of the desired linker section. Data from all specified
# binary files will be placed in the given section name.
# ZIP - Compress the data with specified compression method: gzip or no (default).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gzip != zip, and don't think that has a place here, if you need a zip then the zip file should be present and provided to the function (or compressed manually with a user build step then included)

Copy link
Contributor Author

@hIAhmad hIAhmad Feb 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, ZIP as the name of the parameter is a bit arguable. While the word zip can sometimes be used as a generic word for compression it is also a name for a specific compression method. COMPRESS can be an alternative name here, that does not suffer from this ambiguity.

Now, regarding your other point - whether this parameter should even be provided in first place. It is true that a file can be compressed in a separate build step, prior to passing it on to generate_binary_embed_inc_file but its lower-level counterpart generate_inc_file already provides this capability so I think it is better to preserve the existing capability of that function in this higher-level wrapper (and it is certainly more convenient than having to do it in a separate build step).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have renamed the ZIP parameter to COMPRESS.

# specified files should be started.
# LENGTH - Length, in bytes, of the data subset to be included from
# the specified files.
# READONLY - yes (default) or no.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be a flag and should be CONST not READONLY

Copy link
Contributor Author

@hIAhmad hIAhmad Feb 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be a flag and should be CONST not READONLY

I am a bit divided on the name. I had thought about naming it CONST but then felt that the parameter name should better reflect the eventual logical effect that normal/intended use of the parameter should achieve, rather than an implementation detail so chose READONLY instead. Let's wait for some more feedback on the naming and then consider renaming it accordingly.

I'll make it a flag right away, though. (see below: #86204 (comment)).

Copy link
Contributor Author

@hIAhmad hIAhmad Feb 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nordicjm There is a downside to making this parameter a flag. By default, you'd typically include/embed binary data as read-only, and not as writable. So if we make this a flag, it will always have to be specified for the common and typical case. By making it take a value, you need to specify it only if you want to make the included data writable (which is relatively a rare case, currently, #82603 would be likely the only such practical use in the source tree).

I think it is better to have it take a value.

Alternatively, we can invert the logic here and instead of READONLY, make it WRITABLE. Then we can make it a flag without having to specify it for the common, default case.

Thoughts?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I find problematic is that the only documentation for generate_binary_embed_inc_file_for_target() and friends is:

# READONLY - yes (default) or no.

This tells me nothing about what READONLY actually means, I need to go look at the script which generates the including code.

This needs to be improved, and in this context I believe using the flag / parameter name CONST will ease the understanding of the flag.

@hIAhmad hIAhmad force-pushed the gen_binary_embed_inc_file branch 2 times, most recently from ac3355f to afd4afb Compare February 26, 2025 10:31
@hIAhmad hIAhmad force-pushed the gen_binary_embed_inc_file branch from afd4afb to 2315b09 Compare March 7, 2025 19:14
hIAhmad added 2 commits March 24, 2025 23:09
Adding CMake functions generate_binary_embed_inc_* to introduce a portable
high-level build mechanism for embedding raw binary data.

These functions enable including raw data from one or more binary files,
along with desired symbol names for corresponding data being embedded.
Controlling aspects such as address alignment, linker section, inclusion
as read-only (default) or as writable, data compression and including only
a subset of data from the specified binary files, is supported through
named optional parameters.

These functions provide a higher-level embedding mechanism compared to
generate_inc_* functions, enabling advanced use cases like:

  - Embedding of data from more than one files, where the list and number
    of files being included is determined at build time, e.g. driven by
    some device-tree fields or config options. generate_inc_* functions
    cannot handle such use-cases without a fair amount of additional custom
    support code added to corresponding CMake build files.

  - generate_inc_* functions embed binary data by first converting it
    into C-source compilable form; which is fine for small binary files
    but for larger files this can increase build time and resources usage
    significantly. Whereas these new functions can transparently make use
    of GNU-style "incbin" directive, when available (and usable), for much
    faster inclusion of binary files, automatically falling back to using
    generate_inc_* functions internally, for other cases.

Signed-off-by: Irfan Ahmad <irfan.ahmad@siemens.com>
Add a test that exercises generate_binary_embed_inc_* CMake functions to
embed raw binary data from two test binary files, into an application, in
a comprehensive set of permutations of supported parameters.

Signed-off-by: Irfan Ahmad <irfan.ahmad@siemens.com>
@hIAhmad hIAhmad force-pushed the gen_binary_embed_inc_file branch from 2315b09 to eae0319 Compare March 24, 2025 18:10
@hIAhmad
Copy link
Contributor Author

hIAhmad commented Mar 24, 2025

@tejlmand, @nashif, @nordicjm The dependencies of this PR are now out of the way (approved and merged). It would be great if you can spare some time to review/re-review this PR.

@57300 57300 removed their request for review April 1, 2025 19:02
Copy link
Collaborator

@tejlmand tejlmand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the improved version.

Still a little in doubt on the .incbin, but that's for another discussion.
First round reviewing the implementation itself.

# Required named parameters:
#
# OUTPUT - The generated file.
# BINARY_FILES - List of binary files to be converted into embeddable form.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps just files. The function name, function and parameter description already make it clear that the function works with binary files

Suggested change
# BINARY_FILES - List of binary files to be converted into embeddable form.
# FILES - List of binary files to be converted into embeddable form.

Note: not blocking comment.

Comment on lines +755 to +758
# OFFSET - Offset, in bytes, from which data inclusion from the
# specified files should be started.
# LENGTH - Length, in bytes, of the data subset to be included from
# the specified files.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be too limited.
If wanting to support multiple input files, then it should also be possible to have different offset or length from those files.

It might be that your current use-case will be happy with using same offset or length for all files in a list, but this can easily be too limiting in future.

"#include <zephyr/linker/sections.h>\n")
endif()

if(${READONLY} STREQUAL yes)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just do:

Suggested change
if(${READONLY} STREQUAL yes)
if(READONLY)

Note: comment on naming is discussed else-where.

# specified files should be started.
# LENGTH - Length, in bytes, of the data subset to be included from
# the specified files.
# READONLY - yes (default) or no.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I find problematic is that the only documentation for generate_binary_embed_inc_file_for_target() and friends is:

# READONLY - yes (default) or no.

This tells me nothing about what READONLY actually means, I need to go look at the script which generates the including code.

This needs to be improved, and in this context I believe using the flag / parameter name CONST will ease the understanding of the flag.

# ALIGN - Alignment for data being embedded in bytes. Default is 4.
# SECTION - Name of the desired linker section. Data from all specified
# binary files will be placed in the given section name.
# COMPRESS - Compress the data with specified compression method: gzip or no (default).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please using none or uncompressed.

set(generated_files ${generated_file})
if(use_incbin)
list(APPEND generated_files ${generated_file}.S)
zephyr_library_sources(${generated_file}.S)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the generated assemble file added to the current Zephyr library, even when dedicated target is specified ?

else()
set(bin_file_size ${LENGTH})
endif()
if(${COMPRESS} STREQUAL no)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if(${COMPRESS} STREQUAL no)
if(COMPRESS)

and then swap the if and else bodies.

# Include the binary data.
if(USE_INCBIN)
if("${SECTION}" STREQUAL "")
if(${READONLY} STREQUAL yes)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if(${READONLY} STREQUAL yes)
if(READONLY)


# Include the binary data.
if(USE_INCBIN)
if("${SECTION}" STREQUAL "")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems that the use of READONLY in generate_binary_embed_inc() not only effects the use of the const qualifier but also the section placement (unless SECTION is also used).

However this is not documented anywhere afaict.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice with tests 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants