0. 这个文件的必要性和阅读的注意事项

记录在ArceOS中实现兼容SysFS的文件系统时的思考和手记。

作为阅听者的你,除非有特别的需求,否则建议退出,因为这里的内容可能是错误的,有偏差的,毫无格式的,语言不通顺的,可能会给你带来误导,特别当你对相关问题不熟悉的情况下!!!

阅读方式:基本没有什么固定的格式,一个建议的格式如下:

  • 发生一个事件、实现、修复或思考的时候,将其记录在三级标题中
  • 在每一个三级标题下有一个四级标题,用于记录创建时间与最后修改时间

1. 开发手记

参考StarryOS,分析

创建 1-27

FdManager:一个Process中只有一个FdManager,一个FdManager就可以满足单进程多线程的要求。

对于Linux中的特殊文件:StdinStdoutStderrLinux_fs中提供支持。

1
2
3
pub struct Stdin {
pub flags: Mutex<OpenFlags>,
}

问题:在axhal中,随着sbi-rt的升级(0.02-0.03),StarryOS中依赖的getchar被标记为不可用,所以axhal换了实现方式。

  • 要找到新的axhal中支持“read”的操作:

riscv64_qemu_virt/console.rs:

1
2
3
4
5
const MAX_RW_SIZE: usize = 256;
pub fn putchar(c: u8){}
fn try_write_bytes(bytes: &[u8]) -> usize {}
pub fn write_bytes(bytes: &[u8]) {}
pub fn read_bytes(bytes: &mut [u8]) -> usize {}

应当将StarryOS中对于StdinStdoutStderr的实现,通过这几个函数进行替换。

image-20250127182835783

目前实现状况记录

2025_2_18 17:33

经过长时间的开发,目前陆续实现了多数FS的系统调用,但是遇到了几个问题

1)我使用metadata替换了原本Starry中的path_exits,在这种情况下,metadata能够在判断路径是否存在的前提下,获得更多的数据与信息。基本上我彻底抛弃了path_exits。

2)我使用deal_path路径处理替换了deal_with_path的路径处理,代码更优秀,且对复杂情况的处理更加充分。

目前遗留下如下BUG:

1)metadata函数与FileDesc等结构体中调用的metadata函数在获取权限时,都是要从文件系统的底层获取。由于axfs默认使用fatfs文件系统,而这个文件系统不支持设定权限,所以在任何情况下都会直接返回权限0o755。因此,相关的Fs系统调用(设置权限、查询权限,以及所有涉及到的对权限的检查)都是不可用的。

1.1)我尝试过使用ext4文件系统,直接迁移了Starry中的实现。但是有两个问题。第一个是目前出于种种原因,axfs始终被默认调用fatfs,我认为可能是被其他的模块调用了。而在axfs/src/root.rs中检查的优先级是fatfs>ext4fs>ext4_rs的,所以默认仍然初始化fatfs。第二个BUG是,将featuresdefault中的fatfs更改为ext4fsext4_rs,会出现BUG。ext4fs是缺失一个库导致不能编译,没有详细排查;ext4_rs是在初始化的时候报告一个尝试除以0的问题,没有详细排查;

image-20250218180009455
这个就是那个地方。后面的两个文件系统是新增的。

2)我在查看Starry的实现的时候发现,他们并没有使用ramdisk,而是使用virtio-blk。我尝试迁移使用virtio-blk却发生报错,位于axfs/src/lib.rs::init_filesystems中,报错为:No block device found!

image-20250218180334152

向上追踪:
image-20250218180412087

image-20250218180433219

image-20250218180452027

image-20250218180505471

最终定位于axdirve/src/drivers.rs中,在这里我认为有两个可能性:一)正常情况下就是使用ramdisk的,因为看不到其他的probe_global;二)我对这里不熟悉;

没有继续Debug下去。

3)在使用axfeat的时候,在尝试直接使用paging的部分,会开启axruntime/paging,而开启这个paging选项后,会导致出现LoadFault,而报错地址正是PLASH的部分。我的猜想是,在axruntime/paging开启后,会使用axmm进行内存的管理,而在这个情况下,PLASH可能是一个不可以直接访问的部分。那么就会导致出现BUG

[FIXED?]stdout在dynamic下无法访问(LoadFault)的错误

2025_2_20 22:56 - 2025_2_21 10:07

在dynamic情况下,执行FLOCK时,#define FLOCK(f) int __need_unlock = ((f)->lock>=0 ? __lockfile((f)) : 0)中会尝试访问f->lock。使用printf函数时,会传入结构体stdout,但是访问这个lock会报错:LoadFault

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
Unhandled trap Exception(LoadFault) @ 0xffffffc08018af60:
TrapFrame {
regs: GeneralRegisters {
ra: 0xffffffc08018af4a,
sp: 0xffffffc0802a8440,
gp: 0xffffffc0802a8478,
tp: 0xffffffc080219d44,
t0: 0xffffffc08025b525,
t1: 0xffffffc08018717c,
t2: 0x147b,
s0: 0xffffffc0802a85b0,
s1: 0x1,
a0: 0x0,
a1: 0xffffffc080104bf8,
a2: 0xffffffc0802a8580,
a3: 0x0,
a4: 0x0,
a5: 0x0,
a6: 0x0,
a7: 0x4442434e,
s2: 0x2,
s3: 0xffffffc0801b68e6,
s4: 0xffffffc0802a89f0,
s5: 0xffffffc0802a8930,
s6: 0xffffffc0802a8990,
s7: 0xffffffc0802453aa,
s8: 0xffffffc022008180,
s9: 0x2ca0,
s10: 0xffffffc0802ad6da,
s11: 0xffffffc0802450a8,
t3: 0xffffffc08018aef8,
t4: 0xffffffc08025d904,
t5: 0x14,
t6: 0x8042,
},
sepc: 0xffffffc08018af60,
sstatus: 0x8000000200006100,
}

报错处:image-20250220225847530

这个中可以看到a5是错误的。image-20250220225928698

可以看到,a5的值是由s0访问到的。那么s0是什么呢?0xffffffc0802a85b0,这个值是来自于Unikernelkernel的部分。

这部分是来自于static编译条件的qemu.log记录:image-20250220230524463

不难看到,其编译后结果一致。这里我们需要在static编译条件下,取出这个s0寄存器的值一看。(TODO)

目前做了如下修改:

  1. 我尝试在modify_plt_for_lib中将TYPE0的符号也链接,并且链接之后的值仍然为0(或者算上偏移量是LIB_START+0)。但是修改之后,却使得a5的值变化为a5: 0x4f758
  2. 我尝试在修改1的基础上,将c/stdio/stdout.c__stdout_FILE的前缀hidden删除,然后重新编译执行,a5的值变化为LIB_START
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
报错一:
Unhandled trap Exception(StoreFault) @ 0xffffffc08018c8ca:
TrapFrame {
regs: GeneralRegisters {
ra: 0xffffffc080187326,
sp: 0xffffffc0802a8280,
gp: 0x800000020,
tp: 0x3,
t0: 0xffffffc0802b2430,
t1: 0xffffffc08018688c,
t2: 0x147b,
s0: 0xffffffc0802a82d0,
s1: 0x1,
a0: 0x4f758,
a1: 0xffffffc080104bf8,
a2: 0xa,
a3: 0x0,
a4: 0x43454843,
a5: 0x4f758,
a6: 0x0,
a7: 0x1,
s2: 0x2,
s3: 0xffffffc0801b68e6,
s4: 0xffffffc0802a89f0,
s5: 0xffffffc0802a8930,
s6: 0xffffffc0802a8990,
s7: 0xffffffc08024541c,
s8: 0xffffffc022008180,
s9: 0x2ca0,
s10: 0xffffffc0802ad6da,
s11: 0x17,
t3: 0xffffffc08018c7e6,
t4: 0xffffffc08025da44,
t5: 0xa,
t6: 0x561fa,
},
sepc: 0xffffffc08018c8ca,
sstatus: 0x8000000200006100,
}
报错二:
Unhandled trap Exception(IllegalInstruction) @ 0xffffffc080180004:
TrapFrame {
regs: GeneralRegisters {
ra: 0xffffffc08018730e,
sp: 0xffffffc0802a82d0,
gp: 0xffffffc08025b630,
tp: 0xffffffc022008188,
t0: 0xffffffc0802b2430,
t1: 0xffffffc0801868bc,
t2: 0x147b,
s0: 0xffffffc0802a8310,
s1: 0x1,
a0: 0xffffffc0801c21e0,
a1: 0xffffffc0801043ef,
a2: 0xb,
a3: 0x16,
a4: 0xa,
a5: 0xffffffc080180000,
a6: 0x0,
a7: 0x1,
s2: 0x2,
s3: 0xffffffc0801b6916,
s4: 0xffffffc0802a89f0,
s5: 0xffffffc0802a8930,
s6: 0xffffffc0802a8990,
s7: 0xffffffc08024541c,
s8: 0xffffffc022008188,
s9: 0x2cb8,
s10: 0xffffffc0802ad6da,
s11: 0x17,
t3: 0xffffffc08018c816,
t4: 0xffffffc08025da44,
t5: 0xa,
t6: 0x3e822,
},
sepc: 0xffffffc080180004,
sstatus: 0x8000000200006100,
}

观察之后得出结论:

第二次修改后,执行流脱离了FLOCK(f)

image-20250221094632807image-20250221094648512

最后,我们可以看到image-20250221094835132

所以,可能是PC值被导向了LIB_START值。进行修改三:将在修改二的基础上,删除修改一的改动。

改动后的报错:

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
Unhandled trap Exception(StoreFault) @ 0xffffffc08018c8fa:
TrapFrame {
regs: GeneralRegisters {
ra: 0xffffffc080187356,
sp: 0xffffffc0802a8280,
gp: 0x3,
tp: 0x20,
t0: 0xffffffc08025b665,
t1: 0xffffffc0801868bc,
t2: 0x147b,
s0: 0xffffffc0802a82d0,
s1: 0x1,
a0: 0x0,
a1: 0xffffffc080104bf8,
a2: 0xa,
a3: 0x0,
a4: 0x43454843,
a5: 0x0,
a6: 0x0,
a7: 0x4442434e,
s2: 0x2,
s3: 0xffffffc0801b6916,
s4: 0xffffffc0802a89f0,
s5: 0xffffffc0802a8930,
s6: 0xffffffc0802a8990,
s7: 0xffffffc08024542a,
s8: 0xffffffc022008188,
s9: 0x2cb8,
s10: 0xffffffc0802ad6da,
s11: 0x17,
t3: 0xffffffc08018c816,
t4: 0xffffffc08025da44,
t5: 0x14,
t6: 0x8042,
},
sepc: 0xffffffc08018c8fa,
sstatus: 0x8000000200006100,
}

image-20250221095324168

思考:1. 在修改2删除了hidden标签后,执行通过FLOCK处。2. 在修改1删除之后,我们可以看到,PC并没有被导向LIB_START。是否可以推演:我们需要的那个值,因为被打上了hidden标签,而导致出现了问题?当我们采用static选项之后,就可以正确的链接到我们需要的值。

我们在2、3中见到了6*nop的标签,但是没有看到5*nop的标签。说明错误发生在之中。

image-20250221095504884

紧接着的思考是:真的是hidden标签本身的问题,还是说hidden标签没有被正确使用的问题?

我尝试迁移了文件c/include/stdio.h,没有任何变化。我开始思考,是否是编译脚本的问题?

2025_2_21 11:35

新的想法出现:GOT表中是不是就不应该出现一个LOCAL类型的符号?因为LOCAL类型符号本来不应该在OBJECT外部调用。在静态链接的条件下,也就不需要进行修改。可在动态链接的条件下,上哪里修改呢?

2025_2_22 8:59

我刚才一直在尝试删除库中的hidden标签,突然有个想法,这个出现问题的原因可能是因为在定义处和实现处标签不一致的原因。在声明(往往是头文件)中,被标记为hidden,但是在C文件的定义处,却并没有被标记为hidden,而且这个问题貌似广泛存在。

2025_2_22 15:34

修改了很多东西,目前卡在了image-20250222151108806

在这里只要传入f就会报错。目前考虑进行追踪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
START: vfprintf
======
Entry: <printf_core>
JAL: printf_core, arg list:
---
addi a4,s0,-88
addi a3,s0,-256
addi a5,s0,-48
mv a2,a5
ld a1,-352(s0)
ld a0,-344(s0)
jal ra,-3698
---
ACTUALLY TO: pritnf_core + (32096-31f7c), arg list:
---
lw a5,-68(s0)
mv a2,a5
ld a1,-24(s0)
ld a0,-200(s0)
jal ra,-8156
---32096j
======
Entry: <out>
TO:

2025_2_22 18:34

==完成BUG修正,目前还是保留了一部分BUG。==

我在c/internal/stdio_impl.h中做了如下修改(删除hidden标签的同时,加载LIB_STARTimage-20250222183633312

目前加载的时候的debug log如下:image-20250222183744559

但是目前依然不能取消这几个hidden标签。可能未来在其他的已经取消hidden标签的代码处会有BUG。

重点其实在于这部分的代码:
image-20250222190233554

==重点在于,其实RELATIVE是否在某种程度上需要被设置?==

目前能知道的信息是,在某些情况下,(貌似是全局变量)某些量需要通过上图的Addend来设置,这个是一个偏移量,也就是真正的地址在LIB_START+Addend;而某些情况下,(貌似是函数)是不能如上设置的(会跑飞)。

现象是会跑到LIB_START处,明显是PC跑飞的症状:IllegalInstructionimage-20250222190721619

那么下一个问题是:是否可以提出这样的一个猜想:对于全局变量而言,需要被设置,而对于函数而言,应该删除hidden标签?

__nl_langinfo_l函数跑飞:

3_20 12:24

跑飞的状态:
image-20250320122504113

错误的函数名称:image-20250320122524409

错误发生处:image-20250320122546670

源代码:c/locale/langinfo.c
image-20250320122612487

根据函数调用(LCTRANS转换为__lctrans函数)关系来看,这个a5寄存器应该是在要传递a1参数,也就是代码中的(loc)->cat[(lc)]

image-20250320123435588

我们将locale_t结构展开讨论,存放cat的结构是这样的:(internel/libc.h)
image-20250320123544275

根据源码,我猜想如下:

  1. loc->cat本身不能访问。(大概率)
  2. loc->cat[n]不能访问。

先看看是谁调用了它:
整个从4a940而跳转来,函数本身是__strftime_fmt_1
image-20250320124049812image-20250320124107782

函数定义:c/time/strftime.c
image-20250320124226750

调用处:image-20250320124324871

loc也是被简单传入的:image-20250320124351175image-20250320124412118

image-20250320124451989

最终定位,可能是CURRENT_LOCALE宏的问题。

image-20250320124639074

充分说明了问题:__pthread_self()->locale的结构存在问题。==而我们之前有做过修改,__pthread_self()不是通过tp获取的,而是通过直接调用abicall完成的,也就是这个函数:
image-20250320124815493

观察输出也能验证这个想法:image-20250320124839400

其调用的是posix的代码,但是被转化为pthread_t类型的是Pthread,而我严重怀疑这个返回可能未必与musl中的相同:

对比:image-20250320130240045image-20250320125506396image-20250320130027912image-20250320130105864

分别位于:(…), (…), (include/alltypes.h.in), (c/internal/libc.h)

==但是可能的一个好的问题是:那errno不是能正常设置吗?为什么那个可以而这个不行?==

image-20250320130609414

所以我们可以知道,可能:pthread_self()->localepthread_self()->errno_val是一样的,都是存在的,可是locale->cat就没办法正常获得了。image-20250320130825829

此外,我还要注意,在开发的时候不要一股脑的cp,要考量原本的代码有没有修改。````