当前位置: 首页 > news >正文

Debian或Ubuntu静态交叉编译arm和aarch64

Debian或Ubuntu静态交叉编译arm和aarch64

  • 介绍
  • 术语
  • ARM架构
  • 前置条件
  • 从源代码编译一个简单的C程序
  • configure和make交叉编译
  • 关于静态链接和依赖关系
  • 使用 musl libc 实现与 configure 和 make 进行交叉编译

ARM 正在获得越来越多的关注,并且越来越受欢迎。直接在这些基于 ARM 的设备上构建并不总是可能的,特别是当它们的资源有限时。大多数构建和开发计算机仍然在 x86 上,通过使用交叉编译,可以构建可在其他体系结构上使用的二进制文件或可执行文件。例如,使用您的标准 PC(很可能是 x86)来构建可在其他架构(例如 ARM)上的另一台机器或设备上使用的东西。在这篇文章中,我将解释如何使用 Debian 10 或 Ubuntu 20.04 LTS 对 32 位 ARM (arm) 或 64 位 ARM (aarch64) 进行交叉编译。静态编译的好处是减少依赖,方便移植。

在这里插入图片描述

介绍

对我来说,交叉编译功能最常用于构建在基于 Linux 的设备上未安装或不可用的故障排除工具。例如,具有自定义 Linux 版本的 Raspberry Pi、NAS、路由器或接入点等设备,没有安装附加软件包的选项或安装附加软件包的选项有限。挑选一些设备随机运行一些Linux常用命令,输出如下:

/ # telnet
/bin/sh: telnet: not found
/ # tcpdump
/bin/sh: telnet: not found
/ # strace
/bin/sh: strace: not found
/ # curl
/bin/sh: curl: not found

对于下面的步骤,我将使用 Debian 10 (Buster),并且还将在 Ubuntu 20.04.1 (LTS) 上测试相同的步骤。所有步骤都经过验证可以在两者之间互换。两者的起点都是最小安装(标准系统实用程序 + SSH 服务器)。这可以确保,如果有人想要重复这些步骤,所有内容都是可重现的,并且不会跳过或遗漏任何已预安装的内容。

术语

在交叉编译中,需要使用以下令人困惑的术语:

  1. 构建平台:构建机器的架构
  2. 主机平台:您正在构建的架构
  3. 目标平台:将处理编译的二进制文件的体系结构

构建和主机或多或少是清晰的,但目标可能会令人困惑。简而言之,目标仅在使用开发工具(如编译器本身)时才相关。

在这里插入图片描述

当您为与正在使用的相同架构进行构建时,构建、主机和目标是相同的。这称为“本机”编译。如果构建和目标平台相同,但主机不同,那么我们正在讨论交叉编译,这是本文所讨论的。这三个平台都不同用于为另一种体系结构构建交叉编译器。

需要明确的是,在本文中,构建平台和目标平台是 x86_64(标准 PC),主机是 ARM 平台。我将介绍 32 位 ARM(armv6、armv7 或简称为 arm)和 64 位 ARM (aarch64)。

ARM架构

要找出需要编译哪一个(32 位或 64 位 ARM),最简单的方法是查看 uname -m 的输出。对于X86_64(标准PC),

jensd@deb10:~$ uname -m
x86_64

32位ARM,

[ap1:~]# uname -m
armv7l

64位ARM:

root@armv8:/ # uname -m
aarch64

前置条件

在开始编译之前,我们需要安装ARM交叉编译所需的软件包和工具。其中包括编译本机所需的标准工具。对于32位ARM(arm):

jensd@deb10:~$ sudo apt install gcc make gcc-arm-linux-gnueabi binutils-arm-linux-gnueabi
Reading package lists... Done
Building dependency tree
...
Processing triggers for man-db (2.8.5-2) ...
Processing triggers for libc-bin (2.28-10) ...

对于64位ARM(aarch64):

jensd@deb10:~$ sudo apt install gcc make gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu
Reading package lists... Done
...
Processing triggers for man-db (2.8.5-2) ...
Processing triggers for libc-bin (2.28-10) ...

当然,如果您计划针对这两种架构进行编译,则可以安装 32 位和 64 位所需的编译器。

从源代码编译一个简单的C程序

一旦我们安装了先决条件,我们就可以尝试编译一个简单的 C 程序。首先,我们对要进行编译的 PC 进行所谓的本机编译,以确保我们的程序能够执行我们想要的操作。将源代码保存为 helloworld.c:

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

本机(x86-64)编译源代码:

jensd@deb10:~$ vi helloworld.c
jensd@deb10:~$ gcc helloworld.c -o helloworld-x86_64

要查看编​​译结果的类型和平台,我们可以使用file工具检查输出:

jensd@deb10:~$ file helloworld.x86_64
helloworld.x86_64: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=f55972265cd343c3110a5d339d71caeed755c23e, not stripped

我们可以执行二进制文件来检查结果:

jensd@deb10:~$ ./helloworld-x86_64
Hello World!
jensd@deb10:~$

下一步是为 ARM 编译相同的源代码。我们只需使用不同的编译器(对于 32 位 ARM,使用 arm-linux-gnueabi-gcc 代替 gcc;对于 64 位 ARM 或 aarch64,使用 gcc-aarch64-linux-gnu)来完成此操作。32位ARM编译:

jensd@deb10:~$ arm-linux-gnueabi-gcc helloworld.c -o helloworld-arm -static
jensd@deb10:~$ file helloworld-arm
helloworld-arm: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=971f8b03dcb473de2caa1abbb63990977c6478b3, not stripped

64位ARM编译:

jensd@deb10:~$ aarch64-linux-gnu-gcc helloworld.c -o helloworld-aarch64 -static
jensd@deb10:~$ file helloworld-aarch64
helloworld-aarch64: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.7.0, BuildID[sha1]=428239fafd78d5fd28dc400e913618817224ea50, not stripped

file命令行输出给了我们一个不同的结果,这是我们所期望的。正如预期的那样,尝试在构建机器 (x86_64) 上执行这些二进制文件将导致错误:

jensd@deb10:~$ ./helloworld-aarch64
-bash: ./helloworld-aarch64: cannot execute binary file: Exec format error
jensd@deb10:~$ ./helloworld-arm
-bash: ./helloworld-arm: cannot execute binary file: Exec format error

为了测试这是否有效,我们需要一台运行我们构建的架构的机器或设备:

root@armv8:/$ uname -a
Linux armv8 4.4.214-armada-17.10.1 #1 SMP Fri Jul 31 23:23:54 UTC 2020 aarch64 aarch64 aarch64 GNU/Linux
root@armv8:/$ uname -m
aarch64
root@armv8:/$ wget http://192.168.1.1/helloworld-aarch64
...
helloworld-aarch64 100% |******************************| 557k 0:00:00 ETA
...
root@armv8:/ # chmod +x helloworld-aarch64
root@armv8:/ # ./helloworld-aarch64
Hello World!

上面的输出显示,我们的小程序在交叉编译后在 ARM 上运行得很好!

configure和make交叉编译

上面的例子非常简单,但是当从较大的项目编译源代码时,通常是通过使用configure生成一个makefile,然后使用make运行编译和其他必要的步骤来完成的。要将 gcc 替换为另一个特定于目标平台的编译器将需要大量工作。幸运的是,大多数时候您可以在运行配置时指定要编译的平台。

作为示例,我将为 strace 的 ARM aarch64 创建一个二进制文件。为了避免出现与嵌入式 ARM 设备的依赖关系问题,我将提供静态选项(有关更多说明,请参阅下文)。

第一步是从 https://github.com/strace/strace/releases/tag/v5.10 获取 strace 的源代码并提取它:

jensd@deb10:~$ wget https://github.com/strace/strace/releases/download/v5.10/strace-5.10.tar.xz
...
strace-5.10.tar.xz    100%[===============>]   1.77M  2.49MB/s    in 0.7s
2021-01-26 16:57:20 (2.49 MB/s) - ‘strace-5.10.tar.xz’ saved [1859688/1859688]
jensd@deb10:~$ tar -xf strace-5.10.tar.xz
jensd@deb10:~$ cd strace-5.10/
jensd@deb10:~/strace-5.10$

下一步是运行配置。但这里我们需要指定构建和主机平台,以便我们最终得到 ARM 的二进制文件(静态链接):

jensd@deb10:~/strace-5.10$ ./configure --build x86_64-pc-linux-gnu --host aarch64-linux-gnu LDFLAGS="-static -pthread" --enable-mpers=check
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane… yes
checking for aarch64-linux-gnu-strip... aarch64-linux-gnu-strip
...
config.status: creating strace.spec
config.status: creating debian/changelog
config.status: creating config.h
config.status: executing depfiles commands
jensd@deb10:~/strace-5.10$

此时,我们已准备好通过运行 make 来进行实际的交叉编译:

jensd@deb10:~/strace-5.10$ make
aarch64-linux-gnu-gcc -E -P -DHAVE_CONFIG_H   \
...
jensd@deb10:~/strace-5.10$ file strace
strace: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.7.0, BuildID[sha1]=db1ce4305df1dac73b81efe99847725d65ac9ab4, with debug_info, not stripped

正如您所看到的,我构建了这个用于 aarch64 的版本。如果您想对armv6或armv7执行相同的操作,只需在运行configure时将–host aarch64-linux-gnu替换为–host arm-linux-gnueabi即可。

为了使其正常工作,我传递给 ./configure 的标志和参数有一些额外的解释:

checking for library containing timer_create… no
configure: error: failed to find timer_create
was fixed by adding LDFLAGS=”-pthread”
checking for m32 personality compile support… no
checking whether to enable m32 personality support… no
configure: error: Cannot enable m32 personality support
was fixed by adding –enable-mpers=check

与小型 C 程序一样,是时候在 ARM 上测试编译后的二进制文件了:

root@armv8:/data $ uname -a
Linux armv8 4.4.214-armada-17.10.1 #1 SMP Fri Jul 31 23:23:54 UTC 2020 aarch64 aarch64 aarch64 GNU/Linux
root@armv8:/data $ uname -m
aarch64
root@armv8:/data $ wget http://192.168.1.1/strace 
...
strace               100%  |************************| 5989k  0:00:00 ETA
...
root@armv8:/data $ chmod +x strace
root@armv8:/data $ ./strace -V
strace -- version 5.10
Copyright (c) 1991-2020 The strace developers https://strace.io.
This is free software; see the source for copying conditions.  There is NOwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.Optional features enabled: no-m32-mpers

这使我们能够在随机的 aarch64 机器上简单地复制和使用 strace。

关于静态链接和依赖关系

正如我在一开始提到的,我主要使用交叉编译来构建故障排除工具。通常,您构建的平台是有限的。这可能是由于缺乏资源(例如嵌入式设备)造成的。但也因为预构建的软件包要么不可用,要么无法安装。在很多情况下,这也意味着为您正在构建的任何内容安装依赖项可能会出现问题。显然,这些依赖关系也必须针对同一架构构建。

如果您有这种限制,或者您只是希望二进制文件在该架构上运行。无需担心依赖项或可能已安装的这些依赖项的冲突(旧)版本,您可以使用静态链接。这意味着在构建时所有必需的依赖项都将包含在二进制文件本身中。

静态链接有一些缺点,因为它可能不安全(所包含的依赖项不会随系统更新),当执行较低级别系统调用的库和生成的二进制文件较大时,可能会导致不兼容。在构建故障排除工具时,我可以忍受这些事情,因为它们不适合长期使用。

在这里插入图片描述

虽然静态链接可能是您正在寻找的,但它并不总是那么容易实现。尤其是与交叉编译相结合,它会让你头疼。大多数工具都依赖于 libc 或 glibc,出于我在上一段中提到的充分理由,这不鼓励静态链接。幸运的是,有一个从头开始开发的 libc 实现,它允许对 libc 依赖项进行正确的静态链接:musl(发音为 musscle)。

使用 musl libc 实现与 configure 和 make 进行交叉编译

为了使用 musl,我们需要下载正确的版本进行交叉编译。您可以在这里找到完整列表:https://musl.cc/#binaries。

下载后,我们可以提取存档并测试这是否适用于我们的构建机器:

jensd@deb10:~$ wget https://musl.cc/aarch64-linux-musl-cross.tgz
...
aarch64-linux-musl-cross.tgz   100%[============>]   103.69M  6.81MB/s    in 16s
2021-01-27 15:19:55 (6.42 MB/s) - ‘aarch64-linux-musl-cross.tgz’ saved [108731156/108731156]
jensd@deb10:~$ tar -xvzf aarch64-linux-musl-cross.tgz
aarch64-linux-musl-cross/
aarch64-linux-musl-cross/usr
...
jensd@deb10:~$ ./aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc --version
aarch64-linux-musl-gcc (GCC) 10.2.1 20210116
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

接下来,我将为 aarch64 构建 TCPdump 的静态链接版本。当您需要记录、调查或排除网络连接故障时,它始终是一个方便使用的好工具。不幸的是,tcpdump 并不总是可用,或者在最好的情况下作为有限的 Busybox 版本。首先,我们需要安装一些必需的工具,这些工具由 tcpdump 用于构建过程:

jensd@deb10:~$ sudo apt install flex bison
Reading package lists... Done
Building dependency tree
...
Processing triggers for man-db (2.8.5-2) ...
Processing triggers for libc-bin (2.28-10) ...

接下来,我们需要下载 tcpdump 和 libpcap 的源代码并解压。您可以在这里找到最新版本:https://www.tcpdump.org/index.html#latest-releases

jensd@deb10:~$ wget https://www.tcpdump.org/release/libpcap-1.10.0.tar.gz
...
libpcap-1.10.0.tar.gz   100%[============>]   912.66K  1.51MB/s    in 0.6s
2021-01-27 15:20:22 (1.51 MB/s) - ‘libpcap-1.10.0.tar.gz’ saved [934559/934559]
jensd@deb10:~$ tar -xvzf libpcap-1.10.0.tar.gz
jensd@deb10:~$ wget https://www.tcpdump.org/release/tcpdump-4.99.0.tar.gz
...
tcpdump-4.99.0.tar.gz   100%[============>]   1.92M  2.57MB/s    in 0.7s
2021-01-27 15:21:36 (2.57 MB/s) - ‘tcpdump-4.99.0.tar.gz’ saved [2008080/2008080]
jensd@deb10:~$ tar -xvzf tcpdump-4.99.0.tar.gz

下载并解压源代码后,我们需要首先运行 libpcap 的配置脚本。只是这一次,我们需要将编译器设置为 musl-compiler 以进行交叉编译,方法是将 CC 设置为:aarch64-linux-musl-gcc:

jensd@deb10:~$ cd libpcap-1.10.0/
jensd@deb10:~/libpcap-1.10.0$ CC=/home/jensd/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc ./configure --build x86_64-pc-linux-gnu --host aarch64-linux-gnu LDFLAGS="-static"
checking build system type... x86_64-pc-linux-gnu
checking host system type... aarch64-unknown-linux-gnu
...
config.status: creating config.h
config.status: executing default-1 commands

如果一切顺利,我们可以通过发出 make 来实际编译 libpcap:

jensd@deb10:~/libpcap-1.10.0$ make
...
config.status: creating pcap-config.tmp
mv pcap-config.tmp pcap-config
chmod a+x pcap-config

现在,我们可以对 tcpdump 本身重复相同的操作(./configure 和 make)。通过进行静态链接,将包含 libpcap,结果是单个二进制 tcpdump:

jensd@deb10:~/libpcap-1.10.0$ cd ../tcpdump-4.99.0/
jensd@deb10:~/tcpdump-4.99.0$ CC=/home/jensd/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc ./configure --build x86_64-pc-linux-gnu --host aarch64-linux-gnu LDFLAGS="-static"
checking build system type... x86_64-pc-linux-gnu
checking host system type... aarch64-unknown-linux-gnu
checking for aarch64-linux-gnu-gcc... /home/jensd/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc
checking whether the C compiler works... yes
...
config.status: creating Makefile
config.status: creating tcpdump.1
config.status: creating config.h
config.status: executing default-1 commands
jensd@deb10:~/tcpdump-4.99.0$ make
...
aarch64-linux-gnu-ranlib libnetdissect.a/home/jensd/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc  -DHAVE_CONFIG_H   -I. -I../libpcap-1.10.0  -I/usr/inet6/include -g -O2 -static -o tcpdump fptype.o tcpdump.o  libnetdissect.a ../libpcap-1.10.0/libpcap.a
jensd@deb10:~/tcpdump-4.99.0$ file tcpdump
tcpdump: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, with debug_info, not stripped

正如您在最后一个命令中看到的,我们有静态链接的 tcpdump 二进制文件。如果一切顺利,我们应该能够在基于 aarch64 的机器上执行此操作,而无需任何进一步的依赖。

root@armv8:/$ uname -m
aarch64
root@armv8:/$ wget http://192.168.1.1/tcpdump
...
tcpdump   100%[============>]   6.73M  --.-KB/s    in 0.1s
2021-01-27 13:55:49 (52.5 MB/s) - 'tcpdump' saved [7061368/7061368]
root@armv8:/$ chmod +x tcpdump
root@armv8:/$ ./tcpdump -i eth0
tcpdump: verbose output suppressed, use -v[v]for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
13:57:20.452643 IP 192.168.0.90.34020 > 192.168.1.10.9000: Flags [S], seq 3386726277, win 64240, options [mss 1460,sackOK,TS val 3092573417 ecr 0,nop,wscale 7], length 0
13:57:20.452715 IP 192.168.1.10.9000 > 192.168.0.90.34020: Flags [S.], seq 3761157769, ack 3386726278, win 28960, options [mss 1460,sackOK,TS val 4397992 ecr 3092573417,nop,wscale 7], length 0
13:57:20.453325 IP 192.168.0.90.34020 > 192.168.1.10.9000: Flags [.], ack 1, win 502, options [nop,nop,TS val 3092573418 ecr 4397992], length 0
13:57:20.508762 IP 192.168.1.10.46819 > 8.8.8.8.53: 15310+ PTR? 90.0.168.192.in-addr.arpa. (43)^C
4 packets captured
23 packets received by filter
5 packets dropped by kernel
root@armv8:/$

正如您所看到的,tcpdump 工作正常并且不需要任何依赖项。如果您需要在工具集非常有限并且无法轻松安装它们的平台上工作或排除故障,这确实很有帮助。您只需复制该文件即可运行。

作者:岬淢箫声
日期:2023年10月31日
版本:1.0
链接:http://caowei.blog.csdn.net

相关文章:

  • miniconda快速安装
  • 我的云栖大会之旅:见证云计算创新的15年
  • 使用springboot对Elasticsearch 进行索引的增、删、改、查
  • 企业网络带宽使用情况检查技巧
  • Vite+Vue3项目全局引入scss文件
  • 【蓝桥杯选拔赛真题44】python小蓝晨跑 青少年组蓝桥杯python 选拔赛STEMA比赛真题解析
  • 从用户角度出发,如何优化大数据可视化体验|北京蓝蓝UI设计公司
  • [100天算法】-实现 strStr()(day 52)
  • Selenium学习(Java + Edge)
  • 软考之知识产品+例题
  • Mozilla Firefox 119 现已可供下载
  • 算法通关村第四关|黄金挑战|表达式问题
  • window压缩包安装mongodb并注册系统服务
  • AlarmManager闹钟管理者
  • JAVA 实现PDF转图片(pdfbox版)
  • Angular 2 DI - IoC DI - 1
  • CSS相对定位
  • Docker容器管理
  • go语言学习初探(一)
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • JWT究竟是什么呢?
  • PAT A1120
  • Redux系列x:源码分析
  • spring security oauth2 password授权模式
  • Webpack 4x 之路 ( 四 )
  • Yeoman_Bower_Grunt
  • 编写符合Python风格的对象
  • 对象管理器(defineProperty)学习笔记
  • 基于webpack 的 vue 多页架构
  • 判断客户端类型,Android,iOS,PC
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 如何优雅地使用 Sublime Text
  • 小程序 setData 学问多
  • 新手搭建网站的主要流程
  • LIGO、Virgo第三轮探测告捷,同时探测到一对黑洞合并产生的引力波事件 ...
  • Semaphore
  • ​中南建设2022年半年报“韧”字当头,经营性现金流持续为正​
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #LLM入门|Prompt#1.7_文本拓展_Expanding
  • (70min)字节暑假实习二面(已挂)
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (react踩过的坑)antd 如何同时获取一个select 的value和 label值
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (翻译)Quartz官方教程——第一课:Quartz入门
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (附源码)ssm经济信息门户网站 毕业设计 141634
  • (数据结构)顺序表的定义
  • (转)iOS字体
  • (转)shell调试方法
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .net 开发怎么实现前后端分离_前后端分离:分离式开发和一体式发布
  • .NET单元测试
  • .NET国产化改造探索(三)、银河麒麟安装.NET 8环境