[TOC]
0. 这个文件的必要性和阅读的注意事项
因为自己在实现musl兼容库——mocklibc的时候,往往要进行一些分析,也要写一些东西。这些东西往往不成体系,也没有什么总结的价值,基本是随写随丢的状态。但是这个随写随丢有时候反而会造成一些困扰,特别是在回顾之前思路的时候。同时,写下这个博客也算是记录自己的工作内容
所以,我开了这个文件,用来记录自己的笔记。作为阅听者的你,除非有特别的需求,否则建议退出,因为这里的内容可能是错误的,有偏差的,毫无格式的,语言不通顺的,可能会给你带来误导,特别当你对相关问题不熟悉的情况下!!!
阅读方式:基本没有什么固定的格式,一个建议的格式如下:
- 发生一个事件、实现、修复或思考的时候,将其记录在三级标题中
- 在每一个三级标题下有一个四级标题,用于记录创建时间与最后修改时间
1. mocklibc开发手记
[ADD] sleep函数
创建:2025_1_18_21:53;修改:2025_1_18_22:09
需要实现的:sleep(),依赖于:nanosleep(),nanosleep依赖于syscall_ret和__clock_nanosleep()
syscall_ret本身不难实现,注意的是要写入一个叫做errno的全局变量中
__clock_nanosleep或许可以作为替换的选择
__clock_nanosleep函数依赖__syscall_cp函数,以及IS32BIT宏(定义在好多地方)
src/thread/__syscall_cp.c 在中,通过 week_alias 告诉我们当 single thread 的时候 syscall_cp 可以退化为普通的 syscall 的。
不行的话,我就替换__clock_nanosleep
有一说一可以直接用sleep,ArceOS底层有这个函数[X],就用这个
完成支持。
[FIXED] BUG 实现pthread
创建19号上午 最后更新:2025-1-24 12:02
很奇怪,在不同情况下会跑飞之类的,但是也就这样了吧,先放下,解决另一个
在杨金全同学的帮助下完成了修正,具体请看[这个内容](#[FIXED] 在关于ArceOS mocklibc_libloader中线程创建的BUG修复[见专题文章])
[XXX] BUG Math库中,所有带l(long)类型的操作都会被导向到一些奇怪的符号,例如__getf2
创建19号中午
可能的相关文章:https://github.com/ziglang/zig/issues/5320
-fno-compiler-rt是什么?
有关运行时库的内容:https://compiler-rt.llvm.org/
这个是CSDN上的运行时库的介绍:https://blog.csdn.net/ssdlearnerused/article/details/107426495
这个是一个类似问题,有解决的:https://rustcc.cn/article?id=50ac4eec-17eb-4fc7-be08-a23d7794fefc
最接近和具有参考价值的:https://github.com/termux/termux-packages/issues/8029



Yeah, that’s the problem: libtool supplying all the link flags to the compiler and overriding the runtime library the compiler chooses with
-nostdlib. Maybe it needs to be patched to addcompiler-rtinstead oflibgcc, but in the meantime, perhaps we could override it by passing in the-rtlib=compiler-rtflag for those packages, as I did with the Swift CMake config.
是的,这就是问题所在: libtool 向编译器提供所有链接标志,并使用-nostdlib覆盖编译器选择的运行时库。也许需要修补以添加compiler-rt而不是libgcc,但与此同时,也许我们可以通过为这些包传递-rtlib=compiler-rt标志来覆盖它,就像我对 Swift CMake 配置所做的那样。
gcc关于这个问题的通信邮件:
https://gcc.gnu.org/legacy-ml/gcc/2001-09/msg00262.html
https://gcc.gnu.org/legacy-ml/gcc/2001-09/msg00327.html
第一次修改方式
添加链接:$($CC -print-libgcc-file-name)
第一次修改前报错:
1 | make -C hello dynamic |
第一次修改后报错:
1 | make -C hello dynamic |
问题并未得到解决,这里我想应该是将上述符号切换为DEFAULT(HIDDEN),而不是让他们保持NOTYPE.
这是另一个相关的:https://gcc.gnu.org/bugzilla/show_bug.cgi?format=multiple&id=36669
该问题的完整描述:
我的问题是这样的:我使用的是musl的编译器(版本不重要),其在面临float_128类型变量的时候,出于某些我目前不能肯定的原因(我有看到说musl不支持这个长度),会尝试链接外部的函数,例如:__getf2这样的。
但是相关函数musl库本身并没有实现,那么我使用musl编译库的时候就会出现,由于我是要开发自己的库,使用了-nostdlib,所以屏蔽了标准库,然后我想说要不就先链接已有的这些外部函数。路径如下:/home/marinatoo/App/riscv64-linux-musl-cross/bin/../lib/gcc/riscv64-linux-musl/11.2.1/libgcc.a(getf2.o),然后链接后出现的问题是:musl提供的gcc库里相关的符号是hidden的。然后我尝试链接会报错:riscv64-linux-musl-ld: hello: hidden symbol __getf2' in /home/marinatoo/App/riscv64-linux-musl-cross/bin/../lib/gcc/riscv64-linux-musl/11.2.1/libgcc.a(getf2.o) is referenced by DSO。不链接的话就是riscv64-linux-musl-ld: ../../ulib/mocklibc_lib/lib/libmock.so: undefined reference to __getf2'
不过这个在遇到float 128类型的时候调用llvm的compile_rt的行为是编译器进行的,所以其实有一个解决方案,就是将它需要的函数自己实现出来,不过考虑到其工作之多,我选择去部分使用llvm的那个实现,不过llvm最终就是用的我上面的那个gcc的实现,我也不确定是不是musl编译器自己的行为。
但是我出问题的函数是个纯数学库,然后编译后的目标文件中引用了符号”__getf2”,然后在这里是没有调用这个函数的,就是,这个是一个0函数调用库,它使用了一些宏定义(如 RPOLY 和 VPOLY)来计算多项式,但这些宏只是用来计算值,并不涉及对其他函数的调用。
然后我的编译选项如下:CFLAGS = -nostartfiles -ffreestanding -nostdlib -O0 -mcmodel=medany $(INTERNAL_INCLUDE) -fPIC
$(SHARED_LIB): $(LIBOBJS) $(CRTOBJS) $(CC) $(CFLAGS) $(LDFLAGS) -shared -fPIC -o $@ $^
LINK_FLAGS += /home/marinatoo/App/riscv64-linux-musl-cross/bin/../lib/gcc/riscv64-linux-musl/11.2.1/libgcc.a # 尝试修正compile-rt被-nolib屏蔽的问题,也就是__getf2符号没有的问题。
这个LINK_FLAGS就是在尝试手动链接这些缺失的库的,不过可以不加
==最终我还是选择自己去实现这些函数,然后直接链接了,然后在这里函数中跳转到一个“未实现”的系统调用,然后直接退出==
思考:是否可以找到足够底层且开放的输出接口,去完成musl中的输出底层接口的替换
创建:1_20_9:06
可能是axhal::console::write_bytes(s.as_bytes());,不带fmt,能直接输出,似乎可以作为musl中print_core函数的输出接口。往下再探索,发现其实就已经是跟具体的架构相关的代码了,感觉不是很适合继续挖掘。
现在的问题是,我打算直接使用printf_core来验证思路,但是printf_core在使用errno,而errno是需要通过__errno_location()的返回值来获取的,__errno_location()依赖__pthread_self()来获得,同时也依赖其返回的结构体指针,然后我要定位这个函数的位置。
#define __pthread_self() ((pthread_t)__get_tp()),依赖__get_tip()。这个与架构相关,RV64的:nvim arch/riscv64/pthread_arch.h
1 | static inline uintptr_t __get_tp() |
我在想,这样的汇编其实什么都没做,不会影响执行流(对于RV64),那么是否可以直接用?
==我发现printf函数在面对FILE的问题的时候,需要大幅修改原来函数,会使得思路二的优势丧失,不建议使用了==
==在没有系统解决FILE的兼容之前,不能盲目替换。因此暂时放弃相关想法==
开发errno以及__errno_location()函数
创建:2025_1_20 13:16
直接复制的,用的就是上面的思路,直接使用,经过测试是报错了。
在理论上而言,tp应当指向一个结构体,映射关系就在这个结构体中,说明当前的硬件线程编号,以及已经分配的地址空间。但是我发现报错了,报的LoadFault。但是tp看不太出来问题:
1 | Unhandled trap Exception(LoadFault) @ 0xffffffc080110670: |

总体而言是这样的,所以是出现了一些问题,本来应该去访问高地址的

所以是tp寄存器本身的问题,这里会出现问题,也就是tp寄存器存在问题。tp寄存器存放的数值很奇怪,或者说,是否意味着,这个值本来就是在低地址呢?
==保留BUG==
我觉得得做文件系统的迁移了
创建:1_20 16:20
思路,先通过对文件系统操作的迁移,理解ArceOS的文件系统与Linux的文件系统的最基础共同点,然后以此为基础,进行对Linux文件系统的模拟
然后文件系统的迁移要优先考虑一些宏观操作的相同。
重点是能不能找到一些同级抽象的东西,或者说比目标兼容库更底层的操作,并以这些操作为基础组合模拟目标兼容库的底层操作。
做了一些
[FIXED] BUG修复:动态链接编译的时候,尤其是开了没有startfile的那个选项后,entry可能不是main函数
在load.rs中修复:

修改后:

[FIXED] BUG修复:lgamma_r.c文件中的函数在编译后会出现bgu指令,对a5寄存器的空间写,但是a5已经成0了,目前看不出有什么特别的点
创建时间:1_21 20:29


很神奇
出于对sw命令的定位,我做了如下操作:

所以,问题定位完成。
问题是,这个东西理论上是有的哇:

可能是因为是全局变量的原因,没有被正确的指定!


所以我们可以知道,要在modify的时候,为这些全局变量做modify。

这个符号我好像早就注意到了,但是一直没有理会,没想到真的出问题了。
一个很值得思考的是,函数是txt,所以位置是固定的,也不用考虑那么多,但是全局变量是什么东西?这个放在哪里?
最后修复方案:
阅读计划:https://blog.csdn.net/tjcwt2011/article/details/106520862
[FIXED] 超长期的一个BUG
创建:1_21 20:44 修改: 1_24 11:32
我发现,似乎在某些情况下,qemu在启动-l debug选项的时候就会发生:qemu卡死在一个tlb重填的位置,然后一直卡着,关闭掉qemu之后,反而在qemu.log上一直在写入,但是在文件系统中找不到文件,可是硬盘很快就会占满!具体描述后面我会插视频!
然而这个bug感觉似乎不是随时都可以的,而是在特殊的一段时间内,会频繁的发生,现在就可以,我有一个猜想:==可能是内存剩余空间少的时候,就会发生问题!==
==已经修正==
这个BUG的修正似乎与下面这个有一定的关系,当我修正[这个问题](#[FIXED] 关于在部分情况下使用debug参数和其他参数结果不一致的问题)后,这个超长期BUG就自己消失了。
[FIXED] 关于在部分情况下使用debug参数和其他参数结果不一致的问题
创建:2025_1_22,修改:2025_1_24 11:30
在过去一段时间内,在杨金全同学的帮助下,我发现了在libloader_lib应用中src/main.rs下汇编代码的一处错误,在修正这个错误后,似乎在执行QEMU模拟的时候不会再发生启用debug log参数和启用info log参数在结果上有不同的bug了。
具体如下:

修改后:

由于写下这个记录距离修正BUG已经过了两天,所以记录不是很详细
推测是由于写入的错误,损坏了一些关键信息,而这些信息在启用debug参数后是会被关键依赖,导致控制流程跑飞。具体跑飞包括LoadFault、StoreFault、TLB反复重填写等等。在启用qemulog记录的情况下,还会出现卡死、无法退出模拟,==并且在强行关闭后依然发生着向qemulog持续不停的写入新记录的情况,只能重启解决。这个问题我觉得很有意思,而且应该会导向qemu的一个关键bug。目前qemu的版本为9.1.2,系统是Ubuntu22.04 LTS。目前暂时不准备解决。我捕捉到一次发生的卡死是卡在了TLB重填写的部分,在访问一个地址的时候发生TLB缺失,重填后依然缺失,如此循环。==

这个是一个截图的一部分,但是也完整涵盖了整个循环流程,因为截图中有隐私信息,就不全部展示了。
[Bug reserved.]关于Qemu中可能存在的BUG的讨论
创建:2025-1-24 11:44
[上述BUG](#[FIXED] 关于在部分情况下使用debug参数和其他参数结果不一致的问题)修正前会出现:
推测是由于写入的错误,损坏了一些关键信息,而这些信息在启用debug参数后是会被关键依赖,导致控制流程跑飞。具体跑飞包括LoadFault、StoreFault、TLB反复重填写等等。在启用qemulog记录的情况下,还会出现卡死、无法退出模拟,==并且在强行关闭后依然发生着向qemulog持续不停的写入新记录的情况,只能重启解决。这个问题我觉得很有意思,而且应该会导向qemu的一个关键bug。目前qemu的版本为9.1.2,系统是Ubuntu22.04 LTS。目前暂时不准备解决。我捕捉到一次发生的卡死是卡在了TLB重填写的部分,在访问一个地址的时候发生TLB缺失,重填后依然缺失,如此循环。==

这个是一个截图的一部分,但是也完整涵盖了整个循环流程,因为截图中有隐私信息,就不全部展示了。
QEMU可能存在的BUG点是:在运行的时候卡死,就不能正常退出,而且退出后依然在向log书写,即使删除qemu.log,还是会继续书写,直到将空间占满卡崩Ubuntu。
BUG可以通过以下方式复现,感兴趣的可以去查看:
访问BUG所在的仓库分支,BUG存在于这一条提交记录中。clone之后,在arceos(也就是下载下的那个目录)中切换到对应的mocklibc_libloader分支下的提交记录:Commit 1c38b66,然后在arceos目录下运行命令./loader_lib.sh -l debug,就可以观察到。
再次声明我的环境:
1 | qemu --version |