• 使用GDB+QEMU调试Cosmos内核代码


    1. 生成带调试符号的elf文件

    修改编译选项:

    1. GCC 的-O2参数要修改成O0 -g参数:-O0是告诉 GCC 编译器,在编译时不要对代码做优化,这么做可以避免在 GDB 调试时源码和实际程序对应不上的问题;-g参数是为了告诉编译器带上调试符号。

      使用grep命令查找需要修改的文件,使用sed命令批量替换,命令如下:

      grep -i '\-o2' -r  //-i 代表不区分大小写
      
      • 1

      在这里插入图片描述

         sed -i 's/-O2/-O0 -g/' ./initldr/build/krnlbuidcmd.mh ./script/krnlbuidcmd.S
         sed -i 's/-Os/-O0 -g/' ./initldr/build/krnlbuidcmd.mh ./script/krnlbuidcmd.S 
      
      • 1
      • 2

      在这里插入图片描述

    2. 去掉 ld 的-s参数:-s是告诉 ld 程序链接时去掉所有符号信息,其中包括了调试符号。

      grep '\-s ' -r
      
      • 1

      在这里插入图片描述

      使用 sed 命令批量去掉 ld 的-s参数,命令如下:

       sed -i 's/-s / /g' ./initldr/build/krnlbuidcmd.mh ./script/krnlbuidcmd.S
      
      • 1

    编译生成“带调试符号的 elf 文件"

    执行make就可以编译出带有调试符号的 elf 文件,如下图:这里的“not stripped”就表示文件带有调试符号。

    1. Cosmos.elf:当需要调试“内核代码”时,可以在 GDB 中执行symbol-file ./build/Cosmos.elf加载调试符号
    2. initldrkrl.elf:当需要调试“二级加载器代码”时,可以在 GDB 中执行symbol-file ./initldr/build/initldrkrl.elf加载调试符号。

    制作 hd.img, 用于QEMU运行Cosmos内核

    1. 打包生成内核映像文件Cosmos.eki
      将要打包的文件copy到同一个文件夹下,执行下列命令:

      ./lmoskrlimg -m k -lhf initldrimh.bin -o Cosmos.eki -f initldrkrl.bin initldrsve.bin Cosmos.bin background.bmp font.fnt logo.bmp 
      
      • 1

      在这里插入图片描述

    2. 如果已有 hd.img,则需要先将其挂载到临时文件,然后替换新生成的Cosmos.eki

          cd mount hd.img /tmp/
          cp Cosmos.eki /tmp/boot/
          umount /tmp/
      
      • 1
      • 2
      • 3
    3. 第一次生成hd.img需要按如下命令制作

      使用dd命令生成100MB的纯二进制文件,也就是我们要用到的虚拟硬盘文件hd.img:

      dd bs=512 if=/dev/zero of=hd.img count=204800
      
      ;bs:表示块大小,这里是512字节 
      ;if:表示输入文件,/dev/zero就是Linux下专门返回0数据的设备文件,读取它就返回0 ;
      of:表示输出文件,即我们的硬盘文件。 ;
      count:表示输出多少块
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      格式化虚拟硬盘并挂载到本地目录下:

      sudo losetup /dev/loop0 hd.img // losetup命令将hd.img变成Linux的回环设备
      sudo mkfs.ext4 -q /dev/loop0  // mkfs.ext4格式化为ext4格式的文件系统
      sudo mount -o loop ./hd.img ./hdisk/   //将hd.img当做块设备挂载到硬盘文件中
      sudo mkdir ./hdisk/boot/   //建立boot目录
      
      • 1
      • 2
      • 3
      • 4

      如果提示回环设备忙,则使用losetup -f查找第一个空闲的设备,替换即可:
      在这里插入图片描述

      安装GRUB:

      sudo grub-install --boot-directory=./hdisk/boot/ --force --allow-floppy /dev/loop0 
      
      • 1

      这时看到boot目录下多了一个grub目录,说明grub安装成功。

      创建grub.cfg文件:

      set timeout=2
      
      menuentry 'Cosmos' {
      insmod part_msdos
      insmod ext2
      set root='hd0' #我们的硬盘只有一个分区所以是'hd0'
      multiboot2 /boot/Cosmos.eki #加载boot目录下的Cosmos.eki文件
      boot #引导启动
      }
      set timeout_style=menu
      if [ "${timeout}" = 0 ]; then
        set timeout=10 #等待10秒钟自动启动
      fi
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14

      将Cosmos.eki文件放置到boot目录下,解除挂载

      cp Cosmos.eki /tmp/boot/
      umount /hdisk/
      
      • 1
      • 2
    4. 如果要在virtualbox中启动,则需要转换成hd.vdi格式,命令如下:

      VBoxManage convertfromraw ./hd.img --format VDI ./hd.vdi
      
      • 1

    2. qemu启动内核

    qemu-system-x86_64 -drive format=raw,file=hd.img -m 512M -cpu kvm64,smep,smap -s    // 一定要加-s参数,此参数可以打开调试服务。
    
    • 1

    3. 使用GDB加载调试符号

    在这里插入图片描述

    (gdb) symbol-file ./initldr/build/initldrkrl.elf  // 加载调试符号,这样才能在显示源码、可以用函数名下断点
    Reading symbols from ./initldr/build/initldrkrl.elf...  //连接qemu-system-x86_64 -s选项打开的1234端口进行调试
    
    • 1
    • 2

    4. 启动调试

     (gdb) target remote :1234   // 连接qemu-system-x86_64 -s选项打开的1234端口进行调试  
     Remote debugging using :1234  
    0x0000000000200040 in _32bits_mode ()
    
    • 1
    • 2
    • 3

    5. 设置断点,使用GDB命令调试

    在这里插入图片描述

      root# od -tx4 ./initldr/build/Cosmos.eki | head -3
      0000000 909066eb 1badb002 00010003 e4514ffb
      0000020 04000004 04000000 00000000 00000000
      0000040 04000068 90909090 e85250d6 00000000
    
    • 1
    • 2
    • 3
    • 4

    根据GRUB 头结构,结合上面的 Cosmos.eki 文件头信息,我们很容易就能知道,_start符号地址是0x04000000,_entry符号地址是0x04000068。我们在这两个地址设置断点,通过 GDB 可以看到,程序不是在0x04000000断点暂停,而是直接在0x04000068 断点暂停,说明grub启动后会加载cosmos.eki 到0x04000000位置,但执行的第一条指令不是 _start 符号位置而是 _entry 符号位置。到 _entry 时,cr0 的 pe=1,表明此时保护模式已经打开了。

    而在断点处执行的正是 _entry处的代码: ./initldr/ldrkrl/imginithead.asm
    在这里插入图片描述

    参考手册:

    100个gdb小技巧

    QEMU User Documentation

  • 相关阅读:
    SpringBoot框架学习(三)——热部署,整合Redis
    功能测试如何编写测试用例
    vscode基于cmake结果调试运行
    nginx学习(1)
    电压检测芯片如何发挥作用,保护电路?纳米软件为您介绍工作原理
    pom文件引用本地对象
    servlet介绍
    JUC03-volatile、CAS及并发原子类
    网络工程师知识点4
    1024程序员节主题征文 | 程序员节节日祝福语大全
  • 原文地址:https://blog.csdn.net/jingr1/article/details/126213381