关于visual-studio-code:首发UbuntuVSCode搭建Linux-Kernel单步调试IDE环境

4次阅读

共计 6890 个字符,预计需要花费 18 分钟才能阅读完成。

编者荐语:

举荐应用 vscode 来单步调试奔跑吧内核,在按 F5 单步之前,请先设置断点,例如在 start_kernel 函数设置断点,间接在源代码左侧点击左键即可。

以下文章来源于弹指码通,作者弹指神通

继上一篇介绍纯 Win10 下 VSCode 搭建 Linux Kernel 单步调试 IDE 环境

本篇持续介绍在纯 Ubuntu 下用 VSCode 搭建 Linux Kernel 单步调试 IDE 环境

0. 环境介绍
0.0 主机版本
主机:Ubuntu(其余任意 Linux 发行版、虚拟机、真机均可)

版本:18.04.4

编译器:aarch64-linux-gnu-gcc (gcc version 5.5.0)

调试器:主机:aarch64-linux-gnu-gdb (gcc version 5.5.0)

0.1 VS Code 版本
版本: 1.46.1
提交: cd9ea6488829f560dc949a8b2fb789f3cdc05f5d
日期: 2020-06-17T21:13:08.304Z
Electron: 7.3.1
Chrome: 78.0.3904.130
Node.js: 12.8.1
V8: 7.8.279.23-electron.0
OS: Linux x64 5.3.0-28-generic
1. 筹备工作
1.1、装置 Ubuntu
下载安装 Ubuntu,如本例中提到的:ubuntu18.04

1.2、装置 VSCode
Ubuntu 版的 VSCode:下载地址

1.3、装置 VSCode 插件
在扩大外面增加即可

C/C++ (必选)

C/C++ Intellisense(可选)

C/C++ Snippets(可选)

Remote Development(必选三件套,微软官网出品)

Remote-WSL

Remote-SSH

Remote-Containers

设施树插件

Embedded Linux Dev

Kconfig(设施树插件依赖)

1.4、下载 Linux 内核代码
举荐下载:

git clone https://e.coding.net/benshush… -b rlk_basic
举荐理由:

1、4.0 版本十分经典,适宜学习

2、该代码仓库是书籍的配套代码,比较完善

3、该代码仓库曾经配置好了各种比拟烦杂的环境,如:qemu 网络桥接、根文件系统、qemu 共享文件夹等

4、最初强调:后期配套开发环境,不须要反复造轮子,纠结于小细节,先站在伟人肩上,整套流程相熟之后,能够随时替换、批改这套环境

1.5、搭建 Linux 内核编译环境
Ubuntu18.04 相干问题可间接百度查找

Linux 环境:ubuntu18.04

Linux 装置依赖包:

sudo apt-get install qemu libncurses5-dev libssl-dev build-essential openssl bison bc flex git
当然你能够应用如下命令来装置编译内核须要的所有依赖包。

sudo apt build-dep linux-image-generic
Linux 环境装置编译链:

因为 linux 内核版本起因,因为所用版本为 4.0,所以须要 5.x 的 gcc 穿插链

一次性装置 ARM32/64 所用的穿插链

sudo apt install gcc-5-aarch64-linux-gnu gcc-5-arm-linux-gnueabihf
如果零碎中曾经有其余版本的 gcc 穿插链,可应用 update-alternatives 进行治理, 能够参考:

update-alternatives 命令的主要参数如下
update-alternatives –install <link> <name> <path> <priority>
link:指向 /etc/alternatives/<name> 的符号援用
name:链接的名称
path:这个命令对应的可执行文件的理论门路
priority:优先级,在 auto 模式下,数字大的优先级比拟高。
2.Ubuntu 下 VSCode 搭建 IDE
到这里咱们曾经实现了 Ubuntu、VScode 及其插件的装置,接下来能够应用 VSCode 进行编译、调试

2.1、应用 VSCode 关上内核源码

2.2、开始编译内核
其实该源码目录曾经集成好编译、运行、调试所须要的脚本

该源码曾经反对 ARM32+debian 或 ARM64+debian,本例认为 ARM64+Debian 为例

在 VSCode 中按下 Ctrl+~ 即可调出零碎终端,在终端中运行:

./run_debian_arm64.sh build_kernel

编译实现:

2.3、编译 Rootfs
编译 ARM64 版本的 Debian 零碎 rootfs

$sudo./run_debian_arm64.sh build_rootfs

留神:这里须要应用 root 权限。

编译实现后会生成一个 rootfs_debian_arm64.ext4 的文件系统。

2.4、运行 Rootfs
$./run_debian_arm64.sh run

留神:运行此命令不须要 root 权限。

留神:用户名:root 明码:123

胜利运行之后,如下图所示:

胜利登录之后,如下图所示:

2.5、测试 Debian 零碎
因为是基于 Debian 零碎,且网络等都是曾经搭建好的,间接能够应用 APT 等命令进行装置在线包,以下为简略测试:

QEMU 虚拟机能够通过 VirtIO-NET 技术来生成一个虚构的网卡,并且通过 NAT 网 络桥接技术和主机进行网络共享。首先应用 ifconfig 命令来查看网络配置。能够看到生成了一个名为 eth0 的网卡设施,调配的 IP 地址为:10.0.2.15。通过 apt update 命令来更新 Debian 零碎的软件仓库。


2.6、主机和 QEMU 虚拟机之间共享文件
主机和 QEMU 虚拟机能够通过 NET9P 技术进行文件共享,这个须要 QEMU 虚 拟机的 Linux 内核使能 NET9P 的内核模块。本平台曾经反对主机和 QEMU 虚拟机的共享文件,能够通过如下简略办法来测试。

共享目录为:kmodules

系统目录为:mnt

成果如下图所示:

在 kmodules 目录上面新建一个 test.c 文件。

咱们在后续会常常利用这个个性,比方把编译好的内核模块或者内核模 块源代码放入 QEMU 虚拟机。

  1. 一键单步调试内核
    直到这一步,曾经实现了基于 WSL+VScode 的环境搭建,这样就能够失去一个集终端、文件管理器、git 管理器、运行调试等等等一体化的 IDE 环境了,这样就能够在纯 WIN10 下实现单步调试内核的目标了,十分不便。

3.1、配置内核调试命令
内核的编译调试命令曾经全副打包进了脚本文件,感兴趣的童鞋能够去深刻理解一下,这里以 ARM64 为例:

./run_debian_arm64.sh run debug

此时,gdbserver 曾经在 1234 端口期待连贯!

3.2、配置 VSCode Debug 选项
抉择:运行(R)-> 增加配置 -> C++(GDB/LDB)

如下动图所示:

增加如下配置信息:

{

// 应用 IntelliSense 理解相干属性。// 悬停以查看现有属性的形容。// 欲了解更多信息,请拜访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
    {"name": "(gdb) 启动",
        "type": "cppdbg",
        "request": "launch",
        "program": "${workspaceFolder}/vmlinux",
        "args": [],
        "stopAtEntry": true,
        "cwd": "${workspaceFolder}",
        "environment": [], 
        "externalConsole": true,// 调试时是否显示控制台窗口,个别设置为 true 显示控制台
        "MIMode": "gdb",
        "miDebuggerPath":"/usr/local/bin/aarch64-linux-gnu-gdb",
        "miDebuggerServerAddress": "localhost:1234",
        "setupCommands": [
            {
                "description": "为 gdb 启用参差打印",
                "text": "-enable-pretty-printing",
                "ignoreFailures": true
            }
        ]
    }
]

}
留神: localhost 是本地的 IP 也就是运行 qemu 的 ubuntu 的 IP,哪个主机运行 ./run_debian_arm64.sh run debug 就取该主机的 IP,因为此时是同一台主机,所以就填 localhost

gdb 门路:”miDebuggerPath”:”/usr/local/bin/aarch64-linux-gnu-gdb”

gdb 监听端口:”miDebuggerServerAddress”: “localhost”,1234 与上一大节中的 Listen 端口统一!

3.3、一键调试
[笨叔] 在按 F5 键之前,请先设置断点,例如在 start_kernel 函数里设置断点,间接关上 init/main.c 文件在 start_kernel 函数左侧点击鼠标左键,即能够设置断点。

通过 3.1、3.2 的配置曾经实现了调试的前置条件,现只需按下 F5 键就能够实现一键调试了,如下图所示:

3.4、更多 GDB 调试技巧
在终端界面栏,切换至调试控制台; 输出命令,如:-execinfo registers,即可查看调试过中的的寄存器:

4. 单步调试应用层 + 内核
通过后面 0~3 的铺垫,咱们曾经具备了以下三个条件:

1、残缺的内核(编译环境、调试环)+ 残缺的 Rootfs 2、残缺的 qemu 环境, 包含:网络共享、桥接等,能够随时将主机的文件共享给 qemu(本地的 kmodules 文件夹《—》虚拟机外面的 mnt 文件夹)3、残缺的 GDB 调试环境,能够实现内核单步调试

那么,针对以上条件,如果咱们想要调试或者参考一个 Linux 应用程序如何拜访到内核的,是否能够实现呢?答案是能够的!请看上面!

4.1 创立简略的 APP 程序
在 kmodules 文件夹外面新建一个 test.c 内容如下:

include <stdio.h>

include <stdlib.h>

include <unistd.h>

include <sys/types.h>

include <sys/stat.h>

include <fcntl.h>

include <errno.h>

unsigned char readbuf[255];
int count = 0;
int main(void)
{

int fd ;
int retval;
printf("hello world!\n");
fd = open("./README",O_RDONLY);
if (fd == -1)
{perror( "open dht11 error\n") ;
    exit(-1) ;
}
printf("open ./README\n") ;
sleep(2) ;
while(1)
{sleep( 1) ;
    if(count++ == 0)
    {printf("count=%d\n",count);
    }
}
close(fd) ;
return 0;

}

4.2 编译 & 调试
输出编译命令:

aarch64-inux-gnu-gcc test.c-o test

即可在 kmodules 文件夹上面失去一个新的 test 应用程序。

接着,按第三节做法进入可单步调试内核的环境。

整体演示成果如下图所示,只是简略的演示,实现从应用层到内核层的调用过程,更深的利用能够持续挖掘。

5. 单步调试 modules+ 内核
5.1、简略测试代码筹备
在主机 kmodules 文件夹下新建一个简略的内核模块程序 hello_drv.c 及对应的 Makefile 文件

内核模块程序 hello_drv.c 内容示例:

/*

  • 1 include files
  • 2 __init module_init() insmod
  • 3 __exit module_exit() rmmod
  • 4 GPL BSD Aeplli GPLv2 MIT
  • 5 module_license(GPL)
    */

    include <linux/init.h>

    include <linux/module.h>

    include <linux/fs.h>

    include <linux/device.h>

include <asm/uaccess.h>

include <asm/io.h>

include <linux/device.h>

struct class *hello_class;
struct device * hello_dev;

int hello_open(struct inode inode, struct file flips)
{
printk(“————–%s————–\n”,__FUNCTION__);
return 0;
}

static ssize_t hello_write(struct file file, const char __user in,

     size_t size, loff_t *off)

{
printk(“————–%s————–\n”,__FUNCTION__);
unsigned int buf = 88;
copy_from_user(&buf, in ,size);
printk(“write buf is : %d\n”,buf);

}

static ssize_t hello_read(struct file file, char __user buf,

    size_t nbytes, loff_t *ppos)

{

printk(“————–%s————–\n”,__FUNCTION__);
unsigned int a = 100;
copy_to_user(buf,&a,sizeof(int));

}

static int my_major = 0;
const struct file_operations myfops={

.open = hello_open,
.write= hello_write,
.read = hello_read,
};

static int __init hello_init(void)
{
printk(“————–%s————–\n”,__FUNCTION__);//app printf

my_major = register_chrdev(0,”hello”,&myfops);
if(my_major <0)
{

printk("reg error!\n");

}
else

printk("my_major =%d",my_major);

hello_class = class_create(THIS_MODULE,”hello_class”);//creat hello_class
hello_dev = device_create(hello_class, NULL,MKDEV(my_major,0), NULL, \

          "hello_dev");//creat hello_dev--->>/dev/hello_dev

return 0;
}

static void __exit hello_exit(void)
{
printk(“————–%s————–\n”,__FUNCTION__);

device_destroy(hello_class,MKDEV(my_major,0));

class_destroy(hello_class);

unregister_chrdev(my_major,”hello”);
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE(“GPL”);

内核模块程序 Makefile 内容示例:

ifeq ($(KERNELRELEASE),)

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

KERNELDIR=/home/jackeyt/runninglinuxkernel_4.0-rlk_basic #your kernel dirction
NFS_DIR=$(KERNELDIR)/kmodules

CUR_DIR := $(shell pwd)

all :
make -C $(KERNELDIR) M=$(CUR_DIR) modules

install:
cp -ranf *.ko $(NFS_DIR)/

clean :
make -C $(KERNELDIR) M=$(CUR_DIR) clean

.PHONY: modules install clean

else
obj-m := hello_drv.o
endif

编译

留神,这里的编译应该是在主机环境下,即 Ubuntu 的命令行中:

make

5.2、开始调试
咱们晓得当应用 insmod 时,会调用对应的 __init 接口,而在本例中,hello_init 就为入口函数,因而简略测试一下,在内核中找到 register_chrdev 对应的接口定义,并打好断点,待 insmod 执行之后,察看内核的运行过程。


在内核中打上相应断点:

演示成果动图:

正文完
 0