You literally end up with a table ("section header table") in your executable with sections of type "SHT_PROGBITS" and "SHT_NOBITS". You copy <n> bytes from the offset in the ELF image for the former to the address specified, and zero <n> bytes at the address specified for the latter.
Obviously in a system with virtual memory, you need to account for page table mappings, and for a multiprocessing system you need to setup a process vector with initial instruction pointer, address of the page tables etc. so that you can task switch.
But for the most basic case (ie. a DOS or CP/M workalike) you can pretty much wallop the program into memory and jump or call to the entry point.
Here is a full compiler that emits ELF in about 1600 lines of code (the ELF emitter is about 50 lines in size, most which deals with generating the header):
 So I could run an old MS-DOS executable and redirect its I/O to the Linux host running the program. Dosbox did not support that feature.
That is interesting note about DOS though. CP/M binaries from memory are raw binary images hard coded to be loaded into address 0x100, at least for CPM-80 anyway.