How to add a binary resource? - Page 1
Forum section: electronics / general

author: DELTA67
author_rank: Regular Contributor
timestamp: March 07, 2025, 10:05:42 pm
content: Hi,I'm trying to add a binary resource into the final executable for an ARM CORTEX-M4 (STM32F411).I do this with the following command:Code:arm-none-eabi-objcopy -I binary -O elf32-littlearm -B arm my_res.bin my_res.oThis operation appears to work in that the "my_res.o" file is created.However, when I try to link this object file into the executable, I get:Code:arm-none-eabi-ld: error: my_res.o: Conflicting CPU architectures 13/0Here's the format of main.o:Code:arm-none-eabi-objdump -f main.omain.o: file format elf32-littlearmarchitecture: arm, flags 0x00000011:HAS_RELOC, HAS_SYMSstart address 0x00000000And that of my_res.o:Code:arm-none-eabi-objdump -f my_res.omy_res.o: file format elf32-littlearmarchitecture: arm, flags 0x00000010:HAS_SYMSstart address 0x00000000Help please.----------------------NOTE:Another way to do the same thing is by converting the binary file to a C header and include it in the project.Here's a very good tool to do the job:https://github.com/AntumDeluge/bin2header

author: eutectique
author_rank: Frequent Contributor
timestamp: March 08, 2025, 12:50:55 am
content: Tryldinstead ofobjcopy:Code:arm-none-eabi-ld -r -b binary -o my_res.o my_res.binOr usexxd:Code:xxd -i my_res.bin > my_res.h

author: DELTA67
author_rank: Regular Contributor
timestamp: March 08, 2025, 01:01:03 pm
content: Quote from: eutectique on March 08, 2025, 12:50:55 amThankseutectique, tested, I've got the same error:Code:arm-none-eabi-ld: error: my_res.o: Conflicting CPU architectures 13/0

author: DELTA67
author_rank: Regular Contributor
timestamp: March 08, 2025, 01:31:38 pm
content: Well, even the LD error, the generated ELF in fact contains the binary resource!!Here's the output of "nm":Code:arm-none-eabi-nm test.elf20000400 D _binary_my_res_bin_end00000400 A _binary_my_res_bin_size20000000 D _binary_my_res_bin_start08000354 T uart_init20000404 B BSS_END20000400 D BSS_START080001b0 T send_char080001a0 T delay_ms...............................................................I use the resource like this:Code:extern uint8_t _binary_my_res_bin_start;uint8_t *my_res = &_binary_my_res_bin_start;for(uint16_t j=0; j<_binary_my_res_bin_size; j++) printf("%X ", my_res);

author: tellurium
author_rank: Frequent Contributor
timestamp: March 08, 2025, 01:45:14 pm
content: There is an alternative way of embedding anything into an executable.Say, if you have a binary file, then simply hexdump it and turn into a C array. Then simply add that C array file to your build.There is an "xxd" utility with handy "-i" option that does that for you:xxd -i a.bin a.cNow a.c contains "a.bin" file as a C array:Code:unsigned char _tmp_a_bin[] = {0x61, 0x62, 0x63, 0x0a};unsigned int _tmp_a_bin_len = 4;Add a.c to your build, and your C code can access "_tmp_a_bin" data .Just noticed eutectique already suggested that.

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 08, 2025, 01:58:21 pm
content: Yes, there are a few ways of doing that. I remember a thread dealing with all this, but I can't find it.Other than the methods shown in this thread, FYI, C23 introduces a standard way of doing exactly that.That should be available in GCC 15.As for me, I used to use the method with 'ld', but then I designed my own "resource embedding" tool (in Lua), which is very flexible. It generates assembly files to embed binary data (gas syntax, which supports a '.incbin' directive), and a C header file with functions allowing accessing "resources" from their name. Pretty handy.This will again become more or less moot with C23, which allows this directly.See:https://en.cppreference.com/w/c/preprocessor/embed

author: DELTA67
author_rank: Regular Contributor
timestamp: March 08, 2025, 02:43:02 pm
content: Quote from: tellurium on March 08, 2025, 01:45:14 pmThanks, I've already said that since the first post.The probleme is why LD complains when using objdump?

author: Nominal Animal
author_rank: Super Contributor
timestamp: March 08, 2025, 02:57:01 pm
content: Quote from: DELTA67 on March 07, 2025, 10:05:42 pmYup: yourmy_res.oisn't relocatable, but your main object file is. (So, it's aflagsissue, not CPU architecture per se.) Usearm-none-eabi-ld -r -b binary -o my_res.o my_res.binto generate a relocatablemy_res.o, with symbols_binary_my_res_bin_startand_binary_my_res_bin_endin the.datasection, and an absolute symbol_binary_my_res_bin_size.If you need to rename the symbols or change the section name, usetemp.othere, andarm-none-eabi-objcopy(without-b, letting it autodetect the architecture and flags from the ELF input file) fromtemp.otomy_res.owith the appropriate--rename-section.data=.rodata,alloc,load,readonly,data,contentsand/or--redefine-symoldsym=newsymoptions.

author: DELTA67
author_rank: Regular Contributor
timestamp: March 08, 2025, 05:38:33 pm
content: Quote from: Nominal Animal on March 08, 2025, 02:57:01 pmThanks, already tested and gives the same issue.

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 08, 2025, 06:21:59 pm
content: We may need to know which version of binutils/gcc you are using.

author: westfw
author_rank: Super Contributor
timestamp: March 08, 2025, 07:37:55 pm
content: QuoteI about had a fit when I discovered that someone had done that with the .iso file of a Linux distribution…. Get the objcopy technique working. It’s the correct solution.

author: DELTA67
author_rank: Regular Contributor
timestamp: March 08, 2025, 09:24:47 pm
content: Quote from: SiliconWizard on March 08, 2025, 06:21:59 pmI'm uing an old yagarto build from 2012!!gcc V4.7.2binutils V2.23.1

author: Nominal Animal
author_rank: Super Contributor
timestamp: March 08, 2025, 10:05:39 pm
content: Quote from: DELTA67 on March 08, 2025, 05:38:33 pmThen you use a very different linker command or script to link the main binary. Can you show the exact commands (including non-section, non-program header parts of your linker script) you use to build/link your main binary?

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 08, 2025, 10:36:18 pm
content: Quote from: DELTA67 on March 08, 2025, 09:24:47 pmPrecisely, you can find quite a few threads online about this particular error. Seems that they affected gcc older than 4.9 with the binutils that was current at the time (don't know which exactly). Pretty sure you're gonna suffer some more with this. Update your toolchain and report back.

author: nctnico
author_rank: Super Contributor
timestamp: March 08, 2025, 10:49:30 pm
content: I solve this by using a bit of assembly. GNU assembler allows to absorb a binary blob into the output file. That way you can also place it into a defined memory section and thus at a defined address (if needed) and with symbols pointing to the start & end of the binary image. The advantage is that including the binary file is part of the source and not some extra step that may get lost when transferring the project from one IDE to the other. Bonus points for having a file dependency on the binary so the assembler file (only) gets rebuild when the binary changes.

author: DELTA67
author_rank: Regular Contributor
timestamp: March 09, 2025, 09:50:39 am
content: Quote from: Nominal Animal on March 08, 2025, 10:05:39 pmSure. I'm using a minimal linker script:Code:MEMORY{flash(RX) : ORIGIN = 0x08000000, LENGTH = 512Ksram(WAIL) : ORIGIN = 0x20000000, LENGTH = 128K}SECTIONS{.text : {*(.vectors*)*(.text*)} > flash.bss : { *(.bss*) } > sram}

author: Nominal Animal
author_rank: Super Contributor
timestamp: March 09, 2025, 05:05:24 pm
content: And the compiler/linker commands you use to build the main object file?

author: Nominal Animal
author_rank: Super Contributor
timestamp: March 09, 2025, 05:58:48 pm
content: Quote from: SiliconWizard on March 08, 2025, 10:36:18 pmBloody hell, you're absolutely right. Specifically, all binutils versions before 2.25 fail here regardless of the compiler version, because when one object file lacks the.ARM.attributesection created by the C compier, binutils cannot combine the two object files, yielding OP's error.It might be possible to useCode:mv -f main.o old.main.oarm-none-eabi-objcopy \--add-section .rodata.my_res,file=my_res.bin \--set-section-flags .rodata.my_res,alloc,load,readonly,data,contents \--add-symbol _my_res_start=.rodata.my_res:0,global,object \--add-symbol _my_res_stop=.rodata.my_res:$(stat -c '%s' my_res.bin),global,object \--add-symbol _my_res_size=$(stat -c '%s' my_res.bin) \old.main.o main.oto insertmy_res.binas a new section (read-only above), synthesizing the three related symbols,intothe original main object file instead.The correct solution is to update the toolchain, because binutils-2.25 or later is required for these commands to work on ARM architectures.If the compiler cannot be updated for some silly reason, it suffices to update binutils. If neither can be updated, then OP is hosed: these commands are not supported properly in the versions they are using, and they must instead convert the binary data into a C array definition.

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 09, 2025, 07:25:48 pm
content: And I see little reason not to update. If the OP is on Linux, the arm-none-eabi toolchain should be readily available. If on Windows, they can download it from there:https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloadsThey are the official toolchains from ARM and work absolutely fine on Windows.

author: cv007
author_rank: Frequent Contributor
timestamp: March 09, 2025, 07:48:49 pm
content: Code:MEMORY{flash(RX) : ORIGIN = 0x08000000, LENGTH = 512Ksram(WAIL) : ORIGIN = 0x20000000, LENGTH = 128K}TARGET(binary)INPUT (./my.bin)SECTIONS{.text : {*(.vectors*)*(.text*)KEEP(./my.bin) /* KEEP to just make sure its not discarded if unused section optimization is used */. = ALIGN(4);} > flash.bss : { *(.bss*) } > sram}created a binary file with text-Code:$printf "0123\n\0" > my.bincompiled code-Code://compile disassembled output for binary data08000b14 <__fini_array_end>:8000b14:33323130.word0x333231308000b18:000a.short0x000a//this is for alignment we specified08000b1a <_binary___my_bin_end>:8000b1a:0000.short0x0000SYM32G031K8 Nucleo32 board I had hooked up-Code:...int main(){//linker symbols auto created- _binary___NAME_EXT_start, _binary___NAME_EXT_endextern const char _binary___my_bin_start[]; //char array at linker created symbol addressuart << normal<< "_binary___my_bin_start ["<< Hex0x( 8, reinterpret_cast<u32>(&_binary___my_bin_start) ) //flash address of symbol<< "]: " << _binary___my_bin_start //binary data, which is text data<< endl;//output-//_binary___my_bin_start : 0123while(1){}}

author: Nominal Animal
author_rank: Super Contributor
timestamp: March 09, 2025, 08:12:00 pm
content: Quote from: SiliconWizard on March 09, 2025, 07:25:48 pmExactly.I can imagine there might be some internal rules in an organization that would stop one from updating the compiler version –– for any hobbyist or open source project, there absolutely should not be any reason not to update thearm-none-eabi-tools toat leastgcc-9.2.1 + binutils-2.34 ––, but even then updating binutils to 2.25 would be safe (as in, not change the resulting binaries, just fix this problem).Considering the number of issues and enhancements, I'd definitely update binutils to the latest easily available version (whatever is provided by the distribution if using Linux, latest binaries from the link SiliconWizard provided if using Windows). If OP is not constrained by organizational policy, then GCC also.(There are some code generation and code optimization differences in various GCC versions for 32-bit ARM architectures, but for learners, it is definitely best to use an up to date toolchain if at all possible. I for one am most interested in the many very useful features in ISO C23 –– as documented inN3220–– landing in GCC and Clang. Not justembedthat SiliconWizard described, but others likeconstexprtoo. I still sometimes write code for 8-bit AVRs with Harvard architecture where GCC's C implementation is more useful than its C++ implementation, and I don't trust Clang's support for that old architecture enough.)For what it is worth, I declare these external objects (sections or binary data symbols) usingextern constTYPE__section_start[];extern constTYPE__section_stop[];#defineARRAYNAME__section_start#defineARRAYNAME_SIZE (__section_stop - __section_start)so that I can do stuff likefor (size_t i = 0; i <ARRAYNAME_SIZE; i++)// do something withARRAYNAME]i]whereTYPEis the data type for the array elements, and has to have size a multiple of its alignment ((alignof (TYPE) % sizeof (TYPE)) == 0). Then,ARRAYNAME_SIZEis the number of elements in that array.To define such an element anywhere, I dostatic constTYPEvarname__attribute__((used, section, ("section"))) =definition;wherevarnamedoesn't really matter, as long as it is unique name within the file (I often use preprocessor and construct the name using some prefix and the line number,__LINE__), but note that C and C++ initializer rules differ a bit. While C allows anything computable at compile time, C++ may compute it at a run time using a constructor function, and that can be an issue depending on the exact linker script and runtime used. Remember, the C/C++ used on microcontrollers is not the full version that guides and tutorials use: we use thefreestandingversion on microcontrollers, whereas that full version is thehostedversion, using the actual standard terminology. The two differ in what is available.

author: nctnico
author_rank: Super Contributor
timestamp: March 09, 2025, 09:49:00 pm
content: There is an interesting web page on this subject:http://elm-chan.org/junk/32bit/binclude.htmlThe easiest form seems to be using inline assembly in C.

author: cv007
author_rank: Frequent Contributor
timestamp: March 10, 2025, 03:01:20 pm
content: QuoteEasier than 3 lines in a linker script?

author: westfw
author_rank: Super Contributor
timestamp: March 10, 2025, 07:00:45 pm
content: QuoteSure. Changing source code is relatively easy and transparent, compared to causing the build procedure to deviate from expected norms. (how many times have we seen someone trying to chase "a bug in my code" when it turned out that they had something funny going on, buried in the makefile or cmake params or IDE build procedure?)

author: cv007
author_rank: Frequent Contributor
timestamp: March 10, 2025, 07:22:35 pm
content: QuoteYou are already deviating quite a bit with other solutions- a bunch of objcopy post-build options to deal with and/or the need to specify a section for this binary to park itself in, and more source files to deal with. In the end you may still end up in the linker file adding sections to accommodate your binary include system.With the 3 linker script lines your build system is unchanged and no additional source files or conversions of binary files to C. You simply have a binary file you already wanted included, and you simply specify its file name and where you want it. I'm not sure how it gets any simpler.TARGET(binary) /* INPUT file will be binary */INPUT(./myfile.bin) /* *//* we place the binary data where we want */....text {..../myfile.bin /* or KEEP(./myfile.bin) */...

author: DELTA67
author_rank: Regular Contributor
timestamp: March 10, 2025, 09:04:10 pm
content: Thanks for you all for your contributions.I'll update my toolchain, I am among the people who do not like change (large inertia)!!!Do you know a leightweight one for WINDOWS, please? I'm keeping yagarto for this reason + the yagarto tools.

author: mwb1100
author_rank: Frequent Contributor
timestamp: March 10, 2025, 09:13:22 pm
content: You have tested that updating the tools no longer spits out the error? What tool versions are working?

author: nctnico
author_rank: Super Contributor
timestamp: March 10, 2025, 09:32:40 pm
content: Quote from: cv007 on March 10, 2025, 07:22:35 pmThe problem is that linker scripts are often in obfustigated places. Try to figure out where the linker script is located for building software on a PC. And then try to include / maintain different linker descriptor files for various targets in a project. The same for the objcopy method where the objcopy needs to be able to deal with the specific target architecture. The (inline) assembler route is way more portable and easier to understand for people who are used to dealing only with source code and have little understanding of the intricacies of the build process and associated tools.

author: mwb1100
author_rank: Frequent Contributor
timestamp: March 10, 2025, 10:47:50 pm
content: FWIW I like the bin2hex approach:- add a couple lines to the makefile to create a C source/header file from binary- a dependency in the makefile to that header or C sourceDone.Nothing complex about that as far as I can see. Might not be the best approach for really large binaries, but it would probably still work.

author: cv007
author_rank: Frequent Contributor
timestamp: March 11, 2025, 05:55:24 am
content: QuoteBut in this case we were given the user created linker script. In any case various options are available.Quotehttps://developer.arm.com/downloads/-/arm-gnu-toolchain-downloadsPick the Windows hosted cross toolchain, arm-none-eabi version. I'm not sure it makes much difference for an mcu target whether the x86_64 or i686 version is chosen (except maybe if your Windows OS is old and still 32bit). I have the i686 version on my Windows pc .I also use a make.exe compiled from a TCC download (busybox for win32 zip file)-https://download.savannah.gnu.org/releases/tinycc/Compiler + make.exe + makefile is a good combination for Windows, and is easy to move around to different pc's or can even move to something like a Linux pc with only the addition of a Linux hosted cross toolchain (from the same download location linked to above).

author: DELTA67
author_rank: Regular Contributor
timestamp: March 12, 2025, 07:29:57 am
content: Quote from: mwb1100 on March 10, 2025, 09:13:22 pmNo, not yet.