当前位置: 首页 > 工具软件 > PE-Linux > 使用案例 >

在ubuntu中构建x86_64-linux for windows

魏晨
2023-12-01


本篇博文介绍的是如何在ubuntu中构建一个支持 多库的加拿大交叉工具链:“x86_64-linux for windows”。这个工具链将在windows中运行,但却为linux编译产生可执行文件( *.elf)。

一般来说,在ubuntu中构建x86_64-linux for windows的主要步骤如下:

  1. 在虚拟机中安装linux系统
  2. 安装构建工具和依赖包
  3. 导入系统变量
  4. 下载源代码
  5. 构建x86_64-linux for linux
  6. 导入系统路径
  7. 构建x86_64-linux for windows
  8. 构建gdb(可选)
  9. 安装使用

0 为什么要构建x86_64-linux for windows

在搭建操作系统的开发环境中,于渊的Orange S:一个操作系统的实现给出了很详细的建议和方案。总结一点:linux系统是开发操作系统的首选平台。

但是,由于无法完全抛弃windows,所以通常将linux系统安装在虚拟机中来实现跨系统访问的。于是,这让操作系统的开发过程变成这样:

  1. 首先,在windows中编写程序;
  2. 然后,切换到虚拟机中的linux中编译程序;
  3. 最后,返回到windows调试程序。

可以看到,整个开发过程是非常繁琐的很浪费时间。为了简化开发操作,需要所用到的开发工具的windows版本,以求在windows完成所有的操作,而不用切换到虚拟机中的linux系统。

很幸运,大部分的工具都找到了对应的windows版本。其中,最重要的gcc编译器也找到了对应的windows版本:mingw-w64

mingw-w64只是一个编译器,它不同于cygwinmsys2还附带了一个庞大的类UNIX模拟环境。这即是它的缺点,但也是它的优点!

在cmd或PowerShell中,使用mingw-w64可以很方便地编译产生一个在windows上运行的可执行文件(*.exe),操作如同在linux中一样方便,并且可以将编译命令写入一个bat脚本来一步搞定!是的,一切都是那么自然!

当我希望使用它编译链接产生一个无格式的平坦纯二进制文件(*.bin)时,却总是出现下列错误提示:

# 不能在非PE输出文件'main.bin'上执行PE操作
ld.exe: cannot perform PE operations on non PE output file 'main.bin'

kos docs网站中的plainbin.pdf.gz文件提供了如何制作平坦纯二进制文件的教程。)

这是没办法的,mingw-w64只能编译产生在windows上运行的可执行文件(*.exe),不能编译产生在linux上运行的可执行文件(*.elf)。要想使用gcc制作平坦纯二进制文件,只能通过elf格式的可执行文件 。

OSDev Wiki中提供了一篇GCC_Cross-Compiler教程,从中找到了一个解决方案,那就是构建一个交叉编译器。在这个教程的底部提供了许多为每个系统预构建的交叉编译器。其中,为windows主机构建的是i686-/x86_64-elf 7.1.0 target + GDB

打开链接,进入它的github。根据它所提供的脚本,可以成功地构建了一个在windows中运行并为linux编译可执行文件的交叉编译器。但是,这个工具链是freestanding版本的(不支持头、库和运行时文件)。

在网上,也有很多教程演示了如何构建一个hosted版本的交叉编译器。比较全面的教程是How to Build a GCC Cross-Compiler(网上有翻译的中文版)和gcc 9.2 交叉工具链构建过程。(这里,感谢开发者的无私分享!让我从中找到了很多有用的信息。)

当我认为它能完全胜任我的工作时,又发现了一个问题:不支持多库。也就是,不能使用gcc -m32选项编译产生32位代码。不能产生16位代码也就算了,连32位代码也不能产生,这如何能在保护模式大展拳脚?!

为了构建一个支持多库的宿主交叉编译器,尝试了很多构建方法。在处理了无数个莫名奇妙的构建错误之后,终于,还是在崩溃的边缘构建成功了!为了方便记忆,记录下这篇笔记!

1 在虚拟机中安装linux系统

首先需要在虚拟机中安装一个基于debian的linux发行版,推荐:ubuntu linuxvmware workstation

选择ubuntu的版本时,应该与交叉工具链相匹配。当构建x86_64-linux时,选择64位ubuntu; 当构建i686-linux时,选择32位ubuntu。

选择vmware workstation是因为它提供了一个很方便的功能:通过识别大多数光盘镜像文件(ios)的系统类型来执行简易安装。这可以省去很多安装步骤。

在vmware workstation中安装ubuntu linux是很简单的,直接点击菜单“新建虚拟机”,按照提示操作即可,这里不在截图详解。唯一需要注意的是:硬盘大小。因为构建过程将会占用几十G的磁盘空间,为了避免空间不足,硬盘的大小至少需要30G以上。

当ubuntu安装好之后,只需要进行一些简单的配置就可以了:重置root密码、安装VMware-tools和修改软件源。如下所示。

在桌面空白处右击,打开终端,执行下列命令重置root密码:

sudo passwd root

进入root用户:

su root

点击菜单”虚拟机 → 安装VMware-tools“,然后,在ubuntu的文件管理器中解压VMware-tools。将压缩包中的vmware-install.pl拖拽到终端中以root用户运行,第一个提示输入yes,之后的全部回车使用默认值,直到安装完成。注销或重启ubuntu之后,就能在主机和虚拟机之间执行复制粘贴操作了。

打开sources.list:

gedit /etc/apt/sources.list

将下列内容复制到sources.list中:

deb https://mirror.bjtu.edu.cn/ubuntu/ focal main restricted universe multiverse
deb https://mirror.bjtu.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
deb https://mirror.bjtu.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
deb https://mirror.bjtu.edu.cn/ubuntu/ focal-security main restricted universe multiverse
deb https://mirror.bjtu.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse
# deb-src https://mirror.bjtu.edu.cn/ubuntu/ focal main restricted universe multiverse
# deb-src https://mirror.bjtu.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
# deb-src https://mirror.bjtu.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
# deb-src https://mirror.bjtu.edu.cn/ubuntu/ focal-security main restricted universe multiverse
# deb-src https://mirror.bjtu.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse

注意,软件源中的“focal”字符表示的是ubuntu 21.04的系统代号。对于其他版本 ,需要执行lsb_release -c命令查看当前ubuntu系统的系统代号。然后,将软件源中的所有“focal”字符替换成获得的系统代号。

最后,更新软件源:

apt-get update

2 安装构建工具和依赖包

执行下列命令来安装构建gcc/glibc所需要的各种工具和依赖包。参考gcc installglibc manual

apt-get install \
    autoconf \
    autogen \
    automake \
    bash \
    binutils \
    bison \
    bzip2 \
    dejagnu \
    diffutils \
    expect \
    flex \
    g++ \
    g++-multilib \
    gawk \
    gcc \
    gettext \
    gfortran \
    git \
    gnat \
    gperf \
    guile-3.0 \
    gzip \
    libc6-dev-i386 \
    libgmp-dev \
    libisl-dev \
    libmpc-dev \
    libmpfr-dev \
    libtool \
    libzstd-dev \
    m4 \
    make \
    mingw-w64 \
    patch \
    perl \
    sphinxsearch \
    ssh \
    tar \
    tcl \
    texinfo \
    texlive \
    unzip \
    wget -y

apt-get install build-essential -y

当然这些工具并不会都用到,但是全部安装可以避免日后想增添某些功能时,不会出现因工具缺失而再次产生某些构建错误。

2.1 mingw-w64:gcc for windows

mingw-w64是一个免费开源的交叉编译器,用于编译产生在windows上运行的可执行文件。它主要由binutils、gcc和gdb组成,并包含一套windows专用的系统头文件和静态导入库,以允许使用windows API。

mingw-w64支持2种目标三元组:

  • i686-w64-mingw32:用于编译产生32位windows可执行文件
  • x86_64-w64-mingw32:用于编译产生64位windows可执行文件

当构建x86_64-linux时,使用x86_64-w64-mingw32; 当构建i686-linux时,使用i686-w64-mingw32。

3 导入系统变量

执行下列命令将多次重复用到的选项和参数当做作变量导入到系统环境中,以方便批量修改。这些变量包括:源代码的下载目录和版本号、以及工具链的系统类型和安装目录等。

# 源代码目录
export srcdir=$HOME/src

# 源代码包的版本号
export binutils_ver=2.34
export gcc_ver=9.3.0
export kernel_ver=5.6
export glibc_ver=2.31
export pthread_ver=2.3
export gdb_ver=9.1
export expat_ver=2.2.9

# 系统类型
export host=x86_64-w64-mingw32           # 主机系统
export target=x86_64-linux               # 目标系统

# 安装目录
export sysdir=/usr/local/xgcc            # x86_64-linux for linux 
export optdir=/opt/xgcc                  # x86_64-linux for windows

# 系统根目录
export sysroot=$sysdir/$target           # x86_64-linux for linux 
export optroot=$optdir/$target           # x86_64-linux for windows

# 系统路径
export PATH=$sysdir/bin:$PATH

$HOME是系统预设的系统变量。在普通用户中,$HOME=/home/user(user是系统的登陆用户名);在管理用户中,$HOME=/root。

如果希望升级工具链或改变工具链类型(例如i686-linux),只需要改变这些变量即可,而无需修改后续章节中的命令。

4 下载源代码

一个交叉工具链主要由LinuxKernelHeaderglibcbinutilsgcc组成。可选地,可以包含gdb。因此,执行下列命令将所有需要的源代码下载到$srcdir中,并解压。

# 新建源代码目录
cd $HOME
rm -rf $srcdir
mkdir -p $srcdir

# 下载源代码包
cd $srcdir
wget http://ftpmirror.gnu.org/gnu/binutils/binutils-$binutils_ver.tar.gz
wget http://ftpmirror.gnu.org/gnu/gcc/gcc-$gcc_ver/gcc-$gcc_ver.tar.gz
wget https://www.kernel.org/pub/linux/kernel/v5.x/linux-$kernel_ver.tar.xz
wget http://ftpmirror.gnu.org/gnu/glibc/glibc-$glibc_ver.tar.gz
wget http://ftpmirror.gnu.org/gnu/glibc/glibc-linuxthreads-$pthread_ver.tar.gz
wget https://mirror.bjtu.edu.cn/gnu/gdb/gdb-$gdb_ver.tar.gz
wget https://sourceforge.net/projects/expat/files/expat/$expat_ver/expat-$expat_ver.tar.gz

# 解压
cd $srcdir
tar -xf binutils-$binutils_ver.tar.gz
tar -xf gcc-$gcc_ver.tar.gz
tar -xf linux-$kernel_ver.tar.xz
tar -xf glibc-$glibc_ver.tar.gz
tar -xf gdb-$gdb_ver.tar.gz
tar -xf expat-$expat_ver.tar.gz

# 解压glibc-linuxthreads
cd $srcdir/glibc-$glibc_ver
tar -xf ../glibc-linuxthreads-$pthread_ver.tar.gz

所有的GNU软件都保存在ftp://ftp.gnu.org/gnu中。使用http://ftpmirror.gnu.org/会自动选择最近和最新的镜像站进行下载。如果使用这两个地址都下载失败,那么可以直接从国内的某些开源镜像站(例如,北京交通大学开源镜像站)下载这些源代码包,然后将其复制到$srcdir目录并执行上述命令来解压。

4.1 下载gcc的依赖库

# 导入版本号
export gmp_ver=6.1.0
export mpfr_ver=3.1.4
export mpc_ver=1.0.3
export isl_ver=0.18
export cloog_ver=0.18.1

# 下载
cd $srcdir
wget ftp://gcc.gnu.org/pub/gcc/infrastructure/gmp-$gmp_ver.tar.bz2
wget ftp://gcc.gnu.org/pub/gcc/infrastructure/mpfr-$mpfr_ver.tar.bz2
wget ftp://gcc.gnu.org/pub/gcc/infrastructure/mpc/mpc-$mpc_ver.tar.gz
wget ftp://gcc.gnu.org/pub/gcc/infrastructure/isl-$isl_ver.tar.bz2
wget ftp://gcc.gnu.org/pub/gcc/infrastructure/cloog-$cloog_ver.tar.gz

# 解压
cd $srcdir
tar -xf gmp-$gmp_ver.tar.bz2
tar -xf mpfr-$mpfr_ver.tar.bz2
tar -xf mpc-$mpc_ver.tar.gz
tar -xf isl-$isl_ver.tar.bz2
tar -xf cloog-$cloog_ver.tar.gz

# 符号链接
cd $srcdir/gcc-$gcc_ver
ln -s ../mpfr-$mpfr_ver mpfr
ln -s ../gmp-$gmp_ver gmp
ln -s ../mpc-$mpc_ver mpc
ln -s ../isl-$isl_ver isl
ln -s ../cloog-$cloog_ver cloog

正常情况下,gcc会在构建过程中通过自动执行源代码目录中的./contrib/download_prerequisites脚本下载它依赖的gmp、mpfr、mpc和isl库。附加的cloog库需要自己下载。

为了统一,这里是将gmpmpfrmpcislcloog从各自的官网下载到$srcdir目录之后,制作一个符号链接到gcc源码目录,以便gcc能够找到它们并自动构建。

需要注意的是,gmp、mpfr、mpc和isl的版本号应该与gcc源代码目录中的download_prerequisites文件中指定的版本号相同,以保持最大兼容。

5 构建x86_64-linux for linux

在构建x86_64-linux for windows之前,需要先构建x86_64-linux for linux。如果构建一个支持多库的x86_64-linux for windows,那么构建的x86_64-linux for linux也必须支持多库。

这个x86_64-linux for linux将在linux中运行,并为linux产生可执行文件。因此,它的主机和目标系统类型为:

  • --host=x86_64-pc-linux-gnu
  • --target=x86_64-linux

x86_64-linux for linux的构建步骤如下:

  1. 构建LinuxKernelHeader
  2. 构建binutils
  3. 构建gcc bootstrap
  4. 构建glibc
  5. 构建gcc

因为目标系统是构建系统的别名,所以省略gcc bootstrap步骤,这将使用系统自带的gcc来构建glibc。但是,作为标准的构建方法,应该构建gcc bootstrap。

5.1 构建LinuxKernelHeader

如果构建glibc,那么需要一份linux内核头文件(LinuxKernelHeader)作为参考。LinuxKernelHeader描述了用户空间程序使用内核服务的API。 glibc使用这些内核头文件来定义可用的系统调用以及与这些系统调用一起使用的常量和结构。

执行下列命令将x86架构的Linux内核头文件(LinuxKernelHeader)安装到$sysroot/usr目录中。参考了LinuxKernel官网提供的headers_install.rst文档。

cd $srcdir/linux-$kernel_ver
make headers_install ARCH=x86 INSTALL_HDR_PATH=$sysroot/usr

安装LinuxKernelHeader文件最简单的方法是在内核源码的根目录中运行make headers_install命令。该命令接受两个可选参数:ARCHINSTALL_HDR_PATH

ARCH选项指定要为哪个架构生成头文件。在大多数情况下,架构的名称与/arch目录中的子目录名相同。这里应该指定为x86。对于x86架构,还支持两个别名:i386(32位)和x86_64(64位)。

INSTALL_HDR_PATH变量用于指定系统头文件的安装目录。所有的头文件将被安装到该目录下的/include子目录中。gcc将在--with-sysroot选项指定的系统根目录下的/usr/include子目录搜索LinuxKernelHeader文件。

5.2 构建binutils

执行下列命令来构建binutils并将其安装到$sysroot目录中。

cd $srcdir
rm -rf build-binutils
mkdir -p build-binutils
cd build-binutils
../binutils-$binutils_ver/configure --target=$target \
--prefix=$sysdir --{with-sysroot,with-build-sysroot}=$sysroot
make
make install

--with-sysroot选项指定目标的系统根目录。–with-build-sysroot选项指定构建的系统根目录。它们的值应该是glibc的安装目录($sysroot)。binutils将在该目录中搜索头、库和运行时文件。

5.3 构建gcc bootstrap

执行下列命令来构建一个完整的gcc并将其安装到$sysdir目录中。

cd $srcdir
rm -rf build-gcc
mkdir -p build-gcc
cd build-gcc
../gcc-$gcc_ver/configure --target=$target \
--prefix=$sysdir --with-newlib --without-headers \
--enable-languages=c,c++ \
--enable-multilib --with-multilib-list=m32,m64,mx32
make all-gcc
make all-target-libgcc
make install-gcc
make install-target-libgcc

当不需要多库目标时,使用-disable-multilib选项。当需要多库目标时,使用--enable-multilib--with-multilib-list=m32,m64,mx32。对于多库工具链,需要为每个gcc都添加这两个选项。

5.4 构建glibc

在x86架构中,具有三个glibc版本:i686 glibc、x86_64 glibc和x32 glibc。它们分别通过gcc的-m32-m64-mx32选项调用。

  • 指定CC='x86_64-linux-gcc -m32' CXX='x86_64-linux-g++ -m32' --host=x86_64-x32-linux构建i686 glibc
  • 指定CC='x86_64-linux-gcc -m64' CXX='x86_64-linux-g++ -m64' --host=x86_64-linux构建x86_64 glibc
  • 指定CC='x86_64-linux-gcc -mx32' CXX='x86_64-linux-g++ -mx32' --host=i686-linux构建x32 glibc

执行下列命令来构建i686、x86_64和x32 glibc并将其安装到系统根目录中($sysroot)。它们分别被安装在$sysroot目录的三个子目录中:/lib、/lib32、/lib64和/libx32。

# (1)  i686 glibc
cd $srcdir
rm -rf build-glibc-i686
mkdir -p build-glibc-i686
cd build-glibc-i686
echo "libdir=/usr/lib32" > configparms
echo "slibdir=/lib32" >> configparms
../glibc-$glibc_ver/configure CC='x86_64-linux-gcc -m32' CXX='x86_64-linux-g++ -m32' --host=i686-linux \
--prefix=/usr --with-headers=$sysroot/usr/include --enable-add-ons
make
make install DESTDIR=$sysroot


# (2) x86_64 glibc
cd $srcdir
rm -rf build-glibc-x86_64
mkdir -p build-glibc-x86_64
cd build-glibc-x86_64
echo "libdir=/usr/lib" > configparms
echo "slibdir=/lib" >> configparms
../glibc-$glibc_ver/configure CC='x86_64-linux-gcc -m64' CXX='x86_64-linux-g++ -m64' --host=x86_64-linux \
--prefix=/usr --with-headers=$sysroot/usr/include --enable-add-ons
make
make install DESTDIR=$sysroot


# (3) x32 glibc
cd $srcdir
rm -rf build-glibc-x32
mkdir -p build-glibc-x32
cd build-glibc-x32
echo "libdir=/usr/libx32" > configparms
echo "slibdir=/libx32" >> configparms
../glibc-$glibc_ver/configure CC='x86_64-linux-gcc -mx32' CXX='x86_64-linux-g++ -mx32' --host=x86_64-x32-linux \
--prefix=/usr --with-headers=$sysroot/usr/include --enable-add-ons
make
make install DESTDIR=$sysroot

--prefix选项用于指定glibc的相对位置。对于多库构建,必须使用--prefix=/usr来配置glibc并使用make install DESTDIR=$sysroot命令来安装。这将使所有的glibc版本都被安装到$sysroot目录并共存,而不会被之后安装的glibc版本覆盖。

--with-headers选项用于告诉glibc安装LinuxKernelHeader文件的安装目录。这个位置应该是在LinuxKernelHeader安装目录的include子目录($sysroot/usr/include)。

--enable-add-ons选项用于使能构建glibc附加包。这里仅使能构建glibc-linuxthreads。

根据glibc ABIList所述,默认情况下,i686、x86_64和x32 glibc的安装目录分别是/lib、/lib64和/libx32。但是,对于x86_64-linux-gcc,/lib是64位库安装目录,而/lib32作为32位库安装目录(/libx32是匹配的)。因此,需要使glibc与gcc的库目录相匹配。通常,无法修改gcc来匹配glibc,但是,可以修改glibc来匹配gcc。

glibc官网中的FAQ提供了修改方法:在glibc的构建目录下,新建一个名为configparms的脚本,然后,将libdir和slibdir变量的值添加到脚本中来分别用于改变基本库和扩展库的安装目录。例如,执行下列代码将i686 glibc的默认库目录改到/lib32:

gedit $srcdir/build-glibc-i686/configparms
libdir=/usr/lib32
slibdir=/lib32

5.5 构建gcc

执行下列命令来构建一个完整的gcc并将其安装到$sysdir目录中。

cd $srcdir
rm -rf build-gcc
mkdir -p build-gcc
cd build-gcc
../gcc-$gcc_ver/configure --target=$target \
--prefix=$sysdir --{with-sysroot,with-build-sysroot}=$sysroot \
--enable-languages=c,c++ \
--enable-multilib --with-multilib-list=m32,m64,mx32 --disable-bootstrap
make
make install

--with-sysroot选项用于告诉gcc在构建过程中使用$sysroot作为系统根目录。gcc将在这个目录中搜索目标系统的头、库和运行时对象文件。由于这个目录是gcc安装目录中的子目录,不管安装目录是否被移动,使用gcc编译程序时都不用显式使用--sysroot选项来指定系统根目录的搜索目录。

--with-build-sysroot选项用于告诉(在构建系统上运行的)gcc在构建目标库时的系统根目录。只有当--with-sysroot选项被使用时,这个选项才会有用。对于多库构建,这个选项用于避免gcc无法识别一些系统头、库和运行时文件的路径问题。

--enable-languages选项指定gcc支持的编程语言。当前支持的值有包括:all,default,ada,c,c++,d,fortran,go,jit,lto,objc,obj-c++

--disable-bootstrap选项用于禁用gcc的三阶段引导过程。在构建交叉编译器时,通常是不可能对gcc执行三阶段引导。如果没有禁用这个过程,将在第二阶段引导时出现构建错误。

6 导入系统路径

将上一步构建的x86_64-linux for linux的可执行文件的路径导入到系统路径上。这个路径是其安装目录中的bin目录(--prefix=$sysdir/bin)。

export PATH=$PATH:/usr/local/xgcc/bin

执行下列命令来检查一下x86_64-linux for linux中的ld和gcc是否可用:

$target-ld -v
$target-gcc -v

7 构建x86_64-linux for windows

当构建x86_64-linux for linux之后,就可以构建x86_64-linux的windows版本(x86_64-linux for windows)。这个交叉编译器将在windows中运行,但为linux产生可执行文件。因此,它的主机和目标系统类型分别为:

  • --host=x86_64-w64-mingw32(主机windows)
  • --target=x86_64-linux(目标linux)

构建x86_64-linux for windows的过程与x86_64-linux for linux是相似的。它们的不同之处在于,安装目录和主机系统。

7.1 构建LinuxKernelHeader

执行下列命令将x86架构的Linux内核头文件安装到$optroot/usr目录中。

cd $srcdir/linux-$kernel_ver
make headers_install ARCH=x86 INSTALL_HDR_PATH=$optroot/usr

7.2 构建glibc

执行下列命令来构建i686、x86_64和x32 glibc并将其安装到$optroot目录中。其实,可以直接安装为x86_64-linux for linux构建好的glibc,这里只是为了标准化构建方法。

# (1)  i686 glibc
cd $srcdir
rm -rf build-glibc-i686
mkdir -p build-glibc-i686

cd build-glibc-i686
echo "libdir=/usr/lib32" > configparms
echo "slibdir=/lib32" >> configparms
../glibc-$glibc_ver/configure CC='x86_64-linux-gcc -m32' CXX='x86_64-linux-g++ -m32' --host=i686-linux \
--prefix=/usr --with-headers=$optroot/usr/include --enable-add-ons
make
make install DESTDIR=$optroot


# (2) x86_64 glibc
cd $srcdir
rm -rf build-glibc-x86_64
mkdir -p build-glibc-x86_64

cd build-glibc-x86_64
echo "libdir=/usr/lib" > configparms
echo "slibdir=/lib" >> configparms
../glibc-$glibc_ver/configure CC='x86_64-linux-gcc -m64' CXX='x86_64-linux-g++ -m64' --host=x86_64-linux \
--prefix=/usr --with-headers=$optroot/usr/include --enable-add-ons
make
make install DESTDIR=$optroot

# (3) x32 glibc
cd $srcdir
rm -rf build-glibc-x32
mkdir -p build-glibc-x32

cd build-glibc-x32
echo "libdir=/usr/libx32" > configparms
echo "slibdir=/libx32" >> configparms
../glibc-$glibc_ver/configure CC='x86_64-linux-gcc -mx32' CXX='x86_64-linux-g++ -mx32' --host=x86_64-x32-linux \
--prefix=/usr --with-headers=$optroot/usr/include --enable-add-ons
make
make install DESTDIR=$optroot

7.3 构建binutils

执行下列命令来构建binutils并将其安装到$optdir目录中。

# 构建binutils
cd $srcdir
rm -rf build-binutils
mkdir -p build-binutils
cd build-binutils
../binutils-$binutils_ver/configure --host=$host --target=$target \
--prefix=$optdir --{with-sysroot,with-build-sysroot}=$optroot
make
make install

7.4 构建gcc

执行下列命令来构建gcc并将其安装到$optdir目录中。

# 构建gcc
cd $srcdir
rm -rf build-gcc
mkdir -p build-gcc
cd build-gcc
../gcc-$gcc_ver/configure --host=$host --target=$target \
--prefix=$optdir --{with-sysroot,with-build-sysroot}=$optroot \
--enable-languages=c,c++ \
--enable-multilib --with-multilib-list=m32,m64,mx32
make
make install

这些配置选项应该与x86_64-linux for linux匹配。

8 构建gdb(可选)

如果有特殊需要,也可以构建一个交叉调试器(x86_64-linux-gdb)。但是,这个gdb无法在本机调试目标代码,因为它需要模拟。

通常,模拟操作是通过远程调试来连接目标机实现的。因此,仅需要构建gdb和expat。

expat库是一个XML解析库,主要用于:远程协议内存映射,目标描述,远程共享库列表,windows系统共享库,跟踪框架信息,以及分支追踪。

8.1 构建expat

执行下列命令来构建expat并将其安装到$optdir/opt目录中。

cd $srcdir
rm -rf build-expat
mkdir -p build-expat
cd build-expat
../expat-$expat_ver/configure --host=$host --prefix=$optdir/opt
make
make install

expat仅仅用于解析XML,它不关心目标是什么,所以它没有--target选项。只需要指定--host选项即可。

值得一提的是,在以后通过学习的不断深入,可能会根据需要构建许多库来支配合工具链操作。通常来说,将这些库安装工具链的顶级目录并不是一个好主意,因此这里使用--prefix=$optdir/opt选项将所有库都安装到一个特定的子目录中避免和主要的程序混合在一起。

8.2 构建gdb

执行下列命令来构建gdb并将其安装到$optdir目录中。

# 构建gdb
cd $srcdir
rm -rf build-gdb
mkdir -p build-gdb
cd build-gdb
../gdb-$gdb_ver/configure --host=$host --target=$target \
--prefix=$optdir --{with-sysroot,with-build-sysroot}=$optroot --enable-64-bit-bfd \
--with-expat --with-libexpat-prefix=$optdir/opt \
--with-system-gdbinit=$optdir/etc/gdbinit
make
make install

因为gdb是运行在windows中并且调试的是运行在目标机中的可执行文件,所以--host应该设置为x86_64-w64-mingw32,而--target选项应该设置为x86_64-linux 。

--{with-sysroot,with-build-sysroot}用于指定gdb的系统根目录。该选项主要用于在远程调试时,使用本机安装的运行时库(而不是目标机中的库)来加快调试速度。

--with-expat选项用于使能expat库。如果在安装目录中找到libexpat,自动使能。这个库用于读取由gdb提供的XML文件。如果不可用,那么基于XML文件的某些功能(例如,远程协议内存映射,目标描述和共享库列表)将在gdb中不可用。

–with-libexpat-prefix选项用于指定expat的安装位置,这里是$optdir/opt。

8.3 构建gdbserver

执行下列命令来构建gdbserver并将其安装到$optdir目录中。

cd $srcdir
rm -rf build-gdbserver
mkdir -p build-gdbserver
cd build-gdbserver
../gdb-$gdb_ver/gdb/gdbserver/configure --host=$target --target=$target \
--prefix=$optdir --program-prefix=$target-
make
make install

因为gdbserver是运行在目标机中并且调试的是运行在目标机中的可执行文件,所以--host--target选项都应该指定为$target

如果linux中安装了gdb,那么它附带了一个gdbserver,为了避免同名,需要使用--program-prefix=$target-选项给gdbserver添加一个前缀来进行区分。

9 安装使用

执行下列命令将安装目录($optdir)打包,以方便复制到windows中。

cd $optdir
zip -r $target-tools.zip *

zip命令中的*表示压缩当前目录中的所有文件和目录。-r选项表示递归到子目录。

将x86_64-linux-tools.zip复制到Windows中,并将其解压到某个目录(例如,D:\x86_64-linux-tools)就可以使用了。所有的工具都以x86_64-linux为前缀。例如,gcc的名字为“x86_64-linux-gcc”。

在解压时,会出现有重复文件的提示(这些重复文件都属于LinuxKernelHeader文件)。这是因为windows的文件系统不区分文件名大小写而造成的。在解压时,可以选择重命名或覆盖,都不会影响工具的使用。

下面编写一个程序检查一下x86_64-linux for windows工具链是否可用。

在E:\os目录下,新建一个文本文档,将其命名为main.c。然后,添加如下代码:

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

这是所有c语言入门教程都会用到的Hello World程序。打开cmd,执行下列命令就来编译这个C程序:

set path=%path%;D:\x86_64-linux-tools\bin;
cd /D E:\os
x86_64-linux-gcc -m32 main.c
x86_64-linux-gcc -m64 main.c
x86_64-linux-gcc -mx32 main.c

首先,将工具链的路径添加到系统路径变量(path)上。然后,进入E:\os目录并执行x86_64-linux-gcc -m32/m64/mx32命令来编译main.c。

这将编译产生一个名为a.out的可执行文件。但是,这个可执行文件不能在windows(主机)中运行,必须将它复制到linux(目标)中运行。

9.1 永久添加可执行文件的路径

使用set命令对path的修改,将在cmd对话框关闭之后恢复到默认值。为了永久使用,需要系统变量“path”:在桌面右击“我的电脑”图标,选择“属性”→高级系统设置→高级→环境变量”,在打开的“环境变量”对话框,将可执行文件的路径添加到path变量中。

9.2 为命令设置别名

输入像x86_64-linux-gcc这么长的命令是很痛苦的,并且很浪费时间。因此,需要一个别名功能来简化命令输入。相比来说,git for windows提供的别名功能比cmd和powershell更好用,并且它还提供了大部分常用linux命令能在windows中使用。

在git for windows中添加别名的方式是非常简单的。在桌面右击,选择“Git Bash Here”,打开终端,输入下列命令对x86_64-linux-gcc别名:

alias xgcc="x86_64-linux-gcc"

这样,只需要使用xgcc就可以调用x86_64-linux-gcc。

如果需要永久使用别名,可以将它的别名添加到C:\Program Files\Git\etc\bash.bashrc文件的末尾。这样,每当打开Git Bash时都会自动加载这么别名命令。

下列汇总了x86_64-linux for windows中所有命令的别名:

# 自定义x86_64-linux-tools的别名
alias xaddr2line="x86_64-linux-addr2line"
alias xar="x86_64-linux-ar"
alias xas="x86_64-linux-as"
alias xc++="x86_64-linux-c++"
alias xc++filt="x86_64-linux-c++filt"
alias xcpp="x86_64-linux-cpp"
alias xelfedit="x86_64-linux-elfedit"
alias xg++="x86_64-linux-g++"
alias xgcc="x86_64-linux-gcc"
alias xgcc-9.3.0="x86_64-linux-gcc-9.3.0"
alias xgcc-ar="x86_64-linux-gcc-ar"
alias xgcc-nm="x86_64-linux-gcc-nm"
alias xgcc-ranlib="x86_64-linux-gcc-ranlib"
alias xgcov="x86_64-linux-gcov"
alias xgcov-dump="x86_64-linux-gcov-dump"
alias xgcov-tool="x86_64-linux-gcov-tool"
alias xgdb="x86_64-linux-gdb"
alias xgdb-add-inde="x86_64-linux-gdb-add-index"
alias xgdbserver="x86_64-linux-gdbserver"
alias xgprof="x86_64-linux-gprof"
alias xld.bfd="x86_64-linux-ld.bfd"
alias xld="x86_64-linux-ld"
alias xmake="x86_64-linux-make"
alias xnm="x86_64-linux-nm"
alias xobjcopy="x86_64-linux-objcopy"
alias xobjdump="x86_64-linux-objdump"
alias xranlib="x86_64-linux-ranlib"
alias xreadelf="x86_64-linux-readelf"
alias xsize="x86_64-linux-size"
alias xstrings="x86_64-linux-strings"
alias xstrip="x86_64-linux-strip"

至此,对x86_64-linux for windows工具链的讲解已经结束了。如果有什么问题,可以在评论去留言。

 类似资料: