Contents

如何在64位Linux系统上编译32位二进制文件

1. 简介

如今,在 64 位系统上执行 32 位程序非常方便实用。也就是说,有几种情况我们需要这样做。例如,应用程序的 64 位版本可能不可用或尚未移植。同样,我们可能需要使用 Windows 应用程序或使用 Wine 兼容层玩旧游戏。换言之,某些应用程序需要 32 位操作系统支持。

在本文中,我们将讨论如何使用gcc编译器在 64 位 Linux 系统上编译 32 位程序。我们还将了解在 64 位系统上构建 32 位二进制文件的系统要求,以及如何使用 CMake 自动化这些构建。

2. 开发工具

在开始之前,我们将确保编译二进制构建过程中使用的所有开发工具都安装在我们的系统中。首先,我们需要 GCC ,即 GNU 编译器集合。其次,binutils 是一组用于创建和管理二进制程序的工具,包括链接器和汇编器。此外,我们必须安装Make(构建自动化工具)和 C 标准库 glibc

最重要的是,**我们必须安装Multilib,它允许 32 位程序在 64 位系统上执行。**此外,它将使 32 位和 64 位库能够在同一系统上共存——我们需要它,因为 32 位程序需要 32 位库。

在包括 Ubuntu 在内的类 Debian 发行版中,gcc编译器、C 库和Make工具都存在于build-essential包中。我们需要使用apt安装build-essentialbinutils包:

$ sudo apt -y update
$ sudo apt -y install build-essential binutils

同样,我们将安装gcc-multilib包以将Multilib机制添加到我们的系统中:

$ sudo apt -y install gcc-multilib

如果我们运行 Fedora、Redhat 或 CentOS,我们将使用dnf安装 gcc编译器、Make和 binutils

$ sudo dnf -y install gcc binutils make

同样,要为类似 Redhat 的发行版添加 32 位支持,我们运行:

$ sudo dnf -y install glibc-devel.i686

3. 编译器是 32 位还是 64 位?

现在,让我们使用uname命令检查我们的硬件信息:

$ uname -m 
x86_64

**默认情况下,编译器会生成适合架构的二进制文件。**因此,如果我们有一个 64 位处理器,它将产生一个 64 位二进制文件。

现在,让我们检查一下我们的gcc安装:

$ gcc -v
Using built-in specs.
COLLECT_GCC=/usr/bin/gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/10/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,objc,obj-c++,ada,go,d,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --with-isl --enable-offload-targets=nvptx-none --without-cuda-driver --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 10.3.1 20210422 (Red Hat 10.3.1-1) (GCC) 

正如我们所见,**目标是x86_64——**这是 Linux 系统中 64 位处理器(AMD 或 Intel)的架构名称。

4. 在 64 位系统上编译 32 位程序

如前所述,编译器将为目标架构生成一个二进制文件,在我们的例子中是x86_64

$ gcc prog.c -o prog

为了验证编译过程产生的文件类型,我们使用file工具:

$ file prog
prog: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=07a13c851f21019b160a777a8ac047bed3a06489, for GNU/Linux 3.2.0, not stripped

如果我们尝试编译 32 位程序而不使我们的系统与 32 位配置兼容,我们将观察到以下错误:

$ $ gcc -m32 prog.c -o prog
In file included from /usr/include/features.h:489,
                 from /usr/include/bits/libc-header-start.h:33,
                 from /usr/include/stdio.h:27,
                 from menu.c:1:
/usr/include/gnu/stubs.h:7:11: fatal error: gnu/stubs-32.h: No such file or directory
    7 | # include <gnu/stubs-32.h>
      |           ^~~~~~~~~~~~~~~~
compilation terminated.

现在,让我们使用-m32*选项编译一个 32 位程序*:

$ gcc -m32 prog.c -o prog

在这里,我们已经在兼容系统上成功编译了一个 32 位程序。我们可以使用file命令检查二进制文件:

$ file prog
a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=a711fe476bf35d8e36ff6d5370ac384e3a84539d, for GNU/Linux 3.2.0, not stripped

5. 使用 CMake

现在,让我们使用 CMake 来自动化编译。CMake 是旨在在各种系统上自动编译的软件。我们需要make工具和 C 或 C++ 编译器来构建项目。

让我们看看如何在 Debian 和 Ubuntu 发行版上安装 CMake:

$ sudo apt -y install cmake

要在 Fedora 和 Redhat 之类的系统上安装 CMake,我们运行:

$ sudo dnf install cmake

要使用 CMake 构建 32 位项目,我们将使用由 3 个文件组成的示例项目 - main.c、prog.cprog.h

$ ls project/
main.c prog.c prog.h

首先,我们需要创建一个CmakeLists.txt文件来传递gcc选项

cmake_minimum_required(VERSION 3.19)
set(GCC_COVERAGE_COMPILE_FLAGS "-m32")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}")
project(prog)
add_executable(prog main.c prog.c)

此外,我们需要创建一个build目录来构建我们的项目:

$ cd project && mkdir build && cd build

之后,我们从build目录运行cmake为我们的项目生成一个Makefile

$ cmake ..
-- The C compiler identification is GNU 10.3.1
-- The CXX compiler identification is GNU 10.3.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/blogdemo/CMake/project/build

最后,我们使用make工具运行Makefile来生成可执行程序

$ make
scanning dependencies of target prog
[ 33%] Building C object CMakeFiles/hello.dir/main.c.o
[ 66%] Building C object CMakeFiles/hello.dir/prog.c.o
[100%] Linking C executable prog
[100%] Built target prog

之后,让我们使用file命令检查二进制文件:

$ file prog 
prog: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=21edad69c38b540f9d3dd508a78509bc2d8c61d8, for GNU/Linux 3.2.0, not stripped

因此,我们有一个链接到 32 位 C 库的 32 位可执行程序。