While it is possible to do all this using patching of odd places in your file, using specific locations is often a better choice. Since you haven't already said "Thanks for the advice - I tried it that way, it worked, and I'm happy" in response to any post, I would say that now is the time to take fixed location solutions seriously.
The way I always handle this is to define a struct type of fixed size, containing all the information that might be added post-build. That can be version information, serial numbers, length of the image (very useful if you tag a CRC check on the end of the image), etc., - whatever you want to add. Strings have fixed maximum sizes and space.
Make a dedicated section, and in the source code have a default instance of the type in that section, with default values. (This is especially handy when running from a debugger, as your elf file will not have post-build values.) Empty strings should be all null characters. Remember to declare it "volatile const". Your linker file specifies that this section goes at a specific known fixed address (perhaps just after interrupt vectors, or whatever is appropriate for your microcontroller).
Now your post-build scripts have a simple fixed address to patch the binaries.
Don't bother with hex or srec files. Use binary files - it makes things easier.
There are many suggested solutions and I think all of them can be used with success. Just for sake of curiosity and studying, I'm exploring all of them.
Sincerely I don't *like* solutions where you need to choose a fixed location by yourself. Why you should make a job that can be done by the linker?
How the post-build script should know the exact address of a certain field in the struct?
I know the fixed address of the symbol post_build_data (the only object in my custom section), but now I have to calculate the offset of the field s1 in the struct. This calculations is error prone.
In my opinion, it's much simpler to use a production script that retrieves, without any error or manual calculation, the address of a certain symbol directly from the elf.
I thought you were storing serial numbers? But okay, if you need 10 strings you need 10 strings. The number is just a detail. (But if the number were 200 strings for supporting different languages, you might do things differently.)
Fair enough.
You do so because it makes live much easier. It is the same reason you write your patching script in Python, rather than C.
You figure it out /once/ - using one of many possible methods. Counting with static asserts to check, or looking at the binary after putting canaries in the sample data.
If you think that you might change the struct often, you can use separate variables and put them all in the same section, then look at the map file. In practice you rarely need to do something like that.
Static assertions are your friend here.
I doubt it is simpler. But of course it is possible, and what is simpler for me is not necessarily the same as simpler for you.
You are using a Python script to do the patching. Use pyelftools and do this all in the one Python script. That way, future you who has to maintain this system will not build a time machine to go back and strangle the past you that thought this monster made sense. These kinds of pipes can seem elegant, but they are write-only and a maintainer's nightmare.
It's not you vs. the linker. You co-operate. You need to tell the linker about your chip anyway ("code is from 0x1000 to 0xc000, data is from
0xc000 to 0xd000"). So you can as well tell it "version stamp is from
0xcc00 to 0xd000, data only before 0xcc00".
If you have your identification information in a fixed place, you can, for example, more easily analyze field returns. It's easy for your field service has to change something, and it's easy to do software updates that preserve the identification information. You don't need to figure out which software build is running on the chip and what the address of the structure happens to be in that one.
By defining the struct in a compatible way. For example....
...this is a bad idea, because in most (but probably not all) chips, uint64_t after uint32_t means there's 32 bits of padding, so if you need serial-before-mac, you should at least make the padding explicit. There also might be endian problems.
Using only char/uint8_t fields gives you a very high chance of identical structure layout everywhere (`uint8_t mac_address[8]`).
You can also keep things safe by making sure that you are aligned by "natural" alignment, at least to size 8 bytes (I have never heard of a platform that has more than 8 byte alignment for anything). So two uint32_t's followed by an uint64_t is fine.
Yes, defintely that. Or make the packing explicit. And add compile time checks to verify the offsets of fields withing the structure and fail if they're not what is expected. That has saved my many times.
Of each st4ring that needs this kind of post-build treatment? Oh yes, absolutely. The key insight is that these are not just ordinary strings like any other: they're post-build configuration data.
That discussion is what hides behind the term "known-useful", above. Typically such elements end up near pre-existing memory region boundaries, with some additional space reserved near them for future expansion.
You didn't seriously plan on having the length of this kind of string actually changing willy-nilly, did you? These actually have to be fixed-size arrays, i.e.
ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here.
All logos and trade names are the property of their respective owners.