Here’s the few basic steps for the build process that I use all together (It’s also in complete form on my github page).
The three most important tool-chain programs are ld
, objdump
, and objcopy
, apart from the assembler (as
) and compiler (gcc
), of course.
Fist, use the ld
program to link all the compiled and assembled objects together, specifying the target’s physical layout in the linker script .lds
file. Using the -Map
option, the generated .map
file contains highly useful information telling exactly where each symbol is placed in memory,
riscv32-unknown-elf-ld start.o foo.o bar.o baz.o ... -T mytarget.lds -o myprog.elf -Map myprog.map
Next, as @disasm points out, you can get the final program listing with all its pneumonics and opcodes, by using the objdump
program,
riscv32-unknown-elf-objdump -D myprog.elf > myprog.lst
Lastly, if desired, use the objcopy
program to translate the .elf to either Intel Hex format, a plain-text ASCII and human-readable representation,
riscv32-unknown-elf-objcopy myprog.elf -O ihex myprog.hex
or a more compact and pure binary format,
riscv32-unknown-elf-objcopy myprog.elf -O binary myprog.bin
This last step is not necessary if your loader/debugger tool accomodates the .elf
file directly. For historical reasons, I’ve always been a fan of Intel Hex.
Both the MAP and the LST files together as an output set have always been highly useful for me.
Note: If you’re using the 64-bit version of the riscv gcc, you can still generate 32-bit binaries – you will need option -b elf32-littleriscv
on the ld
program in the First step above,
riscv64-unknown-elf-ld start.o foo.o bar.o baz.o ... -T mytarget.lds -o myprog.elf -Map myprog.map -b elf32-littleriscv