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 <main>:
0: ff010113 addi sp,sp,-16
4: 00200593 addi a1,zero,2
8: 00100513 addi a0,zero,1
c: 00112623 sw ra,12(sp)
10: 154000ef jal ra,164 <print_hex>
14: 00200593 addi a1,zero,2
18: 00100513 addi a0,zero,1
1c: 148000ef jal ra,164 <print_hex>
20: 00200593 addi a1,zero,2
24: 00058513 addi a0,a1,0
28: 13c000ef jal ra,164 <print_hex>
2c: 00200593 addi a1,zero,2
30: 00300513 addi a0,zero,3
34: 130000ef jal ra,164 <print_hex>
38: 00200593 addi a1,zero,2
3c: 00500513 addi a0,zero,5
40: 124000ef jal ra,164 <print_hex>
44: 00200593 addi a1,zero,2
48: 00800513 addi a0,zero,8
4c: 118000ef jal ra,164 <print_hex>
50: 00200593 addi a1,zero,2
54: 00d00513 addi a0,zero,13
58: 10c000ef jal ra,164 <print_hex>
5c: 00200593 addi a1,zero,2
60: 01500513 addi a0,zero,21
64: 100000ef jal ra,164 <print_hex>
68: 0000006f jal zero,68 <main+0x68>
0000006c <print_chr>:
6c: 800007b7 lui a5,0x80000
70: 00a7a023 sw a0,0(a5) # 80000000 <print_hex+0x7ffffe9c>
74: 00008067 jalr zero,0(ra)
00000078 <print_str>:
78: 80000737 lui a4,0x80000
7c: 00054783 lbu a5,0(a0)
80: 00079463 bne a5,zero,88 <print_str+0x10>
84: 00008067 jalr zero,0(ra)
88: 00150513 addi a0,a0,1
8c: 00f72023 sw a5,0(a4) # 80000000 <print_hex+0x7ffffe9c>
90: fedff06f jal zero,7c <print_str+0x4>
00000094 <_print_dec>:
94: ff010113 addi sp,sp,-16
98: 00812423 sw s0,8(sp)
9c: 00112623 sw ra,12(sp)
a0: 00050413 addi s0,a0,0
a4: 00900793 addi a5,zero,9
a8: 00000513 addi a0,zero,0
ac: 0287ec63 bltu a5,s0,e4 <_print_dec+0x50>
b0: 04a7e063 bltu a5,a0,f0 <_print_dec+0x5c>
b4: 03050513 addi a0,a0,48
b8: 0ff57513 andi a0,a0,255
bc: 800007b7 lui a5,0x80000
c0: 00a7a023 sw a0,0(a5) # 80000000 <print_hex+0x7ffffe9c>
c4: 03040413 addi s0,s0,48
c8: 0ff47413 andi s0,s0,255
cc: 800007b7 lui a5,0x80000
d0: 00c12083 lw ra,12(sp)
d4: 0087a023 sw s0,0(a5) # 80000000 <print_hex+0x7ffffe9c>
d8: 00812403 lw s0,8(sp)
dc: 01010113 addi sp,sp,16
e0: 00008067 jalr zero,0(ra)
e4: 00150513 addi a0,a0,1
e8: ff640413 addi s0,s0,-10
ec: fc1ff06f jal zero,ac <_print_dec+0x18>
f0: fa5ff0ef jal ra,94 <_print_dec>
f4: fd1ff06f jal zero,c4 <_print_dec+0x30>
000000f8 <print_dec>:
f8: ff010113 addi sp,sp,-16
fc: 00812423 sw s0,8(sp)
100: 00112623 sw ra,12(sp)
104: 00050413 addi s0,a0,0
108: 00900793 addi a5,zero,9
10c: 00000513 addi a0,zero,0
110: 0487e063 bltu a5,s0,150 <print_dec+0x58>
114: 04a7e463 bltu a5,a0,15c <print_dec+0x64>
118: 03050513 addi a0,a0,48
11c: 0ff57513 andi a0,a0,255
120: 800007b7 lui a5,0x80000
124: 00a7a023 sw a0,0(a5) # 80000000 <print_hex+0x7ffffe9c>
128: 03040413 addi s0,s0,48
12c: 0ff47413 andi s0,s0,255
130: 800007b7 lui a5,0x80000
134: 0087a023 sw s0,0(a5) # 80000000 <print_hex+0x7ffffe9c>
138: 00c12083 lw ra,12(sp)
13c: 00812403 lw s0,8(sp)
140: 00a00713 addi a4,zero,10
144: 00e7a023 sw a4,0(a5)
148: 01010113 addi sp,sp,16
14c: 00008067 jalr zero,0(ra)
150: 00150513 addi a0,a0,1
154: ff640413 addi s0,s0,-10
158: fb9ff06f jal zero,110 <print_dec+0x18>
15c: f39ff0ef jal ra,94 <_print_dec>
160: fc9ff06f jal zero,128 <print_dec+0x30>
00000164 <print_hex>:
164: 04058263 beq a1,zero,1a8 <print_hex+0x44>
168: 00259593 slli a1,a1,0x2
16c: ffc58593 addi a1,a1,-4
170: 000016b7 lui a3,0x1
174: 80000637 lui a2,0x80000
178: 0005d863 bge a1,zero,188 <print_hex+0x24>
17c: 00001537 lui a0,0x1
180: c0050513 addi a0,a0,-1024 # c00 <print_hex+0xa9c>
184: ef5ff06f jal zero,78 <print_str>
188: 00b55733 srl a4,a0,a1
18c: c0468793 addi a5,a3,-1020 # c04 <print_hex+0xaa0>
190: 00f77713 andi a4,a4,15
194: 00e787b3 add a5,a5,a4
198: 0007c783 lbu a5,0(a5)
19c: ffc58593 addi a1,a1,-4
1a0: 00f62023 sw a5,0(a2) # 80000000 <print_hex+0x7ffffe9c>
1a4: fd5ff06f jal zero,178 <print_hex+0x14>
1a8: 00008067 jalr zero,0(ra)
Disassembly of section .rodata.str1.4:
00000c00 <.rodata.str1.4>:
c00: 000a .insn 2, 0x000a
c02: 0000 .insn 2, 0x
c04: 3130 .insn 2, 0x3130
c06: 3332 .insn 2, 0x3332
c08: 3534 .insn 2, 0x3534
c0a: 3736 .insn 2, 0x3736
c0c: 3938 .insn 2, 0x3938
c0e: 4241 .insn 2, 0x4241
c10: 46454443 .insn 4, 0x46454443
...
Below are the object dumps of start.o, print.o, and firmware.o.
start.o: file format elf32-littleriscv
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 00000090 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
4 .riscv.attributes 0000001a 00000000 00000000 000000c4 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
0000008c l .init 00000000 end
00000000 l d .riscv.attributes 00000000 .riscv.attributes
00000000 g .init 00000000 start
00000000 *UND* 00000000 main
RELOCATION RECORDS FOR [.init]:
OFFSET TYPE VALUE
00000080 R_RISCV_JAL main
00000084 R_RISCV_JAL end
0000008c R_RISCV_JAL end
print.o: file format elf32-littleriscv
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 0000014c 00000000 00000000 00000034 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 00000000 00000000 00000180 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 00000180 2**0
ALLOC
3 .rodata.str1.4 00000015 00000000 00000000 00000180 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .comment 0000001a 00000000 00000000 00000195 2**0
CONTENTS, READONLY
5 .note.GNU-stack 00000000 00000000 00000000 000001af 2**0
CONTENTS, READONLY
6 .riscv.attributes 0000001c 00000000 00000000 000001af 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 00000068 _print_dec
00000090 g F .text 00000070 print_dec
00000100 g F .text 0000004c print_hex
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
00000014 R_RISCV_BRANCH .L4
00000024 R_RISCV_JAL .L3
00000040 R_RISCV_BRANCH .L7
00000044 R_RISCV_BRANCH .L8
00000080 R_RISCV_JAL .L6
00000084 R_RISCV_CALL_PLT _print_dec
00000084 R_RISCV_RELAX *ABS*
0000008c R_RISCV_JAL .L9
000000a8 R_RISCV_BRANCH .L13
000000ac R_RISCV_BRANCH .L14
000000f0 R_RISCV_JAL .L12
000000f4 R_RISCV_CALL_PLT _print_dec
000000f4 R_RISCV_RELAX *ABS*
000000fc R_RISCV_JAL .L15
00000100 R_RISCV_BRANCH .L17
0000010c R_RISCV_HI20 .LC0
0000010c R_RISCV_RELAX *ABS*
00000114 R_RISCV_BRANCH .L20
00000118 R_RISCV_HI20 .LC1
00000118 R_RISCV_RELAX *ABS*
0000011c R_RISCV_LO12_I .LC1
0000011c R_RISCV_RELAX *ABS*
00000120 R_RISCV_CALL_PLT print_str
00000120 R_RISCV_RELAX *ABS*
0000012c R_RISCV_LO12_I .LC0
0000012c R_RISCV_RELAX *ABS*
00000144 R_RISCV_JAL .L19
firmware.o: file format elf32-littleriscv
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 .text.startup 0000008c 00000000 00000000 00000034 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
4 .comment 0000001a 00000000 00000000 000000c0 2**0
CONTENTS, READONLY
5 .note.GNU-stack 00000000 00000000 00000000 000000da 2**0
CONTENTS, READONLY
6 .riscv.attributes 0000001c 00000000 00000000 000000da 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 .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 0000008c main
00000000 *UND* 00000000 print_hex
RELOCATION RECORDS FOR [.text.startup]:
OFFSET TYPE VALUE
00000010 R_RISCV_CALL_PLT print_hex
00000010 R_RISCV_RELAX *ABS*
00000020 R_RISCV_CALL_PLT print_hex
00000020 R_RISCV_RELAX *ABS*
00000030 R_RISCV_CALL_PLT print_hex
00000030 R_RISCV_RELAX *ABS*
00000040 R_RISCV_CALL_PLT print_hex
00000040 R_RISCV_RELAX *ABS*
00000050 R_RISCV_CALL_PLT print_hex
00000050 R_RISCV_RELAX *ABS*
00000060 R_RISCV_CALL_PLT print_hex
00000060 R_RISCV_RELAX *ABS*
00000070 R_RISCV_CALL_PLT print_hex
00000070 R_RISCV_RELAX *ABS*
00000080 R_RISCV_CALL_PLT print_hex
00000080 R_RISCV_RELAX *ABS*
00000088 R_RISCV_JAL .L2