I've done something similar to this to embed a CRC into the flash. Essentially it involves (1) convert the hex to bin, (2) CRC the bin, (3) stick the resulting value in the appropriate spot in the bin, and then (4) convert the bin to hex. Subsequently, the normal flash loader burns the flash and the resulting code can now run a CRC on "itself" and get a proper result. The debug file that matches source code line to flash address is none the wiser, so JTAG debugging can continue unaffected.
With the steps above added to the Makefile, it all just happens automatically with each build.
The details of will vary with the toolchain and target but the general outline should be do-able.
How do you get the .elf in place? If you want to use the same flash for the unique ID as for the program binary, I'd suggest dividing up the flash into partitions. The program can read the partition with the ID, whether that is in a file on a filesystem or just a raw flash block.
I do that sort of thing using the boot loader. What boot loader are you using? Since you mention a .elf, I assume you have a boot loader.
That sounds like a job for a send/expect script interacting with the boot loader.
Do you have an environment variable space in your flash? If not, I highly recommend it. Assuming your board can upgrade itself, then it must already have some routines for writing to the flash. Modify those to create an area that's not blown away between upgrades. Write the serial number into this space. This is common practice, we do it with MAC addresses, product descriptions, serial numbers, configuration strings, etc.
Or eeprom should the CPU give you some of that for free (dunno, I use AVRs and they do have)
A good trick if you have a lump of flash or e2 is to create a variable storage struct with a big pad of reserved space at the end. If a software upgrade needs new persistent variables, add these to the end making the reserved space smaller. Never delete or modify the meaning of existing variables. That means you can upgrade *and* downgrade the program without bad things happening.
 Other than by no longer referencing it.
Managers, politicians and environmentalists: Nature's carbon buffer.
We created a tool that modifies the programming binary by looking for a pattern and modifying it. The reason we are looking for a pattern is to allow code motion over various releases. Alternatively we have set aside specific ROM or EEPROM for system information typically checked with a polynomial type checksum.
When a colleague needed something like that for an AVR application, I wrote an m4 macro that added a serial number to the assembler file. A DOS batch file ran the macro that incremented the serial number, assembled the program, and programmed the chip.