Embedded use cases for Cargo build hooks
From simplest to craziest

Displaying binary size

Phase: after linking

What: Run size on the output binary

Motivation: Binary size is an important metric; most (embedded) IDEs show this information after you hit the build button

Add checksum to binary

Phase: after linking

What: Modify the output binary (ELF) in place, but this shouldn’t require an external tool; it should be possible to write the thing that modifies the ELF in Rust (build script like)

Motivation: Some microcontrollers won’t boot a binary if it doesn’t have a checksum in a certain place in memory. This checksum can only be computed after linking because some addresses (inputs) are not known until then

Prepare application as bootloader payload

Phase: after linking

What: Modify the output binary (ELF) in place

Motivation: For IoT use cases a device should be upgradeable on the fly. This requires an intermediate control instance which is capable of booting into different application versions depending on a number of factors. To allow applications to be used with a bootloader they need to conform to certain conventions and be relocatable which is usually achieved by a postprocessing step

Zero cost stack overflow protection

Phase: Linking

What: Run linking twice to change the memory layout of a program. The second time with extra linker arguments derived from the output binary from the first linking process

Motivation: The standard memory layout (that you can get using linker scripts) places the stack and static variables in such a way that the stack can grow and overwrite the static variables. Reversing the location of the stack and static variables avoid this problem (see figure in https://blog.japaric.io/stack-overflow-protection/#fixing-it) but changing the layout requires knowing the total size of static variables, which is only known after linking.