Skip to content

Commit b17d0e5

Browse files
committed
feat: 加上接收控制的那个对象(standalone)
openkal-linux 和 openkal-windows 都有一个 start 对象:内核把控制交过来,它找到 参数、建立线程指针、调用程序。这里补上对应的一个,给一个交出的东西少得多的环境。 ⚠️ 固件交出来的和内核交出来的不是一回事。内核启动一个**程序**:栈上有参数、有 线程指针要建、有程序头可读、栈已经在那里了。固件启动一个**镜像**:在 S 模式跳到 装载地址,a0 是 hart 号,a1 是设备树,别的没有。 于是这个文件多做一件、少做一件: - 多:让栈存在。而 __stack_top 由**程序的链接脚本**定义,因为栈放哪里是关于镜像 布局的陈述,而镜像是程序的。本包引用它、不定义它 —— 没有脚本就链接失败,这是 正确的报告。 - 少:不读参数向量,因为没有人可能供给过一个。openkal.env 在这个环境的答案是 「一个都没有」,这里传同样的答案,两边就不会互相矛盾。 __libc_start_main 弱引用,和另一个一样:直接写在 openkal 上的程序没有这个符号, 那时这个文件自己跑 init_array 再调 main。 feature 名沿用 standalone —— 它是关于**程序**的陈述,不是这个实现的精简版本; 知道是哪种安排的消费者才有资格声明它,所以由 openkal-musl 声明,本包不默认打开。
1 parent 859297c commit b17d0e5

2 files changed

Lines changed: 119 additions & 0 deletions

File tree

mcpp.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,21 @@ repo = "https://github.com/mcpplibs/openkal-opensbi"
2525
[dependencies]
2626
openkal = { git = "https://github.com/mcpplibs/openkal", branch = "feat/openkal-closure" }
2727

28+
# ⭐ WHAT RECEIVES CONTROL, WHICH IS A STATEMENT ABOUT THE PROGRAM.
29+
#
30+
# A program that already carries a runtime has an entry object of its own and
31+
# must not get a second. A program that does not — one whose C library is
32+
# openkal-musl, or one written directly against openkal — needs something to
33+
# make a stack exist and call it. src/start.cpp is that something, and it is
34+
# compiled only when this feature says the second arrangement holds.
35+
#
36+
# It is not a smaller or faster variant of this implementation. It is the same
37+
# distinction openkal-linux and openkal-windows draw with the same name, and the
38+
# consumer that knows which arrangement holds is the one that declares it —
39+
# which is why openkal-musl declares it and this package does not default it on.
40+
[features]
41+
standalone = { defines = ["OPENKAL_OPENSBI_STANDALONE"] }
42+
2843
[build]
2944
# The same reasoning openkal's other implementations record: no exception may
3045
# propagate out of a C entry point, and beneath a supervisor there is no

src/start.cpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Program startup, for a program that carries no runtime of its own.
2+
//
3+
// The same object openkal-linux supplies, for an environment that supplies far
4+
// less. It belongs to the implementation for the same reason: every step is a
5+
// fact about what hands control over, and a consumer that contained these steps
6+
// would contain a copy of them per environment.
7+
//
8+
// ⚠️ WHAT FIRMWARE HANDS OVER, AND WHAT IT DOES NOT.
9+
//
10+
// A kernel starts a program with arguments on the stack, a thread pointer to
11+
// establish, program headers to report, and a stack already there. Firmware
12+
// starts an IMAGE: it jumps to the load address in supervisor mode with a hart
13+
// identifier in a0 and a device tree in a1, and nothing else. There is no
14+
// argument vector to find, because there is no one to have supplied one; and
15+
// there is no stack, because a stack is a region of the image's own memory and
16+
// only the image knows where it put one.
17+
//
18+
// So this file does two things the other does not have to: it makes the stack
19+
// exist, and it stops. And it does not do the thing the other spends most of
20+
// its length on, because there is nothing to read.
21+
//
22+
// ⚠️ THE STACK COMES FROM THE PROGRAM'S LINKER SCRIPT AND CANNOT COME FROM HERE.
23+
//
24+
// `__stack_top' is defined by the linker script the program supplies, because
25+
// where the stack goes is a statement about the image's layout and the image is
26+
// the program's. This package names the symbol and does not define it; a
27+
// program that links this object without a script that defines it is told so by
28+
// the linker, which is the right report.
29+
//
30+
// Control is handed on through `__libc_start_main', weakly, exactly as the
31+
// other does: a program written directly against openkal has no such symbol,
32+
// and then this file runs the initialisers and calls `main' itself.
33+
#ifdef OPENKAL_OPENSBI_STANDALONE
34+
35+
#include <openkal/abort.h>
36+
37+
extern "C" {
38+
39+
int main(int, char**, char**);
40+
41+
[[gnu::weak]] int __libc_start_main(int (*)(int, char**, char**), int, char**,
42+
void (*)(), void (*)(), void (*)());
43+
44+
[[noreturn]] void __okb_start_c(void);
45+
46+
}
47+
48+
namespace {
49+
50+
using initialiser = void (*)(int, char**, char**);
51+
52+
[[gnu::weak]] extern initialiser __preinit_array_start[];
53+
[[gnu::weak]] extern initialiser __preinit_array_end[];
54+
[[gnu::weak]] extern initialiser __init_array_start[];
55+
[[gnu::weak]] extern initialiser __init_array_end[];
56+
57+
void run_initialisers() {
58+
// ⚠️ Only when no C library took the hand-over. One that did runs these
59+
// itself, and running them twice constructs every static object twice.
60+
static char* nothing = nullptr;
61+
for (initialiser* p = __preinit_array_start; p != __preinit_array_end; ++p)
62+
(*p)(0, &nothing, &nothing);
63+
for (initialiser* p = __init_array_start; p != __init_array_end; ++p)
64+
(*p)(0, &nothing, &nothing);
65+
}
66+
67+
} // namespace
68+
69+
// The stack, then C. `la` of a linker-defined symbol resolves at link time, so
70+
// the sequence needs nothing to have been set up before it runs --- which is
71+
// the situation it is in.
72+
//
73+
// ⚠️ In `.text.entry` rather than `.text`, because the linker script places
74+
// that section first. Firmware jumps to the load address, not to `_start`: the
75+
// entry recorded in the image header is not read by firmware that loads a raw
76+
// image, so the first instruction at the load address has to BE this one.
77+
asm(".section .text.entry\n"
78+
".globl _start\n"
79+
".type _start,@function\n"
80+
"_start:\n"
81+
" la sp, __stack_top\n"
82+
" call __okb_start_c\n"
83+
"1: j 1b\n"
84+
".size _start,.-_start\n");
85+
86+
extern "C" [[noreturn]] void __okb_start_c(void) {
87+
// Zero and null rather than a vector: openkal.env is what a program above
88+
// this reads its arguments through, and this environment's answer there is
89+
// that there are none. Passing the same answer here keeps the two from
90+
// disagreeing.
91+
static char* nothing = nullptr;
92+
93+
if (__libc_start_main != nullptr) {
94+
__libc_start_main(main, 0, &nothing, nullptr, nullptr, nullptr);
95+
// A C library's hand-over does not return. Reaching here means one did,
96+
// and continuing would run the program a second time.
97+
kal_exit(127);
98+
}
99+
100+
run_initialisers();
101+
kal_exit(main(0, &nothing, &nothing));
102+
}
103+
104+
#endif // OPENKAL_OPENSBI_STANDALONE

0 commit comments

Comments
 (0)