With the three object files generated, it is time to stich everything togehter. These object files are compiled, so they are converted to machine code. They are organised in sections and they all start at address 0x0. (At the bottom of this page, three object dumps are found so you can verify for yourself.) The image below shows which object file contains which sections. As mentioned before, it is the linker’s job to do the stiching. A recipe for this can be provided in the form of a linker script.
The linker script shown here, first states that the entry point is the label start.
Then the available memories are defined. In the case of a RISC processor, the architecture is a Harvard architecture (in contrast to the von Neumann architecture that is typically found in CISC processors). This implies that the memories for data and instructions are split. In the definition, two memories are made to facilitate this:
ENTRY(start)
MEMORY
{
ROM (rx) : ORIGIN = 0x00000000, LENGTH = 2k
RAM (rwx) : ORIGIN = 0x00000800, LENGTH = 2k
}
SECTIONS {
/* this part has to go into the IMEM */
.text : {
. = 0x000000;
*(.init);
*(.text*);
} > ROM
/* this part has to go into the DMEM */
.data : {
. = 0x000;
*(.data);
} > RAM
.rodata : {
. = 0x400;
*(.rodata);
. = ALIGN(4);
FILL(0xFF)
} > RAM
/DISCARD/ :
{
*(.note*);
*(.iplt*);
*(.igot*);
*(.rel*);
*(.comment);
*(.riscv.attributes);
}
}
It’s worth pointing out the difference between kB and KiB: the base for kB is 10, and the base for KiB is 2.
Back-in-the-day it was custom to state that there are 1024 bytes in a kilobyte. The reason for using 1024 is that this is the power of 2 that is closest to 1000.
This, however, lead to confusion. After all, there are only 1000 meters in a kilometer. To avoid this ambiguity, in 1998 an new set of binary prefixes was introduced. Everything that uses a base 2 got a new prefix.
The current defintions hence are: 1000 bytes in a kilobyte and 1024 bytes in a kibibyte.
Similar definitions hold for: mebibyte (MiB), gibibyte (GiB), tebibyte (TiB), … e.g. 1 TB = 1'000'000'000 bytes and 1 TiB = 1'099'511'627'776 bytes
After the memories are defined, the script dictates which sections have to put where.
The text section
The data section
The rodata section
dicards other sections
The image below shows the final picture, for this exercise. With these 5 files (1 header file, 3 C files and a linker script) the final binary file can be generated. This binary is in the Executable and Linkable Format (.elf)
After running the tool chain, an .elf file is generated. It might become useful to understand what is going on in this file (as humans). Unfortunately, chances are that a hardware designer needs this to figure out what the implemented processor is doing.
The generated binary file can be disassembled again. Doing this allows to read what was eventually generated by the compiler. Againthe the riscv32-unknown-elf-objdump program can come to our rescue. The object dumper can be used to disassemble the .elf by using the -D option.
The disassembly of the .elf file shows all the instructions with the address, the machine code of the instruction and the human readable mnemonic.
firmware.elf: file format elf32-littleriscv
Disassembly of section .text:
00000000 <start>:
0: 00000093 addi ra,zero,0
4: 00000113 addi sp,zero,0
8: 00000193 addi gp,zero,0
c: 00000213 addi tp,zero,0
10: 00000293 addi t0,zero,0
14: 00000313 addi t1,zero,0
18: 00000393 addi t2,zero,0
1c: 00000413 addi s0,zero,0
20: 00000493 addi s1,zero,0
24: 00000513 addi a0,zero,0
28: 00000593 addi a1,zero,0
2c: 00000613 addi a2,zero,0
30: 00000693 addi a3,zero,0
34: 00000713 addi a4,zero,0
38: 00000793 addi a5,zero,0
3c: 00000813 addi a6,zero,0
40: 00000893 addi a7,zero,0
44: 00000913 addi s2,zero,0
48: 00000993 addi s3,zero,0
4c: 00000a13 addi s4,zero,0
50: 00000a93 addi s5,zero,0
54: 00000b13 addi s6,zero,0
58: 00000b93 addi s7,zero,0
5c: 00000c13 addi s8,zero,0
60: 00000c93 addi s9,zero,0
64: 00000d13 addi s10,zero,0
68: 00000d93 addi s11,zero,0
6c: 00000e13 addi t3,zero,0
70: 00000e93 addi t4,zero,0
74: 00000f13 addi t5,zero,0
78: 00000f93 addi t6,zero,0
7c: 00001137 lui sp,0x1
80: 008000ef jal ra,88 <main>
00000084 <done>:
84: 0000006f jal zero,84 <done>
00000088 <main>:
88: 00001537 lui a0,0x1
8c: ff010113 addi sp,sp,-16 # ff0 <print_hex+0xf1c>
90: c0050513 addi a0,a0,-1024 # c00 <print_hex+0xb2c>
94: 00112623 sw ra,12(sp)
98: 020000ef jal ra,b8 <print_str>
9c: 00c12083 lw ra,12(sp)
a0: 00000513 addi a0,zero,0
a4: 01010113 addi sp,sp,16
a8: 00008067 jalr zero,0(ra)
000000ac <print_chr>:
ac: 800007b7 lui a5,0x80000
b0: 00a7a023 sw a0,0(a5) # 80000000 <print_hex+0x7fffff2c>
b4: 00008067 jalr zero,0(ra)
000000b8 <print_str>:
b8: 80000737 lui a4,0x80000
bc: 00054783 lbu a5,0(a0)
c0: 00079463 bne a5,zero,c8 <print_str+0x10>
c4: 00008067 jalr zero,0(ra)
c8: 00150513 addi a0,a0,1
cc: 00f72023 sw a5,0(a4) # 80000000 <print_hex+0x7fffff2c>
d0: fedff06f jal zero,bc <print_str+0x4>
000000d4 <print_hex>:
d4: fff58593 addi a1,a1,-1
d8: 00259593 slli a1,a1,0x2
dc: 000016b7 lui a3,0x1
e0: 80000637 lui a2,0x80000
e4: 0005da63 bge a1,zero,f8 <print_hex+0x24>
e8: 800007b7 lui a5,0x80000
ec: 00a00713 addi a4,zero,10
f0: 00e7a023 sw a4,0(a5) # 80000000 <print_hex+0x7fffff2c>
f4: 00008067 jalr zero,0(ra)
f8: 00b55733 srl a4,a0,a1
fc: c1068793 addi a5,a3,-1008 # c10 <print_hex+0xb3c>
100: 0ff77713 andi a4,a4,255
104: 00e787b3 add a5,a5,a4
108: 0007c783 lbu a5,0(a5)
10c: ffc58593 addi a1,a1,-4
110: 00f62023 sw a5,0(a2) # 80000000 <print_hex+0x7fffff2c>
114: fd1ff06f jal zero,e4 <print_hex+0x10>
Disassembly of section .rodata.str1.4:
00000c00 <.rodata.str1.4>:
c00: 6548 .insn 2, 0x6548
c02: 6c6c .insn 2, 0x6c6c
c04: 6f77206f jal zero,73afa <print_hex+0x73a26>
c08: 6c72 .insn 2, 0x6c72
c0a: 2164 .insn 2, 0x2164
c0c: 2121 .insn 2, 0x2121
c0e: 000a .insn 2, 0x000a
c10: 3130 .insn 2, 0x3130
c12: 3332 .insn 2, 0x3332
c14: 3534 .insn 2, 0x3534
c16: 3736 .insn 2, 0x3736
c18: 3938 .insn 2, 0x3938
c1a: 4241 .insn 2, 0x4241
c1c: 46454443 .insn 4, 0x46454443
...
Below are the object dumps of start.o, print.o, and firmware.o.
build/start.o: file format elf32-littleriscv
build/start.o
architecture: riscv:rv32, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000000 00000000 00000000 00000034 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000000 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 00000034 2**0
ALLOC
3 .init 00000088 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
4 .riscv.attributes 0000001a 00000000 00000000 000000bc 2**0
CONTENTS, READONLY
SYMBOL TABLE:
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .init 00000000 .init
00000084 l .init 00000000 done
00000000 l d .riscv.attributes 00000000 .riscv.attributes
00000000 g .init 00000000 start
00000000 *UND* 00000000 main
Disassembly of section .init:
00000000 <start>:
0: 00000093 li ra,0
4: 00000113 li sp,0
8: 00000193 li gp,0
c: 00000213 li tp,0
10: 00000293 li t0,0
14: 00000313 li t1,0
18: 00000393 li t2,0
1c: 00000413 li s0,0
20: 00000493 li s1,0
24: 00000513 li a0,0
28: 00000593 li a1,0
2c: 00000613 li a2,0
30: 00000693 li a3,0
34: 00000713 li a4,0
38: 00000793 li a5,0
3c: 00000813 li a6,0
40: 00000893 li a7,0
44: 00000913 li s2,0
48: 00000993 li s3,0
4c: 00000a13 li s4,0
50: 00000a93 li s5,0
54: 00000b13 li s6,0
58: 00000b93 li s7,0
5c: 00000c13 li s8,0
60: 00000c93 li s9,0
64: 00000d13 li s10,0
68: 00000d93 li s11,0
6c: 00000e13 li t3,0
70: 00000e93 li t4,0
74: 00000f13 li t5,0
78: 00000f93 li t6,0
7c: 00001137 lui sp,0x1
80: f81ff0ef jal 0 <start>
80: R_RISCV_JAL main
00000084 <done>:
84: 0000006f j 84 <done>
84: R_RISCV_JAL done
build/print.o: file format elf32-littleriscv
build/print.o
architecture: riscv:rv32, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000006c 00000000 00000000 00000034 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 00000000 00000000 000000a0 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 000000a0 2**0
ALLOC
3 .rodata.str1.4 00000011 00000000 00000000 000000a0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .comment 0000001a 00000000 00000000 000000b1 2**0
CONTENTS, READONLY
5 .note.GNU-stack 00000000 00000000 00000000 000000cb 2**0
CONTENTS, READONLY
6 .riscv.attributes 0000001c 00000000 00000000 000000cb 2**0
CONTENTS, READONLY
SYMBOL TABLE:
00000000 l df *ABS* 00000000 print.c
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .rodata.str1.4 00000000 .rodata.str1.4
00000000 l d .note.GNU-stack 00000000 .note.GNU-stack
00000000 l d .comment 00000000 .comment
00000000 l d .riscv.attributes 00000000 .riscv.attributes
00000000 g F .text 0000000c print_chr
0000000c g F .text 0000001c print_str
00000028 g F .text 00000044 print_hex
Disassembly of section .text:
00000000 <print_chr>:
0: 800007b7 lui a5,0x80000
4: 00a7a023 sw a0,0(a5) # 80000000 <.L7+0x7fffffb4>
8: 00008067 ret
0000000c <print_str>:
c: 80000737 lui a4,0x80000
00000010 <.L3>:
10: 00054783 lbu a5,0(a0)
14: 00079463 bnez a5,1c <.L4>
14: R_RISCV_BRANCH .L4
18: 00008067 ret
0000001c <.L4>:
1c: 00150513 addi a0,a0,1
20: 00f72023 sw a5,0(a4) # 80000000 <.L7+0x7fffffb4>
24: fedff06f j 10 <.L3>
24: R_RISCV_JAL .L3
00000028 <print_hex>:
28: fff58593 addi a1,a1,-1
2c: 00259593 slli a1,a1,0x2
30: 000006b7 lui a3,0x0
30: R_RISCV_HI20 .LC0
30: R_RISCV_RELAX *ABS*
34: 80000637 lui a2,0x80000
00000038 <.L6>:
38: 0005da63 bgez a1,4c <.L7>
38: R_RISCV_BRANCH .L7
3c: 800007b7 lui a5,0x80000
40: 00a00713 li a4,10
44: 00e7a023 sw a4,0(a5) # 80000000 <.L7+0x7fffffb4>
48: 00008067 ret
0000004c <.L7>:
4c: 00b55733 srl a4,a0,a1
50: 00068793 mv a5,a3
50: R_RISCV_LO12_I .LC0
50: R_RISCV_RELAX *ABS*
54: 0ff77713 zext.b a4,a4
58: 00e787b3 add a5,a5,a4
5c: 0007c783 lbu a5,0(a5)
60: ffc58593 addi a1,a1,-4
64: 00f62023 sw a5,0(a2) # 80000000 <.L7+0x7fffffb4>
68: fd1ff06f j 38 <.L6>
68: R_RISCV_JAL .L6
build/firmware.o: file format elf32-littleriscv
build/firmware.o
architecture: riscv:rv32, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000000 00000000 00000000 00000034 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000000 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 00000034 2**0
ALLOC
3 .rodata.str1.4 00000010 00000000 00000000 00000034 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .text.startup 00000028 00000000 00000000 00000044 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
5 .comment 0000001a 00000000 00000000 0000006c 2**0
CONTENTS, READONLY
6 .note.GNU-stack 00000000 00000000 00000000 00000086 2**0
CONTENTS, READONLY
7 .riscv.attributes 0000001c 00000000 00000000 00000086 2**0
CONTENTS, READONLY
SYMBOL TABLE:
00000000 l df *ABS* 00000000 firmware.c
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .rodata.str1.4 00000000 .rodata.str1.4
00000000 l d .text.startup 00000000 .text.startup
00000000 l d .note.GNU-stack 00000000 .note.GNU-stack
00000000 l d .comment 00000000 .comment
00000000 l d .riscv.attributes 00000000 .riscv.attributes
00000000 g F .text.startup 00000028 main
00000000 *UND* 00000000 print_str
Disassembly of section .text.startup:
00000000 <main>:
0: 00000537 lui a0,0x0
0: R_RISCV_HI20 .LC0
0: R_RISCV_RELAX *ABS*
4: ff010113 addi sp,sp,-16
8: 00050513 mv a0,a0
8: R_RISCV_LO12_I .LC0
8: R_RISCV_RELAX *ABS*
c: 00112623 sw ra,12(sp)
10: 00000097 auipc ra,0x0
10: R_RISCV_CALL_PLT print_str
10: R_RISCV_RELAX *ABS*
14: 000080e7 jalr ra # 10 <main+0x10>
18: 00c12083 lw ra,12(sp)
1c: 00000513 li a0,0
20: 01010113 addi sp,sp,16
24: 00008067 ret