2025-03-25  2025-04-07    1952 字  4 分钟
AOS

哦对的对的,哎呀不对不对,对…对吗?

并非全力

0x500 处的内存不要碰,否则可能导致程序跑飞

可能是由于BIOS占用了这个区间的一部分内存,随便改写导致异常。

但是实模式内存分布图上显示这一块确实是随便用。

LBA读取磁盘扇区后,fs失去4GB寻址能力

fs通过切换到保护模式加载了段选择子,拥有了4GB寻址能力。在返回实模式后仍然拥有4GB寻址能力,但是不能再对fs段寄存器进行赋值操作,否则将失去4GB寻址能力。猜测LBA读取磁盘时可能对fs段寄存器进行了修改,导致失去4GB寻址能力。

并非无法战胜

printk因调用strlen导致打印出错

首先给出printk调用,功能为输出VBE的OEM字符串及其地址。

1char * pv = (char *)((((ul)VbeInfo->OemStringSegment)<<4)+VbeInfo->OemStringOffset);
2printk(YELLOW, BLACK, "%lx %s\n", pv, pv);

原始strlen源码,输出为 字符串1 LGPL VGABIOS Developers Bochs VBE ...

 1inline uint strlen(const char *__s) {
 2        register int __res;
 3        __asm__ __volatile__ (
 4                "xor   %[result], %[result]   \n"
 5                "decl  %[result]              \n"
 6                "xor   %%al, %%al             \n"
 7                "repnz scasb                  \n"
 8                "notl  %[result]              \n"
 9                "decl  %[result]              \n"
10                :
11                [result]"=c"(__res)
12                :
13                [input]"D"(__s)
14                :
15        );
16        return __res;
17}

修改后strlen源码,输出为 字符串2 Bochs VBE (C) 2002-2020 ...

 1inline uint strlen(const char *__s) {
 2        register int __res;
 3        __asm__ __volatile__ (
 4                "xor   %[result], %[result]   \n"
 5                "decl  %[result]              \n"
 6                "xor   %%al, %%al             \n"
 7                "repnz scasb                  \n"
 8                "notl  %[result]              \n"
 9                "decl  %[result]              \n"
10                :
11                [result]"=c"(__res),
12                [input]"=&D"(__s)
13                :
14                "[input]"(__s)
15                :
16        );
17        return __res;
18}

通过分析可知,由于strlen在开启-O优化后将作为内联函数展开到调用部分,而在strlen中对di寄存器的值进行了修改导致di指向了字符串末端。通过查看内存可知,字符串1 紧跟在 字符串2 后面。

若不开启内联,则不会出现此问题

代码被移动后无法正确计算地址

在切换到 user level 的代码段后,无法正确执行printk(函数入口计算错误)

在分析反汇编代码配合bochs单步调试后,发现gcc8默认在调用函数时会通过计算来确定所调用的函数入口地址,在计算过程中便用到了 GLOBAL_OFFSET_TABLE ,而我将代码通过memcpy转移到了其他的物理内存地址处,进而导致计算出了错误的函数入口地址。

太好了, 是反汇编, 我们有救了!

 1ffff80000010c043 <user_level_function>:
 2ffff80000010c043:»»»55                   »»»push   %rbp
 3ffff80000010c044:»»»48 89 e5             »»»mov    %rsp,%rbp
 4ffff80000010c047:»»»41 57                »»»push   %r15
 5ffff80000010c049:»»»48 83 ec 08          »»»sub    $0x8,%rsp
 6ffff80000010c04d:»»»48 8d 0d f9 ff ff ff »»»lea    -0x7(%rip),%rcx   # rcx  = ffff80000010c04d
 7ffff80000010c054:»»»49 bb d3 36 00 00 00 »»»movabs $0x36d3,%r11      # r11  = _GLOBAL_OFFSET_TABLE_的差值
 8ffff80000010c05b:»»»00 00 00
 9ffff80000010c05e:»»»4c 01 d9             »»»add    %r11,%rcx #       # rcx  = ffff80000010f720
10ffff80000010c061:»»»48 b8 50 0e 00 00 00 »»»movabs $0xe50,%rax
11ffff80000010c068:»»»00 00 00
12ffff80000010c06b:»»»48 8d 14 01          »»»lea    (%rcx,%rax,1),%rdx
13ffff80000010c06f:»»»be 00 00 00 00       »»»mov    $0x0,%esi
14ffff80000010c074:»»»bf 00 ff 00 00       »»»mov    $0xff00,%edi
15ffff80000010c079:»»»49 89 cf             »»»mov    %rcx,%r15
16ffff80000010c07c:»»»b8 00 00 00 00       »»»mov    $0x0,%eax
17ffff80000010c081:»»»49 b8 5e 6b ff ff ff »»»movabs $0xffffffffffff6b5e,%r8 # r8  = ffffffffffff6b5e
18ffff80000010c088:»»»ff ff ff
19ffff80000010c08b:»»»49 01 c8             »»»add    %rcx,%r8 # r8  = 10627e printk入口地址
20ffff80000010c08e:»»»41 ff d0             »»»callq  *%r8
21ffff80000010c091:»»»eb fe                »»»jmp    ffff80000010c091 <user_level_function+0x4e>
22
23
24ffff80000010f720 <_GLOBAL_OFFSET_TABLE_>:
25
26ffff80000010627e <printk>:

解决方案为: 在Makefile文件的CFLAGS变量中添加-fno-pic即可。

添加后编译结果的反编译结果

 1ffff80000010aa2f <user_level_function>:
 2ffff80000010aa2f:»»»55                   »»»push   %rbp
 3ffff80000010aa30:»»»48 89 e5             »»»mov    %rsp,%rbp
 4ffff80000010aa33:»»»48 ba f8 eb 10 00 00 »»»movabs $0xffff80000010ebf8,%rdx
 5ffff80000010aa3a:»»»80 ff ff
 6ffff80000010aa3d:»»»be 00 00 00 00       »»»mov    $0x0,%esi
 7ffff80000010aa42:»»»bf 00 ff 00 00       »»»mov    $0xff00,%edi
 8ffff80000010aa47:»»»b8 00 00 00 00       »»»mov    $0x0,%eax
 9ffff80000010aa4c:»»»48 b9 09 5d 10 00 00 »»»movabs $0xffff800000105d09,%rcx
10ffff80000010aa53:»»»80 ff ff
11ffff80000010aa56:»»»ff d1                »»»callq  *%rcx
12ffff80000010aa58:»»»eb fe                »»»jmp    ffff80000010aa58 <user_level_function+0x29>
13
14ffff800000105d09 <printk>:

可以看到编译器不在通过计算得出函数入口,而是直接把入口地址放入寄存器。

除另有声明外本博客文章均采用 知识共享 (Creative Commons) 署名 4.0 国际许可协议 进行许可转载请注明原作者与文章出处