[Linux]从零开始的vs code交叉调试arm Linux程序教程

发布于:2025-04-04 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、前言

        最近的项目中需要集成rknn的视觉识别,在这之前我并且没有将rknn集成到自己项目的经验。这里我需要在rknn原本demo的基础上我还需要集成自己的业务代码。但是又有一个问题,原本rknn我们都是使用交叉编译编译到开发板上的,并且我们还要再demo的基础上进行修改,这么多代码如果不能调试的话,那实现功能还是有一点困难了。后面找到了一种交叉调试的方法,总的来说就是,通过在X86的主机上交叉变异ARM的程序,然后将编译出来的二进制文件发送到开发板中,然后通过网络在X86的主机上交叉调试这个程序。通过网络交叉调试也是目前最主流的调试方式。那么本次教程,就来教大家如何使用vs code交叉调试arm linux中的程序,如果你准备好了,就让我们开始吧!

二、谁适合本次教程

        本次教程已经涉及到了Linux C应用开发部分了,所以并不适用于完全没有基础的小白,还请看本次教程的小伙伴具备一定的Linux基础和C语言基础。需要大家了解基本的Linux操作,理解交叉编译的概念。这里我会默认大家有基础,很多细节我并不会说或者是一笔带过。现在让我们开始吧!

三、交叉编译器的安装

        这里我们需要在X86的设备上编译ARM的程序就需要使用到交叉编译器,这里我们说的交叉指的就是架构交叉。所以第一步就需要我们安装一个交叉编译器。因为我们是编译ARM Linux的程序,所以这里也必须要在X86的Linux系统中进行编译,这也是为了保证链接的库都是Linux的,不会出现怪异的问题。

这里X86一侧的Linux系统,我们可以选择直接将Linux系统装到我们的电脑中,具体可以看下面的教程:

在物理机中安装Ubuntu:[Linux]如何在物理机安装Ubuntu(小白向)-CSDN博客

这里我们也可以选择将Linux安装到虚拟机中,我在教程中也会使用这种方式,具体可以看下下面的教程:

在虚拟机中安装Ubuntu:[Linux]如何在虚拟机安装Ubuntu?(小白向)_虚拟机 ubuntu-CSDN博客

我在教程中使用的Linux发行版为Ubuntu22.04,在虚拟机中安装Ubuntu的教程在上面给出的教程中已经讲得很详细了,大家直接参考即可。当然,这里也建议大家和我使用同样版本的系统,避免出现奇怪的问题。这里我就默认大家已经安装好了Ubuntu,如下图所示:

这里我们首先需要在终端中使用下面的命令更新一下软件包:

sudo apt update

这里我们安装两个软件包,分别是“openssh-server”和“net-tools”。安装这两个是为了方便我们查看虚拟机的IP地址以及方便我们进行SSH,相信各位Linux大神对此都不陌生,这里就不细说了,直接使用下面的命令安装即可:

sudo apt install openssh-server net-tools

这里安装完成以后,我们就可以通过“ifconfig”命令查看虚拟机的IP地址:

这里大家记住自己虚拟机的IP地址,后面要考的。

现在我们可以来下载一下交叉编译器的压缩包,这里我们直接使用下面的命令拉取即可。这里需要注意,我提供了两种ARM架构的交叉编译器,大家需要根据自己开发板芯片的架构进行选择,这里我们在开发板的终端中输入下面的命令就可以看到开发板的架构了:

uname -m

这里可以看到,我的开发板架构为“aarch64”也就是我们常说的arm64架构:

所以我这里就需要aarch64的交叉编译器。这里非常简单,就不多说了:

aarch64交叉编译器下载:

wget https://releases.linaro.org/components/toolchain/binaries/6.3-2017.05/aarch64-linux-gnu/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu.tar.xz

armhf交叉编译器下载:

wget  https://developer.arm.com/-/media/Files/downloads/gnu-a/8.3-2019.03/binrel/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf.tar.xz?revision=e09a1c45-0ed3-4a8e-b06b-db3978fd8d56&rev=e09a1c450ed34a8eb06bdb3978fd8d56&hash=9C4F2E8255CB4D87EABF5769A2E65733
   - armhf-uclibcgnueabihf(RV1103/RV1106): https://console.zbox.filez.com/l/H1fV9a (fetch code: rknn)

因为我的开发板是64位的所以这里就选择了“aarch64”的交叉编译器,我们直接在终端中输入命令并且回车即可,随后就会进入下载:

如果你在这里被提示wget命令找不到之类的,可以使用下面的命令安装一下wget:

sudo apt install wget

当我们下载完以后,就会在当前目录看到一个交叉编译器的压缩包:

这里我们直接使用下面的命令对压缩包进行解压,这里我解压的是aarch64的压缩包,所以命令这样写,如果你的交叉编译器是armhf请自己修改一下命令:

tar -xvf gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu.tar.xz

回车以后,解压就开始了:

解压以后,我们就得到了一个交叉编译器的文件夹:

进入这个文件夹,我们就可以看到以下文件夹了:

这里我们主要用到的是交叉编译器文件夹下的bin文件夹,如下图所示:

进入“bin”文件夹中就可以看到非常多的可执行文件了:

这里看着非常多,其实我们就只会用到几个可执行文件。下面为大家依次介绍一下。

aarch64-linux-gnu-gcc :主要用于交叉编译C语言

aarch64-linux-gnu-g++:主要用于编译C++

gdbserver:主要用于在ARM侧启动交叉调试服务

aarch64-linux-gnu-gdb :主要用于连接“gdbserver”进行交叉调试

其实我们主要用到的可执行文件就上面几个,两个用于编译,两个用于调试。这里我们需要注意的是“gdbserver”是放在ARM一侧运行的,所以,我们可以看到它的架构是ARM的:

当然,我们现在也用不到这个,所以放在后面再说。

现在,我们得到了这些可执行文件还不算将交叉编译器安装好了,我们还需要将这个可执行文件目录添加到环境变量中,这一步很重要,这也是为了后面我们编译和调试方便。我们直接在bin目录下输入下面的命令来查看一下当前目录的绝对路径:

pwd

这里需要大家记住这里打印出来的路径,下面修改配置文件会用。

这里我们使用下面的命令打开用户环境变量的配置文件:

nano ~/.bashrc

如果你在这里被提示nano找不到,可以使用下面的命令来安装:

sudo apt isntall nano

我们打开用户环境变量的配置文件以后如图所示:

这里我们来到用户环境变量的最下方,添加下面的一行:

export PATH=/home/chulingxiao/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin:$PATH

这里大家需要注意,我们PATH后面跟的是我们一开始使用pwd命令查询到绝对路径。不要写错了,如果写错了环境变量是没办法正常加载的。

修改完以后,如图所示:

修改完成以后,我们保存退出即可,然后使用下面的命令重新加载环境变量:

source ~/.bashrc

在我们确保环境变量没有问题并且正确加载以后,就可以在终端中输入下面的命令来查看一下我们的环境变量是否添加成功:

aarch64-linux-gnu-gcc --version

这行命令会输出交叉编译器的版本信息:

如果你这里被提示命令找不到之类的,首先检查环境变量的路径有没有设置对,再检配置文件有没有被正确加载。

当然这里如果打印出了交叉编译器的版本号说明环境变量添加正确并且被正确加载。

到这里我们的编译环境就配置完成了。

当然,这里我们只配置编译环境还不行,我们还需要配置调试环境,毕竟本篇文章就是讲交叉调试的。这里我们在终端中输入下面的命令,看看能不能启动gdb:

aarch64-linux-gnu-gdb

这里可以看到,我们这里是启动不起来的,因为我们缺少了一个名为“libncurses”的库:

既然缺这个库我们就需要自己手动安装,这里大家注意在则会个库的“.so”的后面跟了一个数字,这就是依赖这个库的版本,总的来说我们缺少了一个名为“libncurses5”的库,这里我们直接使用下面的命令安装:

sudo apt install libncurses5

安装完成以后,我们再次启动gdb,发现又缺少了一个名为“libpython2.7”的库:

因为这里库名中就跟了一个大致的版本号,所以就不用加后面的版本号了,直接使用下面的命令安装即可:

sudo apt install libpython2.7

安装完成以后,我们再次启动gdb,这里可以看到我们的gdb已经正常启动了:

在gdb的对话框中,我们输入“q”并且回车就可以退出了:

前面为大家演示那些报错就是为了为大家展示,如果在启动gdb报错时应该如何解决,结合上面的思路,我们缺少什么库就安装什么库。最终都可以解决依赖关系。

至此,我们交叉编译环境就已经安装完成了。

四、开发板一侧gdbserver配置

        之前也为大家说过,我们交叉调试本质其实是依赖网络进行的,这里我们需要在开发板一侧启动gdbserver我们的X86主机才能对其进行交叉调试,所以现在我们就需要将gdbserver这个可执行程序上传到开发板中,之前我们也看了,在交叉编译器中的这个gdbserver的架构是arm64的,所以它可以直接运行在开发板上。这里需要注意的是,我们使用的交叉编译器和gdbserver必须来自一个交叉编译器的压缩包中,不然可能会出现很奇怪的情况。

这里我使用的是sftp将X86主机的gdbserver上传到开发板中,如果你对Linux比较熟悉,sftp你因该比较熟悉,这里就不细说了。当然你也可以不使用sftp总之能将文件上传到指定目录就行:

这里可以看到我将gdbserver这个可执行文件上传到了开发板的“/usr/bin”目录下,因为这个目录本来就在环境变量中,我们的可执行文件可以直接被找到。当文件传输完成以后,我们直接在开发板的终端中输入下面的命令,注意这里是在开发板的终端中:

gdbserver --version

如果可以正常打印版本信息的话,说明在开发板一侧,gdbserver已经准备好了:

至此,我们开发板一侧的环境就配置完成了。

五、交叉编译程序与gdb测试

        当我们搭建好前面的环境以后,我们就可以编译一段代码来测试一下了,首先我们新建一个文件夹,这里我就新建一个名为test的文件夹,直接使用下面的命令:

mkdir test

进入这个文件夹以后,我们使用下面的命令来新建一个.c的文件:

touch main.c

这里我们使用下面的命令打开这个文件:

nano main.c

我们将下面的测试代码复制到这个.c文件中:

#include "stdio.h"
int main()
{
   printf("Hello World\n");
   printf("Hello GCC\n");
   printf("Hello GDB\n");
   return 0;
}

这里的代码很简单,是C语言中最基础的“Hello World”程序,我们后面就使用这个基础的测试程序来测试gdb调试。

这里我们保存并退出文件,这里我们使用下面的命令来将这个.c的文件编译成可执行文件:

aarch64-linux-gnu-gcc -o main main.c -g -O0

下面我来解释一下这段命令,首先就是“aarch64-linux-gnu-gcc”,这就是我们使用的交叉编译器,然后是“-o main”表示输出的可执行文件的名字,这里我们可执行文件名为“main”;然后是“main.c”,这是我们要编译的c文件。“-g”表示将这个文件编译为Debug版本,这也是为了方便我们后面的调试,“-O0”表示我们编译时的优化等级,注意这里的“-”后面是一个“O”再后面是0,这里的0就是编译优化等级,0表示不优化。

编译以后,我们就得到了一个名为“main”的可执行文件:

这个被我们编译出来的可执行文件在我们的计算机上肯定是不能运行的:

因为这个一个arm64架构的可执行文件,我们只能将其放到arm设备中运行。

这里我们同样将这个可执行文件通过sftp传输到开发板端:

这里我们在开发板端执行这个可执行文件测试一下,我们可以看到,这个可执行文件可以正常打印:

下面我们可以调试测试一下,这里我们首先在开发板启动调试服务器,这里我们使用下面的命令:

gdbserver :8000 ./main

这里的“:8000”表示对外开放的调试端口。后面的“./main”是我们要调试的可执行文件,启动gdbserver以后,我们回到计算机一侧,我们使用下面的命令先启动一下gdb:

aarch64-linux-gnu-gdb ./main

这里的“./main”是我们要调试的程序,输入命令以后就启动了gdb的对话框:

这里我们在gdb的对话框中,使用下面的命令连接开发板:

target remote 192.168.112.50:8000

这里的IP地址就是我们的开发板的IP地址,这里需要我们开发板的IP地址和我们查询到的主机的IP地址在一个网段内,这里很重要。

输入命令以后,我们就可以看到调试已经连接上了:

这里我们在gdb对话框中输入下面的命令进入主函数:

break main  

这里我们可以看到来到的地址和这段代码在文件中的行数:

本质上我们使用“break main”是在主函数处打了一个断点,程序是停在了这里我们这里使用下下面的命令来到让程序继续运行:

continue 

运行上面的命令以后,我们就能看到我们程序继续运行,并且显示调试到了程序中了哪一行:

这里我们在gdb对话框中使用下面的命令调试到下一行:

next 

往下调试完一步以后,我们就可以发现,在开发板一侧的终端中打印出了一行内容:

打印出的这一行就是我们调试过去的一行:

这里我们将所以行都调试过去:

开发板端已经打印出了所有内容:

这里我们的调试就已经完成了。这里至少也证明了我们的gdb调试功能没有问题。

当然,大家肯定已经发现了,这样交叉调试真的是太反人类了,没有图形化不说还都是用命令操作。有没有更好的解决方案呢?当然有,那就是借助vs code进行调试,这个我们放到后面再讲。

六、使用vs code交叉调试Linux程序

        在前面相信大家已经看到了,我们使用命令行的gdb交叉调试时,太困难了,所以我们这里就需要借助vs code的强大插件生态来实现图形化的交叉调试。

这里我们首先需要下载一个vscode,这里我们直接使用ubuntu内部的浏览器来下载vscode的安装包,这里我们启动浏览器,直接搜索vscode:

 一般我们搜出来的第一个就是vscode的官网:

进入官网以后,直接西在deb文件就好了:

下载完成以后,我们得到了一个.deb的软件包:

这里我们直接使用下面的目录安装vscode:

sudo dpkg -i code_1.98.2-1741788907_amd64.deb

在弹出的弹窗中,我们直接回车即可:

随后我们的vscode就安装完成了:

随后vscode就出现在我们的软件列表中了:

vscode启动以后,就能看到以下界面了:

这里我们先安装一个中文的插件:

这里中文插件的安装大家应该都比较熟悉了,这里就不细说了。

重启以后,我们的vscode就变成中文了:

这里我们再安装一个C语言相关的插件,其中的调试功能也集成在其中了,我们直接搜索“C/C++”:

这里我们需要安装这个名为“C/C++ Runner”的插件。安装完成以后,如图所示:

至此,我们所需的交叉调试插件就安装好了,是的,关于调试的插件其实就一个。

这里我们选择“打开文件夹”:

这里我们直接选择我们之前创建的文件夹即可,这个文件夹中包含了我们的可执行文件和源文件:

我们就在这个界面按下“F5”,不出意外的话,会出现这个错误:

这是因为我们没有修改相关的配置文件,这里我们再次回到项目目录中,当我们按下“F5”以后,应该会在我们的项目目录下生成一个名为“.vscode”的文件夹:

这个文件夹下生成了几个配置文件,我们这里只需要关注“launch.json”文件,这就是我们的调试配置文件:

打开这个文件,我们可以看到以下内容:

如果你没有正常生成“.vscode”这个目录,可以自己新建目录和“launch.json”文件。

我i们将“launch.json”文件原本的内容删除,将下面的内容写入文件:

{
  "version": "0.2.0",
  "configurations": [
      {
          "name": "gdb server",//配置名称;在启动配置下拉菜单中显示
          "type": "cppdbg",//配置类型。
          "request": "launch",//请求配置类型。可以是“启动”或“附加”。
          "program": "${workspaceFolder}/main",//需要调试的可执行文件
          "args": [],//传递给程序的命令行参数。
          "stopAtEntry": true,//可选参数。如果为true,则调试器应在目标的入口点停止。如果传递了进程ID,则无效。
          "cwd": "${workspaceFolder}",//目标的工作目录
          "environment": [],//要添加到程序环境中的环境变量。示例:[“name”:“squid”,“value”:“clam”]。
          "externalConsole": false,//如果为true,则为调试对象启动控制台。如果为false,则在Linux和Windows上,它将出现在集成控制台中。
          "linux": {
              "MIMode": "gdb",//指示midebugengine将连接到的控制台调试器。允许值为“gdb”“lldb”。
              "miDebuggerPath": "aarch64-linux-gnu-gdb",//调试器的路径。
          },
          "miDebuggerServerAddress": "192.168.112.50:8000",//要连接到的调试器服务器的网络地址(例如:localhost:1234)。
          "setupCommands": [//要执行的一个或多个gdb/lldb命令,以便设置底层调试器。
              {
          
                  "description": "Enable pretty-printing for gdb",//命令的可选说明。
                  "text": "-enable-pretty-printing",//要执行的调试器命令。
                  "ignoreFailures": true,//如果为true,则应忽略来自命令的失败。默认值为假。                
              }
          ]
      }
  ]
}

这里看起来非常复杂,这里我们只需要改几行就行了,下面依次为大家讲解。

首先就是"program": "${workspaceFolder}/main"这一行,这里我们需要填写我们要填写可执行文件的路径,首先大家需要理解workspaceFolder的概念,当我们使用vscode打开一个文件夹以后,workspaceFolder变量就变成了这个文件夹路径,假如我项目文件夹的路径为“/home/chulingxiao/test”这里workspaceFolder路径就是这个。这里我们可执行文件路径就在项目的目录下,并且名为main,所以这里完整路径为“/home/chulingxiao/test/main”。不管怎样,这里大家只要把可执行文件路径写在这里就行。

然后是"miDebuggerPath": "aarch64-linux-gnu-gdb"这里需要我们填写自己的gdb调试器,因为我们已经前面已经将gdb调试器所在的目录添加到环境变量了,所以这里这里直接写gdb调试器的名字即可。如果前面大家没有添加,可以直接写绝对路径。

然后就是 "miDebuggerServerAddress": "192.168.112.50:8000",这里的IP地址大家直接填写要交叉调试的设备的IP地址,端口号就是我们在开发板一侧启动gdbserver时指定的端口。

这里我们只需要修改这几个地方,大家需要根据自己的情况自行修改。

修改好调试的配置文件后,大家记得保存,然后我们在开发板一侧启动gdbserver:

随后我们在vscode中进入调试界面:

 这里我们需要选择“gdbserver”:

然后我们再回到vscode中再按“F5”,就可以看到调试已经启动了:

这里的调试就和我们在普通IDE中的调试非常像了,这里我们按如图所示按键可以去到下一条代码:

点击如图所示的按键可以进入函数:

这里在代码前面也可以打断点。

这就是大概的使用教程,具体的还是让大家自己摸索吧,至少现在我们可以使用图形化进行交叉调试了。

至此,我们使用vscode交叉Linux程序就已经完成了。

七、结语

        本次教程教了大家如何使用vscode交叉ARM Linux程序,相信大家看了本次教程以后,对C语言的编译与调试一定有更深刻认识了。那么最后,感谢大家观看!