本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10693247.html

本文以嵌入式 Linux 环境下的 gdb 使用为例,记录交叉编译过程中一个比较关键的问题:configure 过程中 --build, --host, --target 参数的区别。

1. 交叉编译

交叉编译是指在一种平台上编译出运行于另一种平台的程序。这里的平台,涉及硬件和软件两个部分,硬件平台指 CPU 架构,软件平台指操作系统。交叉编译主要针对嵌入式领域,因为嵌入式系统资源受限,没有办法在嵌入式平台上运行一套编译环境,因此需要在其他性能更强劲的平台上借助交叉编译工具链来制作可在嵌入式平台上运行的程序。

交叉编译与普通编译基本步骤一样:

[1] configure

在编译前进行配置。如果 --host 参数与 --build 参数不同,则是交叉编译。否则就是普通编译。

[2] make

编译。根据上一步 configure 配置生成的参数,调用相应的编译工具链编译生成目标程序。

[3] make install

安装。将 make 生成的目标程序安装到指定目录。如果不运行 make install,手动拷贝到指定目录也可。

1.1 --build --host --target

看一下 configure 步骤中 --build、--host 和 --target 三个参数的定义,下面在 gdb 源码目录运行 './configure --help'

./configure --help
System types:
--build=BUILD configure for building on BUILD [guessed]
--host=HOST cross-compile to build programs to run on HOST [BUILD]
--target=TARGET configure for building compilers for TARGET [HOST]

源码经过编译生成可执行程序。根据执行编译操作的平台、可执行程序的运行平台、可执行的程序的处理平台,可以将编译操作分为多种类型,对应的三个配置参数如下:

--build:运行编译工具链的平台,也就是正在执行编译操作的平台。如果未指定此参数,则通过 config.guess 猜测得到。通常都不指定此参数。

--host:可执行程序将运行的平台。如果未指定此参数,则和 --build 相同。如果 --host 和 --build 不同,是交叉编译;否则是普通编译。

--target:可执行程序将处理的平台。如果未指定此参数,则和 --host 相同。一般来说,程序运行在什么平台,处理的就是什么平台,此参数值和 --host 参数相同,不需显式指定,所以通常不会关注到此参数。但在制作交叉编译工具 (如 gcc、gdb 等) 这种特殊情况下,此值和 --host 不同,例如交叉编译器 arm-linux-gcc,它运行在 x86-linux 平台 (--host 参数),但处理的是 arm-linux 平台 (--target 参数)。如果是交叉编译一个普通的应用,如运行于 arm-linux 平台的 tftp 程序,则它的运行平台和处理平台都是 arm-linux 平台。

1.2 三种编译类型

编译类型可以分为三种,主要体现在 configure 阶段不同的配置参数:(假设交叉编译工具链安装在 x86-linux 平台,待编译的目标嵌入式平台为 arm-linux 平台,这里仅为了说明意图,平台命名并不严谨)

[1] 编译 PC 平台普通程序

一般执行如下形式的配置命令:

./configure

将缺省的参数补全,实际等效于如下命令:

./configure --build=x86_linux --host=x86_linux --target=x86_linux

[2] 编译交叉编译工具链

交叉编译工具链的特别之处在于,它在 x86-linux 平台上运行,处理的却是 arm-linux 平台。制作交叉编译工具链并不需要使用交叉编译。一般执行如下形式的配置命令:

./configure --target=arm_linux

将缺省的参数补全,实际等效于如下命令:

./configure --build=x86_linux --host=x86_linux --target=arm_linux

[3] 交叉编译嵌入式平台程序

生成嵌入式平台的程序需要用用到交叉编译。一般执行如下形式的配置命令:

./configure --host=arm_linux CC=arm-linux-gcc

将缺省的参数补全,实际等效于如下命令:

./configure --build=x86_linux --host=arm_linux --target=arm_linux CC=arm-linux-gcc

实际上,如果我们要制作运行于嵌入式平台的编译工具(比如 gcc、gdb 等),也可以先执行上面 configure 命令然后执行 make,但是生成的 gcc、gdb 等工具基本上无法在目标嵌入式平台上正常运行,因为硬件带不动,所以嵌入式平台的编译工具链是按 [2] 中的方法制作 ,而不是按 [3] 中的方法制作。

2. GDB 交叉编译与使用实例

嵌入式平台中使用 GDB 时会用到 GDB 的远程 (remote) 调试模式:在目标板上通过 gdbserver 运行待调试的程序,在宿主机上运行 gdb 并通过 'target remote [ip]:[port]' 来连接到目标板上的 gdbserver,从而启动远程调试。各种调试命令在宿主机上输入,程序执行效果(包括打印)在目标板上展示,这很容易理解,因为正在调试的程序本来就是在目标板上运行的。不在目标板上直接运行 gdb 进行调试是因为目标板硬件配置低,跑不动 gdb,当然,不排除某些嵌入式平台的性能很强劲,能够正常运行 gdb。当嵌入式平台的硬件越来越强大时,嵌入式平台与通用计算机平台的界限也越来越模糊,实际情况也正是这样,硬件性能越来越强悍,资源短缺的问题越来越淡化,这种发展形势下,嵌入式技术的没落是必然的结果。

搭建嵌入式平台下的 gdb 调试环境,对理解前面交叉编译过程中的 --build、--host 和 --target 三个参数非常用帮助。

2.1 环境描述

虚拟机:CPU:x86_64,系统:openSUSE Leap 42.3,IP:192.168.202.240

开发板:CPU:mips mt7688,系统:openwrt linux,IP:192.168.202.141

虚拟机上安装的 C 交叉编译器为 mipsel-openwrt-linux-gcc,即交叉编译工具链的前缀为 mipsel-openwrt-linux。

2.2 下载源码

ftp://ftp.gnu.org/gnu/gdb 下载最新版源码 gdb-8.2.tar.gz,解压:

tar zxvf ./gdb-8.2.tar.gz
cd gdb-8.2

2.3 编译 gdb

编译 gdb:

cd gdb-8.2
./configure --target=mipsel-openwrt-linux
make

gdb 运行在虚拟机上,所以它不需要交叉编译。--build 和 --host 参数留空,实际使用的是虚拟机的平台参数。gdb 虽运行在虚拟机上,但它处理的是开发板平台的程序,所以指定 --target 为 mipsel-openwrt-linux,值取的是交叉编译工具链前缀。

2.4 交叉编译 gdbserver

编译 gdbserver:

cd gdb-8.2/gdb/gdbserver
./configure --host=mipsel-openwrt-linux CC=mipsel-openwrt-linux-gcc
make

gdbserver 运行在开发板上,所以需要交叉编译。--build 参数留空,实际使用的是虚拟机的平台参数。--host 参数指定虚拟机平台,值为 mipsel-openwrt-linux。--target 参数留空,所以它的取值将等于 --host 参数值。

2.5 以 remote 方式使用 gdb

开发板:开发板 IP 是 192.168.202.141,则输入下述任一条指令皆可

gdbserver :1234 test
gdbserver 127.0.0.1:1234 test
gdbserver 192.168.202.141:1234 test

主机:首先在 SHELL 命令行里运行 gdb 应用

./gdb

运行上一条命令后,SHELL 将进入 gdb 模式,下列几条指令中 '(gdb)' 是提示符:

(gdb) target remote 192.168.202.141
(gdb) b main.c:55
(gdb) c

上面第一条命令是远程连接到开发板上的 gdbserver。连接之后,就是正常使用了。第二条命令是设置断点。第三条命令是运行程序,注意待调试的程序实际已在开发板上运行了,所以要使用 'c' 指令,而不能使用 'r' 指令,如果输入 'r' 指令,可以看到提示 remote 模式不支持 'r' 指令:

(gdb) r
The "remote" target does not support "run". Try "help target" or "continue".

3. 遗留问题

交叉编译过程中,一般使用交叉编译工具链的前缀作 --host 及 --target 的值;--build 参数一般不指定,编译时自动推测。这些都是在实践层面,一般就算写错了,多试一试最终都能编译通过。而对平台描述的定义,即 --build、--host 及 --target 的值的具体取值规则,我没有找到正式的出处和定义,是的,网上没查到。

4. 参考资料

嵌入式 Linux 的 GDB 调试环境建立

Python Exception 异常信息

QT 远程调试 ARM 板中 python 的问题

5. 修改记录

2019-04-11 初稿

交叉编译问题记录-嵌入式环境下 GDB 的使用方法的更多相关文章

  1. Oracle 11g RAC环境下Private IP修改方法及异常处理

    Oracle 11g RAC环境下Private IP修改方法及异常处理 Oracle 11g RAC环境下Private IP修改方法及异常处理 一. 修改方法 1. 确认所有节点CRS服务以启动 ...

  2. 分布式环境下的id生成方法

    分布式环境下的id生成方法   前几天研究数据库分表分库的问题,其中有一个关键的地方就是生成唯一键的问题,假如数据表有1亿条数据,而且还在不断的增加,这里我们就需要考虑到分表分库,假设我们采用Hash ...

  3. CDN 环境下获取用户IP方法

    CDN 环境下获取用户IP方法 1 cdn 自定义header头的X-Real-IP,在后端使用$http_x_real_ip获得 proxy_set_header X-Real-IP $remote ...

  4. PHP环境下Memcache的使用方法

    原文:PHP环境下Memcache的使用方法 原文地址:http://www.2cto.com/kf/201503/384967.html 如今互联网崛起的时代,各大网站都面临着一个大数据流问题,怎么 ...

  5. 转载:一种云环境下SaaS软件部署方法及装置与流程

    转载:http://www.xjishu.com/zhuanli/55/201710103925.html 本发明涉及云计算技术领域,特别是涉及一种云环境下SaaS软件部署方法及装置. 背景技术: 随 ...

  6. 针对 Linux 环境下 gdb 动态调试获取的局部变量地址与直接运行程序时不一致问题的解决方案

    基础的缓冲区溢出实践通常需要确定运行状态下程序中的某些局部变量的地址,如需要确定输入缓冲区的起始地址从而获得注入缓冲区中的机器指令的起始地址等.在 Linux 环境下,可通过 gdb 对程序进行动态调 ...

  7. 分布式环境下Unique ID生成方法

    ID即标示符,在某个搜索域内能唯一标示其中某个对象.在关系型数据库中每个表都需要定义一个主键来唯一标示一条记录.为了方便一般都会使用一个auto_increment属性的整形数做为ID.因为数据库本身 ...

  8. std::string在多字节字符集环境下substr的实现方法

    昨天写到<使用多字节字符集的跨平台(PC.Android.IOS.WP)编码/解码方法>中提到服务端使用std::string处理字符串,std::string对多字节字符集支持并不是很完 ...

  9. Visual Studio2008环境下查找C#中方法的“查看所有引用”

    在Visual Studio开发环境下,想必F12我们都很熟悉了,有没有用过“查看所有引用”呢? 尤其是在一个解决方案中,包含了很多项目,彼此相互的调用是很常见的,例如三层架构, BLL调用DAL,D ...

随机推荐

  1. Oracle中为表设置自动增长的标识列

    -- Create sequence create sequence INNERID minvalue 1 maxvalue 99999999999999 start with 1 increment ...

  2. 更改h标签的字体粗细

    h1,h2,h3,h4,h5,h6{ font-weight:normal }

  3. javascript(作业九)

    1.javascript简介: JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端 ...

  4. SimpleCursorAdapter和ListView的结合使用

    我们在用SQLite查数据的时候,经常会用到Cursor这个游标,我们希望能将游标指向的数据直接绑定到ListView中,这样就免去了将游标数据取出然后转换到SimpleAdapter中的麻烦.今天我 ...

  5. 端口转发工具lcx使用两类

    lcx是一款强大的内网端口转发工具,用于将内网主机开放的内部端口映射到外网主机(有公网IP)任意端口.它是一款命令行工具,当然也可以在有权限的webshell下执行,正因如此lcx常被认为是一款黑客入 ...

  6. 18.数组(一)之认识java数组

    数组是一个简单的复合数据类型,它是一组有序数据的集合,它当中的每一个数据都具有相同的数据类型,我们通过数组名再加上一个不会越界的下标值来唯一确定数组中的元素. 还有就是,数组是一个特殊的对象. 不管在 ...

  7. 迁移桌面程序到MS Store(5)——.NET Standard

    接下来的几篇,我想讨论下迁移桌面程序到MS Store,可以采用的比较常见.通用性比较强的实施步骤和分层架构. 通常商业项目一般都是不断的迭代,不太可能突然停止更新现有的桌面版本,然后花很长时间从头来 ...

  8. Linux pwn入门教程(1)——栈溢出基础

    作者:Tangerine@SAINTSEC 原文来自:https://bbs.ichunqiu.com/thread-42241-1-1.html 0×00 函数的进入与返回 要想理解栈溢出,首先必须 ...

  9. LinkedList 的源码分析

    LinkedList是基于双向链表数据结构来存储数据的,以下是对LinkedList  的 属性,构造器 ,add(E e),remove(index),get(Index),set(inde,e)进 ...

  10. Swift5 语言指南(十六) 初始化

    初始化是准备要使用的类,结构或枚举的实例的过程.此过程涉及为该实例上的每个存储属性设置初始值,并执行在新实例准备好使用之前所需的任何其他设置或初始化. 您可以通过定义实现这个初始化过程初始化,这就像特 ...