-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathboard.ld
More file actions
145 lines (136 loc) · 6.83 KB
/
Copy pathboard.ld
File metadata and controls
145 lines (136 loc) · 6.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/* The memory map of a program this implementation starts, and the symbols its
* startup object reads.
*
* ⭐⭐ THIS FILE IS A BOARD FACT AND THEREFORE BELONGS TO THIS PACKAGE.
*
* Until now every program that wanted to run over OpenSBI carried its own copy:
* `examples/hello` had one, `openkal-llvm-runtime/examples/same-source` had a
* second, and the specification's conformance suite would have needed a third.
* The load address is not a property of any of them — it is where this firmware
* hands control over, which is precisely what this package exists to know.
*
* ⚠️ Measured 2026-08-23: the conformance suite BUILDS for `riscv64-none-elf`
* against this implementation and then produces an image whose entry point is
* 0x0, because nothing placed it. The suite is written to run in a program that
* carries no other runtime, and asking it to carry a board's memory map would
* contradict that — clause 9 makes behavioural conformance a property of every
* implementation, including one of a machine with no operating system.
*
* ⇒ Supplied through `build.mcpp`, which reaches the CONSUMER's link line. A
* program adds nothing and gets a layout that works.
*
* ⚠️ THE SIZES ARE GENEROUS RATHER THAN MINIMAL, and that is the one judgement
* in this file. A C program printing a string needs neither 256 KiB of stack
* nor 16 MiB of heap; a program carrying a C library and a C++ standard library
* does, and it exhausts the smaller figures during its own initialisation —
* before `main`, so what it looks like is a program that starts and prints
* nothing. Both are past the end of the image and cost nothing in it, and
* QEMU's `virt` has 128 MiB. A board with far less can state its own.
*
* What follows is what a C++ program needs the layout to contain.
*
* OpenSBI occupies 0x80000000 upward and hands control to the next stage at
* 0x80200000, which is why this address and not the start of RAM.
*
* ⚠️ FIRMWARE JUMPS TO THE LOWEST LOADED ADDRESS AND NOT TO THE ENTRY THE IMAGE
* RECORDS. Measured 2026-08-23, after an attempt to put the ELF header inside
* the first loaded segment so that `__ehdr_start` would be usable: the entry
* moved to 0x80200270, OpenSBI still announced `Next Address 0x80200000`, and
* the machine hung executing the header as instructions. So the first thing at
* the load address has to be the first instruction, and everything that would
* otherwise be read out of the program headers is named here instead.
*
* ⚠️ FOUR THINGS BEYOND THE MINIMUM, AND EACH IS SOMETHING A C++ PROGRAM HAS
* THAT A C ONE DOES NOT.
*
* The initialiser arrays. Every static object with a constructor puts a
* pointer in .init_array, and the C library walks it between symbols the
* LINKER normally provides. A script that does not name them gets an empty
* range and every such object stays unconstructed --- which does not fail,
* it MISBEHAVES, and that is worse.
*
* The unwind tables, and the two symbols that bound them. libunwind built
* with _LIBUNWIND_IS_BAREMETAL looks them up by name instead of walking
* program headers; the fragment below is the one its AddressSpace.hpp
* documents, reproduced rather than invented.
*
* The thread-local segment, and three symbols describing it. The startup
* object in openkal-opensbi builds one context's storage from them. Without
* it the register that names thread-local storage holds whatever reset left,
* and the first `throw` faults inside `__cxa_get_globals` --- a message that
* names an exception function and says nothing about a thread pointer.
*
* A stack big enough for the standard library. 16 KiB is enough for a program
* that prints a string; formatting through <format> and sorting a vector is
* not that program.
*/
ENTRY(_start)
SECTIONS {
. = 0x80200000;
.text : { *(.text.entry) *(.text .text.*) }
.rodata : { *(.rodata .rodata.*) *(.srodata .srodata.*) }
/* Verbatim from libunwind's AddressSpace.hpp, which is where the names come
* from. `--eh-frame-hdr` on the link line is what makes the index exist at
* all; without it the section is empty and the two `_hdr_` symbols are the
* zero the conditional below produces, which libunwind reads as "no index"
* and falls back to scanning. */
.eh_frame : {
__eh_frame_start = .;
KEEP(*(.eh_frame))
__eh_frame_end = .;
}
.eh_frame_hdr : { KEEP(*(.eh_frame_hdr)) }
__eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0;
__eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0;
.preinit_array : {
PROVIDE_HIDDEN(__preinit_array_start = .);
KEEP(*(.preinit_array))
PROVIDE_HIDDEN(__preinit_array_end = .);
}
.init_array : {
PROVIDE_HIDDEN(__init_array_start = .);
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN(__init_array_end = .);
}
.fini_array : {
PROVIDE_HIDDEN(__fini_array_start = .);
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN(__fini_array_end = .);
}
/* The thread-local segment, and its three measurements.
*
* They are named here rather than read from PT_TLS for the reason at the
* top of this file: the program headers are not reachable at run time on
* this target. The startup object takes the initialised bytes from
* `__tls_start`, copies `__tls_filesz` of them, and zeroes out to
* `__tls_memsz` — which is what a loader does. */
.tdata : { __tls_start = .; *(.tdata .tdata.*) }
.tbss : { *(.tbss .tbss.*) *(.tcommon) }
__tls_filesz = SIZEOF(.tdata);
__tls_memsz = SIZEOF(.tdata) + SIZEOF(.tbss);
.data : { *(.data .data.*) *(.sdata .sdata.*) }
.bss : {
__bss_start = .;
*(.sbss .sbss.*) *(.bss .bss.*) *(COMMON)
__bss_end = .;
}
/* 256 KiB of stack. See the note above. */
. = ALIGN(16); . = . + 0x40000; __stack_top = .;
/* ⭐ AND THE HEAP, WHICH FOR THIS PROGRAM CANNOT BE THE IMPLEMENTATION'S
* DEFAULT.
*
* openkal-opensbi carries a 64 KiB static region for a program that
* allocates a little. A program carrying a C library and a C++ standard
* library allocates during its own initialisation, before `main`, and does
* not survive that figure — measured, and what it looks like is a program
* that starts and prints nothing, because the allocator runs out before
* there is a stream to report it on.
*
* So the region is stated here, where the stack's size already is, and it
* costs nothing in the image: it is past the end and QEMU's `virt` has
* 128 MiB. 16 MiB is comfortably more than this program needs and small
* enough to fit a machine with far less. */
. = ALIGN(16); __heap_start = .; . = . + 0x1000000; __heap_end = .;
}