一个debug了4个多小时的bug, 操作系统真象还原: 试图运行起第6章的print相关函数却没有预期效果

与其说是分享, 不如说是祭奠.

debug中我犯了好些错误, 浪费了大量时间, 直接说结论, 看链接得到的readelf的结果:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x08048000 0x08048000 0x00110 0x00110 R   0x1000
  LOAD           0x000500 0xc0001500 0xc0001500 0x0012b 0x0012b R E 0x1000
  LOAD           0x001000 0xc0002000 0xc0002000 0x00064 0x00064 R   0x1000
  LOAD           0x002000 0xc0004000 0xc0004000 0x0000c 0x0000c RW  0x1000
  NOTE           0x0000f4 0x080480f4 0x080480f4 0x0001c 0x0001c R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10

有几个段的确是>=0xc0000000的(-Ttext只负责把代码段移到指示的位置), 但第一个段虚拟地址是0x08048000, 但是页表中没有这个地址的页, bochs中用info tab查看也没有这个地址的映射. 于是就出错了.

于是我在loader.S的266行下面插入了这样一段代码, 手动创建了页表. 0x08048000对应的高20位是00001 00000 00010 01000.

add eax, 0x100000 ; 先指向二级页表开始的位置(此前eax是设置了权限位的0x100000, base1)
mov [PAGE_DIR_TABLE_POS + 128], eax ; 128是肯定的
sub eax, 0x100000 ; 为了保证不改变eax
mov edx, PG_US_U | PG_RW_W | PG_P
add edx, 0x6f000
mov dword [PAGE_DIR_TABLE_POS + 0x100000+72*4], edx;

解释一下, 创建了第32项一级页表, 一级页表指向的二级页表的地址是第一个可用的内存, 也就是PAGE_DIR_TABLE_POS + 0x100000, 因为二级页表已经分配了1+254=255个, 于是256*0x1000=0x100000.
只分配了一页, 是0x6f000, 注意必须是2^12的倍数, 也就是最后12位一定得是0(16进制就是3个0).

现在看bochs, 就有这一项:

0x08048000-0x08048fff -> 0x00000006f000-0x00000006ffff

运行就可以了.

其实我还有疑惑: 为什么作者没有遇到这个问题? 难道说, 作者链接后得到的elf文件, 没有虚拟地址为0x08048000的段? 可我为什么会有这个段呢?

debug了这么久, 我做错了哪些事:

  1. 代码的定位. 开始花了不小的力气去确定是哪一行出了错, 其实出错的地方, 在出错的时候就显示出来了.
  2. 花了很大的力气来确定为什么这行代码执行会出错. 我有2个特别不小心的地方, 第一个是没仔细考虑如何取栈中的参数, 误认为mem_cpy代码写错了. 第二个是读readelf的结果时, 误把Section Headers当作了Program Headers, 更加让我. 那么如何避免这样的错误呢? 当做一个与书的不同的结论时, 需要非常慎重. 需要看清楚自己看的是什么.
上一篇:vue 高级技巧之 scoped 下的全局样式


下一篇:深刻理解linux内核调用栈、栈帧结构