使用自定义链接器与Cargo

6

我想使用来构建需要自定义编译(调用汇编器、链接器等)的项目。

我之前用一个构建脚本实现了这个目标,但并不完美。例如,为了构建这篇教程中的代码,我创建了以下构建脚本:

use std::fs::create_dir;
use std::process::Command;

fn main() {
    build();
    link();
    iso();
}

fn build() {
    Command::new("cargo")
        .current_dir("uefi_app")
        .args(&["rustc", "--", "--emit", "obj"])
        .status().unwrap();
}

fn iso() {
    let disk_file = "target/debug/disk.img";
    let disk_dir = "target/debug/disk";
    let efi_boot = disk_dir.to_owned() + "/efi/boot";
    let copy_dest = efi_boot.clone() + "/bootx64.efi";

    let dd_of = "of=".to_owned() + disk_file;
    Command::new("dd")
        .args(&["if=/dev/zero", &dd_of, "bs=512", "count=93750"])
        .status().unwrap();

    Command::new("parted")
        .args(&[disk_file, "-s", "-a", "minimal", "mklabel", "gpt"])
        .status().unwrap();

    Command::new("parted")
        .args(&[disk_file, "-s", "-a", "minimal", "mkpart", "EFI", "FAT16", "2048s", "93716s"])
        .status().unwrap();

    Command::new("parted")
        .args(&[disk_file, "-s", "-a", "minimal", "toggle", "1", "boot"])
        .status().unwrap();

    Command::new("sudo")
        .args(&["losetup", "--offset", "1048576", "--sizelimit", "46934528", "/dev/loop0", disk_file])
        .status().unwrap();

    let _ = create_dir(disk_dir);

    Command::new("sudo")
        .args(&["mkdosfs", "-F", "32", "/dev/loop0"])
        .status().unwrap();

    Command::new("sudo")
        .args(&["mount", "/dev/loop0", disk_dir])
        .status().unwrap();

    Command::new("sudo")
        .args(&["mkdir", "-p", &efi_boot])
        .status().unwrap();

    Command::new("sudo")
        .args(&["cp", "target/debug/boot.efi", &copy_dest])
        .status().unwrap();

    Command::new("sudo")
        .args(&["umount", disk_dir])
        .status().unwrap();

    Command::new("sudo")
        .args(&["losetup", "-d", "/dev/loop0"])
        .status().unwrap();

    Command::new("sudo")
        .args(&["rm", "-R", disk_dir])
        .status().unwrap();
}

fn link() {
    Command::new("x86_64-efi-pe-ld")
        .args(&["--oformat", "pei-x86-64", "--subsystem", "10", "-pie", "-e", "efi_main", "uefi_app/target/debug/uefi_app.o", "-o", "target/debug/boot.efi"]).output().unwrap();
}

您可以在此处查看完整的板条箱。
另一个例子是此板条箱,用于来自这里的操作系统教程。
这需要一个单独的板条箱,因为在构建脚本中运行货物似乎会触发无限循环。
这个构建脚本的问题是,每次更新由构建脚本编译的板条箱代码时,我都需要运行。
我该如何使用cargo自动化这样的编译呢?
我想要的只是输入cargo run,然后获得一个ISO(或硬盘文件)并启动虚拟机。

你的标题说“自定义链接器”,但问题似乎是在询问如何完全实现像Make这样的构建系统。您可能希望尝试同步这两个事情。此外,这并不是构建脚本的真正用途,这可能解释了递归问题。 - Shepmaster
@Sephmaster 如果我只问是否可以使用另一个链接器,那会不会更好一些? - antoyo
2个回答

8
您可以在 .cargo/config 文件中指定自定义链接器。此示例可供参考:
[target.thumbv7em-none-eabi]
linker = "arm-none-eabi-gcc"
ar = "arm-none-eabi-ar"

我应该把这个配置文件放在哪里? - Petrus Theron
如果我没记错的话,.cargo目录应该放在你的项目根目录下,也就是和Cargo.toml文件在同一个目录下。配置文件则应该放在.cargo目录中。 - Dave Hylands
2
如何为所有类型的目标指定链接器? - Dee
1
本页面介绍了配置层次结构以及可以放置配置的多个位置,具体取决于您想要影响模块、项目、所有项目等。 https://doc.rust-lang.org/cargo/reference/config.html - Andrew Mackenzie

3

如果您不想使用配置文件,也可以使用--config指定链接器路径。请参见cargo configuration

对于您的示例,应该可以这样做:

cargo build --target thumbv7em-none-eabi --config target.thumbv7em-none-eabi.linker=\"arm-none-eabi-gcc\"


顺便提一下:反斜杠转义的双引号很重要。 - mcr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接