rCore-Tutorial-Book-v3
rCore-Tutorial-Book-v3 copied to clipboard
rCore-Tutorial-Book-v3/chapter1/5support-func-call
为内核支持函数调用 — rCore-Tutorial-Book-v3 3.6.0-alpha.1 文档
https://rcore-os.github.io/rCore-Tutorial-Book-v3/chapter1/5support-func-call.html
在代码
(sbss as usize..ebss as usize).for_each(|a| {
unsafe { (a as *mut u8).write_volatile(0) }
});
为什么将每一个usize(地址值)转化成u8的指针?
@MrZLeo 不就是对整片区域逐字节置零吗,1u8就是1个字节
我们尝试将其放置到全局数据 .data 段中但最后未能成功,因此才决定将其放置到 .bss 段中 请问一下,这个未能成功是指的是没有被连接到.data中吗,我修改了link文件,将代码stack的内存放到.data
我们尝试将其放置到全局数据 .data 段中但最后未能成功,因此才决定将其放置到 .bss 段中
请问一下,这个未能成功是指的是没有被连接到.data中吗,我修改了link文件,将代码stack的内存放到.data中, 我发现是成功的
[rustsbi] enter supervisor 0x80200000
Hello, world!
.text [0x80200000, 0x80202000)
.rodata [0x80202000, 0x80203000)
.data [0x80203000, 0x80213000)
boot_stack [0x80203000, 0x80213000)
.bss [0x80213000, 0x80213000)
Panicked at src/main.rs:49 Shutdown machine!
@MrZLeo 不就是对整片区域逐字节置零吗,1u8就是1个字节
嗯嗯😆
函数调用与栈中配图:调用函数A 之后,回复上下文 应为 恢复上下文
在代码
(sbss as usize..ebss as usize).for_each(|a| { unsafe { (a as *mut u8).write_volatile(0) } });
为什么将每一个usize(地址值)转化成u8的指针?
*mut u8 解引用了,相当于取值,地址并非是u8,而是把值当作u8,并写成0
可以发现在控制权转交给内核之后会执行两条指令
之后 应该改成 之前吧
我们尝试将其放置到全局数据 .data 段中但最后未能成功,因此才决定将其放置到 .bss 段中
请问一下,这个未能成功是指的是没有被连接到.data中吗,我修改了link文件,将代码stack的内存放到.data中, 我发现是成功的
[rustsbi] enter supervisor 0x80200000 Hello, world! .text [0x80200000, 0x80202000) .rodata [0x80202000, 0x80203000) .data [0x80203000, 0x80213000) boot_stack [0x80203000, 0x80213000) .bss [0x80213000, 0x80213000) Panicked at src/main.rs:49 Shutdown machine!
在第一章的代码基础上测试,的确是可以把启动栈放到.data段中的,不知道后续会不会出问题 第一章的测试代码: https://github.com/jiangshengdev/rust-system/blob/f8eac5291e2c50050a90b8e761e0d9323c051754/os/linker.ld#L27
注解 栈帧 stack frame部分的:“在通常的情况下,我们并不需要区分调用函数函数和被调用函数分别使用了栈的哪个部分。” 这里的“调用函数函数”是否应该是“调用函数”?
大家好,关于这部分代码
extern "C" {
fn sbss();
fn ebss();
}
这部分的调用约定我们可以在什么地方查阅到,我有点不知道怎么查找
@sunboyZgz rust标准库和The Rust Programing Language里有简要的提到
前面我们提到过 .bss 段一般放置需要被初始化为零的数据。然而栈并不需要在使用前被初始化为零,因为在函数调用的时候我们会插入栈帧覆盖已有的数据。我们尝试将其放置到全局数据 .data 段中但最后未能成功,因此才决定将其放置到 .bss 段中。全局符号 sbss 和 ebss 分别指向 .bss 段除 .bss.stack 以外的起始和终止地址,我们在使用这部分数据之前需要将它们初始化为零,这个过程将在下一节进行。
我觉得这段内容有点问题, data是已经初始化的数据,所以是要在二进制文件中占用空间的,而bbs段因为都是未初始化的数据所以加载的时候由内核或者加载器将其初始化为0,本身在二进制文件中并不占用空间,基于栈内存的性质我觉得放置在bbs是更为合理的选择
栈是从高地址向低地址增长,因此我们用更高地址的符号 boot_stack_top 来标识栈顶的位置,而用更低地址的符号 boot_stack 来标识栈底的位置
这里是不是反了
@zhayujie 我自己理解好像也是反了
前面我们提到过 .bss 段一般放置需要被初始化为零的数据。然而栈并不需要在使用前被初始化为零,因为在函数调用的时候我们会插入栈帧覆盖已有的数据。我们尝试将其放置到全局数据 .data 段中但最后未能成功,因此才决定将其放置到 .bss 段中。全局符号 sbss 和 ebss 分别指向 .bss 段除 .bss.stack 以外的起始和终止地址,我们在使用这部分数据之前需要将它们初始化为零,这个过程将在下一节进行。
我觉得这段内容有点问题, data是已经初始化的数据,所以是要在二进制文件中占用空间的,而bbs段因为都是未初始化的数据所以加载的时候由内核或者加载器将其初始化为0,本身在二进制文件中并不占用空间,基于栈内存的性质我觉得放置在bbs是更为合理的选择
有道理。
@zhayujie @ScathonLin 多谢勘误,的确写反了。
@MrZLeo 因为不想考虑bss section的大小是多少字节的整数倍, 因此就一个字节一个字节给清零
我觉得栈,地址高的做栈顶没反呀
确实反了。。。
RISC-V的栈帧第一个数据是返回地址,和x86的不一样,看的时候还以为写错了。于是到riscv-spec查,没有发现frame里关于fp的布局应该是怎么样的,之后发现康奈尔的ppt里说: There is no one true RISC-V calling convention。那么这章里用的RISC-V calling convention是依据哪里来的calling convention呢?为什么选用这个calling convention?
这章里用的RISC-V calling convention(的frame stack布局)是依据哪里来的calling convention(的 frame stack布局)呢?为什么选用这个calling convention(的frame stack布局)?
@longguzzz 据我所知,目前应该仍然不存在一个通用的调用规范,不同的编程语言和编译工具链以及传给编译器的参数都会影响到目标文件采用何种调用规范。因此,本节也仅仅是给出一个例子,并不一定能够精准对应到某个实际存在的调用规范。不过,在使用Rust语言的情况下,再加上适当的参数(与frame pointer相关),能够看到ra和fp的位置与本节给出的例子是一致的。
ra( x1 ) 是调用者保存的,不过它并不会在每次调用子函数的时候都保存一次,而是在函数的开头和结尾保存/恢复即可。
这句话是什么含义呢?根据函数栈帧的结构来看ra
不是在每次函数调用的时候保存吗?
@Riptide3 之前的说法有些模糊,现在已经做了些修改。
@zhayujie @ScathonLin 多谢勘误,的确写反了。
现在代码和文档都进行了调整。
请问为什么Rust通过外部调用访问linker中的全局符号是通过函数的形式? 在Rust的手册中对于extern的使用方法只简要介绍了C函数调用,不知道到哪里找相应的规范。 查找到在C代码中调用链接脚本中的变量的方式是将其作为extern int A导入,其地址&A是对应的符号地址。 在这里Rust使用的两个函数sbss()及ebss()返回的就是相应全局符号的地址吗?而之后还是通过变量的方式进行使用,看起来其实还是某种调用规范?
extern "C" {
fn sbss();
fn ebss();
}
(sbss as usize..ebss as usize).for_each(|a| {
unsafe { (a as *mut u8).write_volatile(0) }
});
@xukp20 我们想要达到的效果是:在extern "C"
里面提到的sbss
和ebss
就只是两个在其他位置(链接脚本linker-qemu.ld
中)声明的全局符号,我们期望在链接的时候这两个符号能正确被修改为它们所在的地址,进而才能知道.bss段的位置并完成初始化。那我们怎么做呢?目前只能想到用FFI的方式来引入,根据官方文档,在extern "C"
块中似乎只能引用ABI接口,也就是一个函数签名,需要有函数名、参数列表和返回值。好像不能像C语言那样extern int c;
这样做。引入之后sbss
和ebss
都变成函数了,所以有as usize
将其转换成函数入口地址也就是符号自身的地址。
@xukp20 不对,其实是能类似于extern int c;
这样用的,我刚看到这样的例子。有空改代码。
好的谢谢🤣