Embedding Binary Objects in C
flak.tedunangst.com
flak.tedunangst.com
Compilers will concatenate sections of the same name. You can use this trick to produce a concatenation of arrays across several files:
$ cat t1.c
__attribute__((section("some_array"))) int a[] = {1, 2, 3};
$ cat t2.c
__attribute__((section("some_array"))) int b[] = {4, 5, 6};
$ cat t.c
#include <stdio.h>
extern const int __start_some_array;
extern const int __stop_some_array;
int main() {
const int* ptr = &__start_some_array;
const int n = &__stop_some_array - ptr;
for (int i = 0; i < n; i++) {
printf("some_array[%d] = %d\n", i, ptr[i]);
}
return 0;
}
$ gcc -std=c99 -o t t.c t1.c t2.c && ./t
some_array[0] = 1
some_array[1] = 2
some_array[2] = 3
some_array[3] = 4
some_array[4] = 5
some_array[5] = 6
This is the mechanism that linkers use "under the hood" to get a list of C++ object initializers that need to run pre-main().It's unfortunate that there is no standard way of getting at this functionality in portable C, or to get it in C++ without actually running code pre-main(). Sometimes you really want a linker-initialized list things (of some sort) that have been linked in, without actually running code pre-main() (which has all kind of issues).
I would love to see a thing like this standardized in both C and C++. C++ compilers need it under the hood anyway.
I was referring more to the way shown above, where I've seen a linker change the list of object files from a sorted list of filenames, to a hash table of filenames, which obviously changed the ordering with which it iterated across all object files.
For example, the FreeBSD kernel's use of this construct embeds a well-defined priority[1], and the array is sorted by this priority value at boot time.[2]
[1]: https://github.com/freebsd/freebsd/blob/master/sys/sys/kerne...
[2]: https://github.com/freebsd/freebsd/blob/master/sys/kern/init...
int my_command(..) { ... }
COMMAND(my_command, "command_name", "example command")
HELP("use --foo argument for this...")
HELP("use --bar argument for that...")
Also used this all the time back when I programmed assembly language.It's definitely a problem that this is not standardized. For example, TI has their own C compiler for their ARM CPUs, not everybody uses gcc..
A patch I helped review that's related to this in LLVM just landed today: https://reviews.llvm.org/D72194. (LLVM previously had support, but would perform bad merges in specific and rare edge cases; that patch fixes those cases).
Also, note that you need a custom linker script to define those symbols (__start_some_array, __stop_some_array). The Linux kernel does this, as noted in: https://nickdesaulniers.github.io/blog/2020/04/06/off-by-two...
Cool. :)
> Also, note that you need a custom linker script to define those symbols
The code I posted above compiles and runs without any custom linker script.
What?! Ok, I've seen the Linux kernel's custom linker script define symbols for custom ELF section boundaries, but TIL that you can get these automagically. I'm curious to see if LLD implements this logic, too.
This post covers a lot of different approaches, and has lots of tips: https://www.devever.net/~hl/incbin
One of us from Memfault also wrote a post about tips with Binutils, and this approach is covered in it.
Every now and again I ponder having another go with mruby...
Currently they append the database to the end of the executable, which has some problems and I'm working to make including it in the image more standardized as part of XVFS [0].
The original developer wrote a tool to expand the PNGs to BMPs (arrays of 32-bit pixel values) and generate a C array definition as a text file. Which is lots bigger than the original PNG (13K => 100K sort of thing). Then included that C source in the build. Used up 700K of my firmware image space which was only 1MB to begin with.
So I wrote a tool to represent the raw PNG file as a C declaration, then added parts of libpng to the boot code to decompress the images during initialization. Even with libpng, I saved over 400K. Now the images use RAM instead of ROM, but that's ok I had buttloads of RAM.
Anyway, this is a much slicker way of including binary files in an image. I may go back and change my build.
Funny, they (VisualGDB) even have a tutorial on doing it. But the option 'Embedded Resource' doesn't exist in my (modern) Visual Studio.
Ha! And the feedback I gave VisualGDB about their outdated tutorial, they responded "Just pay HERE and we'll be glad to solve your problem!" They can go ahead and have outdated documentation, screw them.
- hook custom tool calls
- link external objects into the project
Also, hello fellow automotive embedded SW eng! I know your suffering! :))
I suppose it lets me drop support of the script, which is something.
Edit to add: I settled on pngcrush 3 or 4 years ago; if there's something newer and better, I'm interested in hearing about it!
https://git.sr.ht/~sircmpwn/koio
If you just want the contents of the file as a symbol, the approach described in the article is 100% the way to go.
__asm__(“.incbin file”);I think it's a bit better than
ld -b binary ...
cause it doesn't depend on the static linker generating symbols, which feels a bit like a non-standard/non-portable feature. But, who knows, maybe the vast majority of ld variants actually implement it.Others came up with a solution abusing the C preprocessor by defining macros that would make the known structure of the file valid C and therefore they could just #include it. But my solution works with arbitrary files, without knowing the structure beforehand.
As others have pointed out here, you can use inline assembly and the .incbin directive to include a file. But how could that influence whether the compilation succeeds or fails? I figured out how to guess a byte of the file and create metadata sections only accepted by the linker if the guess was correct.
"""
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored \\"-Wtrigraphs\\"")
??= define STRINGIZE(...) ??=__VA_ARGS__
??= define EXPAND_AND_STRINGIZE(...) STRINGIZE(__VA_ARGS__)
??= define hxp EXPAND_AND_STRINGIZE(
??= define BUILD_BUG_ON_ZERO(e) (sizeof(struct <% int:-!!(e); %>))
??= define BUILD_BUG_ON(condition) ((void)BUILD_BUG_ON_ZERO(condition))
const char flag =
??= include "flag"
)[{}];
BUILD_BUG_ON(flag == {});
_Pragma("clang diagnostic pop")
"""
(The {}, of course, being Python format parameters from our script.) static uint8_t mydata[] = {0xDE, 0xAD, 0xBE, 0xEF, ... };
The advantage is that it works anywhere, with any compiler.The disadvantage is that it can increase compile time. I would limit each .c file to 10MB; it seems there's a quadratic increase in build time with file size, at least with gcc.
Also, instead of "0x%02x" I use decimal notation and no spaces in order to decrease the .c file size:
static uint8_t mydata[] = {
42,7,105,0,0,...,
8,0,1,20,...,
...,
}; xxd -i filename.binEither you're expecting the code from that repository to exist somewhere in the compilation-host system already, or you are vendoring the code by copying it into your own repository and taking on any potential maintenance, unportability, any necessary configuration integration to make sure, for instance, that you're compiling for the host rather than the target in a cross-compile setup (did you make sure?)—
Either way, if you use that code as part of your build process, you are taking a build-dep on that code. The fact that its only build-dep is a C compiler may play into making this a sensible choice as compared with something that has even more transitive dependencies, but it does not make it go away.
I submitted a patch
I’ll take the simple, fast, and easy solution, thank you.
Also note that for bytes, hex literals are longer than decimal ones a lot of the time and never shorter due to the "0x" prefix.
Large files will make gcc use more memory, and if you're building on a low-RAW machine, the OS will start to swap. That's probably the effect I saw. Nothing to do with "quadratic" compilation time.
ld -r -b binary quine.c -o myself.o -m elf_amd64It's the other way around. The command produces these symbols basing on the file name:
$ readelf --symbols myself.o
Symbol table '.symtab' contains 5 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 SECTION LOCAL DEFAULT 1
2: 0000000000000000 0 NOTYPE GLOBAL DEFAULT 1 _binary_quine_c_start
3: 0000000000000113 0 NOTYPE GLOBAL DEFAULT ABS _binary_quine_c_size
4: 0000000000000113 0 NOTYPE GLOBAL DEFAULT 1 _binary_quine_c_endGNU objcopy has a way to change the generated names. As far as I can tell, ld does not.
const a = readFile("mydata")
The `const` makes the expression evaluate at compile time, conveniently slurping the file into `a`, where it will be available at run time.That's irrelevant. What matters is whether they point to (or just past the end of) the same object, which they do.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p104...
It follows the same implementation experience guidelines as #include by leaving the search paths implementation defined, with the understand that implementations are not monsters and will generally provide…
Really? We are going to "understand that implementations are not monsters" after what they've done with "undefined"? I think maybe these standards should be written from the perspective that implementations are sociopathic demons summoned against their will.
(But I really want this feature. I regularly use xxd and some Makefile rules to embed assets in my executables. For instance, in a web service I might have all my default configuration and templates in the executable with command line options to send them to standard output and override them from external files. Then on the chance someone needs to make a change they can just make their own file and use it.)
(For the record, I'm aware that there are size limitation when using xxd, but there are also other solutions).
Scanning for the marker can be avoided, if we do the following:
/* inside the program, at file scope */
struct {
char marker[16] = "dW5pcXVlbWFyawo="
uint32 offset;
} binary_stuff;
Then your tiny utility programs opens the executable and looks for the sequence "dW5pcXVlbWFyawo=" inside it. Having found it, it puts the offset of the data into the 32 bit offset field which follows, and writes the data at that offset.When the program runs, if it sees a nonzero value in binary_stuff.offset, it can open its image and directly proceed to that offset to read the stuff or map it: no searching.
For instance, think of signed Linux kernel images on secure embedded systems. The signature includes the embedded initramfs and device tree blob.
That is not an absolute given. If this is some tool that is launched a large number of times from frequently running shell scripts, or from a busy web server's CGI, I'd tend to agree.
The principle you are championing is not widely followed anyway. All sorts of applications that people use on their machines on a daily basis have atrocious startup times. Though "everyone's startup time sucks" is no excuse, it does remove the motivation to worry about single digit millisecond differences.
With my technique we don't actually have to do anything at all until the blob is required. If there are ways of executing the program that don't require the data, that initialization need not even take place. With lazy initialization we can move the cost out of the startup time.
Even if you work at minimizing startup time, there is a lot of activity in a modern application, like attaching shared libraries and opening various files. Contribution from this approach is a like rounding error in the least significant digit of startup time compared to the default stuff that happens before your program even gains control.
It's a lot more sensible to ask the linker to do this as in OP than to hack together something like you've described.
I didn't state it clearly enough, but I didn't say anything about it being mapped. It almost certainly isn't mapped. Loaders do not blindly map the whole thing to memory; then you would end up with debug info unconditionally mapped.
Even if it were mapped, the program wouldn't easily find it with the latter approach I described: the offset given in the structure is measured from the start of the file, not from some address in memory.
> ask the linker
It is not portable either. For instance, it doesn't work with Microsoft's linker which is called link.exe.
I still think I like adding it as a section better, though.
The author puts a lot of work towards making this proposal a reality (as far as I as a casual twitter/slack observer can see) and I'm looking forward to it.
e.g: ld -r -b binary -z noexecstack input.bin -o output.o
Like all the other features C has been missing for the last 30 years.
The C standard has been very cautious about adding features that would make the standard and the compilers more complex since it is the lingua franca of computing.
Nowadays there is really just a handful of archs in use and their compilers are all backends for GCC/LLVM, so they are relaxing a bit the gating of features.
It works pretty well, though I don't believe it works on Windows.
I can access the site through a VPN.
Anyone else facing the same issue? Anyone know what's up with that?
There are also ways to do this on Windows and Mac OS X.
This library provides CMake support to do this: https://github.com/motis-project/res
Many dynamic recompiler/JIT implementations effectively do something like what you describe: they map some executable portion of memory and output native code that is then executed.
Generating assembly files is my favorite way of including outside data into my C programs.