VSCode+GDB+Qemu调试ARM64 linux内核

俗话说,工欲善其事 必先利其器。linux kernel是一个非常复杂的系统,初学者会很难入门。

如果有一个方便的调试环境,学习效率至少能有5-10倍的提升。

为了学习linux内核,通常有这两个需要

  1. 可以摆脱硬件,方便的编译和运行linux
  2. 可以使用图形化的工具来调试linux

笔者使用VSCode+GDB+Qemu完成了这两个需求

qemu作为虚拟机,用来启动linux。

VSCode+GDB作为调试工具,用来图形化地DEBUG。

最终效果大致如下:

qemu运行界面:

vscode调试界面:

下面将一步一步介绍如何搭建上述环境。

本文所有操作都在Vmware Ubuntu16虚拟机上进行。

安装编译工具链

由于Ubuntu是X86架构,为了编译arm64的文件,需要安装交叉编译工具链

sudo apt-get install gcc-aarch64-linux-gnu
sudo apt-get install libncurses5-dev  build-essential git bison flex libssl-dev

制作根文件系统

linux的启动需要配合根文件系统,这里我们利用busybox来制作一个简单的根文件系统

编译busybox

wget  https://busybox.net/downloads/busybox-1.33.1.tar.bz2
tar -xjf busybox-1.33.1.tar.bz2
cd busybox-1.33.1

打开静态库编译选项

make menuconfig
Settings --->[*] Build static binary (no shared libs) 

指定编译工具

export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-

编译

make
make install

编译完成,在busybox目录下生成_install目录

定制文件系统

为了init进程能正常启动, 需要再额外进行一些配置

根目录添加etc、dev和lib目录

# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install [1:02:17]
$ mkdir etc dev lib
# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install [1:02:17]
$ ls
bin  dev  etc  lib  linuxrc  sbin  usr

在etc分别创建文件:

# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:13]
$ cat profile
#!/bin/sh
export HOSTNAME=bryant
export USER=root
export HOME=/home
export PS1="[$USER@$HOSTNAME \W]\# "
PATH=/bin:/sbin:/usr/bin:/usr/sbin
LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
export PATH LD_LIBRARY_PATH# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:16]
$ cat inittab
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:19]
$ cat fstab
#device  mount-point    type     options   dump   fsck order
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
debugfs /sys/kernel/debug debugfs defaults 0 0
kmod_mount /mnt 9p trans=virtio 0 0# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:26]
$ ls init.d
rcS# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:30]
$ cat init.d/rcS
mkdir -p /sys
mkdir -p /tmp
mkdir -p /proc
mkdir -p /mnt
/bin/mount -a
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

这里对这几个文件做一点说明:

  1. busybox 作为linuxrc启动后, 会读取/etc/profile, 这里面设置了一些环境变量和shell的属性
  2. 根据/etc/fstab提供的挂载信息, 进行文件系统的挂载
  3. busybox 会从 /etc/inittab中读取sysinit并执行, 这里sysinit指向了/etc/init.d/rcS
  4. /etc/init.d/rcS 中 ,mdev -s 这条命令很重要, 它会扫描/sys目录,查找字符设备和块设备,并在/dev下mknod

dev目录:

# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/dev [1:17:36]
$ sudo mknod console c 5 1

这一步很重要, 没有console这个文件, 用户态的输出没法打印到串口上

lib目录:拷贝lib库,支持动态编译的应用程序运行

# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/lib [1:18:43]
$ cp /usr/aarch64-linux-gnu/lib/*.so*  -a .

编译内核

配置内核

linux内核源码可以在github上直接下载。

根据arch/arm64/configs/defconfig 文件生成.config

make defconfig ARCH=arm64

将下面的配置加入.config文件中

CONFIG_DEBUG_INFO=y 
CONFIG_INITRAMFS_SOURCE="./root"
CONFIG_INITRAMFS_ROOT_UID=0
CONFIG_INITRAMFS_ROOT_GID=0

CONFIG_DEBUG_INFO是为了方便调试

CONFIG_INITRAMFS_SOURCE是指定kernel ramdisk的位置,这样指定之后ramdisk会直接被编译到kernel 镜像中。

我们将之前制作好的根文件系统cp到root目录下:

# bryant @ ubuntu in ~/Downloads/linux-arm64 on git:main x [1:26:56]
$ cp -r ../busybox-1.33.1/_install root

执行编译

make ARCH=arm64 Image -j8  CROSS_COMPILE=aarch64-linux-gnu-

这里指定target为Image 会只编译kernel, 不会编译modules, 这样会增加编译速度

启动qemu

下载qemu

需要注意的,qemu最好源码编译, 用apt-get直接安装的qemu可能版本过低,导致无法启动arm64内核。笔者是使用4.2.1版本的qemu

apt-get install build-essential zlib1g-dev pkg-config libglib2.0-dev binutils-dev libboost-all-dev autoconf libtool libssl-dev libpixman-1-dev libpython-dev python-pip python-capstone virtualenv
wget https://download.qemu.org/qemu-4.2.1.tar.xz
tar xvJf qemu-4.2.1.tar.xz
cd qemu-4.2.1
./configure --target-list=x86_64-softmmu,x86_64-linux-user,arm-softmmu,arm-linux-user,aarch64-softmmu,aarch64-linux-user --enable-kvm
make 
sudo make install

编译完成之后,qemu在 /usr/local/bin目录下

$ /usr/local/bin/qemu-system-aarch64 --version
QEMU emulator version 4.2.1
Copyright (c) 2003-2019 Fabrice Bellard and the QEMU Project developers

启动linux内核

/usr/local/bin/qemu-system-aarch64 -m 512M -smp 4 -cpu cortex-a57 -machine virt -kernel arch/arm64/boot/Image -append "rdinit=/linuxrc nokaslr console=ttyAMA0 loglevel=8" -nographic -s

这里对于参数做一些解释:

-m 512M 内存为512M

-smp 4 4核

-cpu cortex-a57cpu 为cortex-a57

-kernel kernel镜像文件

-append传给kernel 的cmdline参数。其中rdinit指定了init进程;nokaslr 禁止内核起始地址随机化,这个很重要, 否则GDB调试可能有问题;console=ttyAMA0指定了串口,没有这一步就看不到linux的输出;

-nographic禁止图形输出

-s监听gdb端口, gdb程序可以通过1234这个端口连上来。

这里说明一下console=ttyAMA0是怎么生效的。

查看linux源码可知ttyAMA0对应的是AMBA_PL011这个驱动:

config SERIAL_AMBA_PL011_CONSOLEbool "Support for console on AMBA serial port"depends on SERIAL_AMBA_PL011=yselect SERIAL_CORE_CONSOLEselect SERIAL_EARLYCONhelpSay Y here if you wish to use an AMBA PrimeCell UART as the systemconsole (the system console is the device which receives all kernelmessages and warnings and which allows logins in single user mode).Even if you say Y here, the currently visible framebuffer console(/dev/tty0) will still be used as the system console by default, butyou can alter that using a kernel command line option such as"console=ttyAMA0". (Try "man bootparam" or see the documentation ofyour boot loader (lilo or loadlin) about how to pass options to thekernel at boot time.)

AMBA_PL011是arm的一个标准串口设备, qemu 的输出就是模拟的这个串口。

在qemu的源码文件中,也可以看到PL011的相关文件:

# bryant @ ubuntu in ~/Downloads/qemu-4.2.1 [1:46:54]
$ find . -name "*pl011*"
./hw/char/pl011.c

成功启动Linux后, 串口打印如下:

[    3.401567] usbcore: registered new interface driver usbhid
[    3.404445] usbhid: USB HID core driver
[    3.425030] NET: Registered protocol family 17
[    3.429743] 9pnet: Installing 9P2000 support
[    3.435439] Key type dns_resolver registered
[    3.440299] registered taskstats version 1
[    3.443685] Loading compiled-in X.509 certificates
[    3.461041] input: gpio-keys as /devices/platform/gpio-keys/input/input0
[    3.473163] ALSA device list:
[    3.474432]   No soundcards found.
[    3.485283] uart-pl011 9000000.pl011: no DMA platform data
[    3.541376] Freeing unused kernel memory: 10752K
[    3.545897] Run /linuxrc as init process
[    3.548390]   with arguments:
[    3.550279]     /linuxrc
[    3.551073]     nokaslr
[    3.552216]   with environment:
[    3.554396]     HOME=/
[    3.555898]     TERM=linux
[    3.985835] 9pnet_virtio: no channels available for device kmod_mount
mount: mounting kmod_mount on /mnt failed: No such file or directory
/etc/init.d/rcS: line 8: can't create /proc/sys/kernel/hotplug: nonexistent directoryPlease press Enter to activate this console.
[root@bryant ]#
[root@bryant ]#

VSCode+GDB

vscode中集成了GDB功能,我们可以用它来图形化的调试linux kernel

首先我们添加vscode的gdb配置文件(.vscode/launch.json):

{// Use IntelliSense to learn about possible attributes.// Hover to view descriptions of existing attributes.// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387"version": "0.2.0","configurations": [{"name": "kernel debug","type": "cppdbg","request": "launch","program": "${workspaceFolder}/vmlinux","cwd": "${workspaceFolder}","MIMode": "gdb","miDebuggerPath":"/usr/bin/gdb-multiarch","miDebuggerServerAddress": "localhost:1234"}]
}

这里对几个重点参数做一些说明:

program: 调试的符号文件

miDebuggerPath:gdb的路径, 这里需要注意的是,由于我们是arm64内核,因此需要用gdb-multiarch来进行调试

miDebuggerServerAddress:对端地址,qemu会默认使用1234这个端口

配置完成之后,可以直接启动GDB, 连接上linux kernel

在vscode中,可以设置断点,进行单步调试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/672.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL数据库】MySQL 高级SQL 语句二

MySQL 高级 SQL 语句二 一、连接查询1.1 inner join(内连接)1.2 left join (左连接)1.3 right join(右连接) 二、CREATE VIEW ---- 视图三、 UNION - - 连集3.1 UNION3.2 UNION ALL 四、交集值(取两个SQL语句结果的交集…

pwn学习day4——ret2text

文章目录 原理&#xff1a;例子x64x32 原理&#xff1a; 覆盖函数返回地址到程序内存在的gadgets获取shell 例子 #include<stdio.h> #include<stdlib.h>void hint(){system("$0"); } int main(){char buf[0x10];printf("input: ");scanf(&q…

【Java】Java核心 78:Git 教程(1)Git 概述

文章目录 01.GIT概述目标内容小结 02.GIT相关概念目标内容小结 01.GIT概述 Git是一个分布式版本控制系统&#xff0c;常用于协同开发和版本管理的工具。它可以跟踪文件的修改、记录历史版本&#xff0c;并支持多人协同工作。通过Git&#xff0c;你可以轻松地创建和切换分支、合…

【算法题】动态规划中级阶段之跳跃游戏、最大子数组和、解码方法

动态规划中级阶段 前言一、最大子数组和1.1、思路1.2、代码实现 二、跳跃游戏2.1、思路2.2、代码实现 三、解码方法3.1、思路3.2、代码实现 总结 前言 动态规划&#xff08;Dynamic Programming&#xff0c;简称 DP&#xff09;是一种解决多阶段决策过程最优化问题的方法。它是…

【Software Testing】【期末习题库】【2023年春】【仅供参考】

文章目录 ①单选②多选③填空④判断⑤大题 类型总分占比平时成绩40%考试/考查60% 题型题量分值备注单选201’多选103’全对3’&#xff0c;错1个0’&#xff0c;少选-1’填空102’判断52’大题210’ 平时习题&#xff08;3次&#xff09;&#xff1a; ①软件测试概述 ②黑盒测…

Java微服务金融项目智牛股-基础知识一(CAT链路监控)

CAT链路监控 背景&#xff1a;从单体架构到微服务架构的演变&#xff0c; 一个业务请求往往会流转多个服务&#xff0c; 大型互联网产品服务架构尤为复杂&#xff0c;腾讯的抢红包服务&#xff0c; 阿里的交易支付服务&#xff0c; 可能就流转成百上千个服务节点&#xff0c; 面…

SAP从入门到放弃系列之可用性检查-订单物料可用性检查-Part1

文章目录导航 概述&#xff1a;系统配置&#xff1a;物料可用性检查维护检查规则维护物料下达维护组件检查类型 数据测试&#xff1a;系统配置准备主数据准备测试结果 概述&#xff1a; 在生产订单下达时保证有足够的组件可以用于生产业务&#xff0c;保证生产业务能够按计划执…

createdTime(new Date()) 数据库时间比实际多八小时

本来是createdTime&#xff08;new Date&#xff08;&#xff09;&#xff09;&#xff0c;一次生成两条数据 一直正常&#xff0c;今天却多八小时 一开始往new Date&#xff08;&#xff09;差八个小时的问题上找&#xff0c; 网上说要 在apprication.yml文件中配置一下数据…

考研算法31天:归并排序 【归并排序,分治】

算法介绍 归并算法其过程分为三步&#xff1a; 1.分&#xff1a;递归到最下面 2.治&#xff1a;两个元素之间排序。 3。归&#xff1a;递归到最下层然后返回&#xff0c;从两个元素变成四个元素再排序。 如下图所示&#xff1a; 动态图如下&#xff1a; 算法题目 算法ac代…

【设计模式】模板方法与策略模式的结合使用

文章目录 1. 概述1.1.简述模板方法 2.模板方法实现2.1.简单实现2.2.在SpringBoot中的实现 3.模板方法与策略模式的结合使用3.1.代码实现 4.总结 1. 概述 模板方法是一种非常简单的设计模式&#xff0c;只要能够理解面向对象中的继承与多态就能够理解这种设计模式&#xff0c;我…

Spark中python和jvm的通信杂谈--ArrowConverter

背景 要提起ArrowConverters&#xff0c;就得说起Arrow这个项目&#xff0c;该项目的初衷是加速进程间的数据交换&#xff0c;从目前的社区发展以及它的周边来看&#xff0c;其实是一个很不错的项目。 那为什么Spark要引入Arrow呢&#xff1f;其实还得从Pyspark中python和jvm的…

购物车业务

一、分析购物车vo &#xff08;1&#xff09;添加成功页 public class CartItemVo implements Serializable {/*** 商品id*/private Long skuId;/*** 是否选中*/private Boolean check true;/*** 商品标题*/private String title;/*** 商品图片*/private String image;/***…