哦对的对的,哎呀不对不对,对…对吗?
并非全力
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 国际许可协议 进行许可。转载请注明原作者与文章出处。