【问题标题】:Rust custom bare metal compile target: linker expects "_start" symbol and discards unused ones: How can I specify a custom entry symbol?Rust 自定义裸机编译目标:链接器需要“_start”符号并丢弃未使用的符号:如何指定自定义条目符号?
【发布时间】:2021-06-10 09:17:19
【问题描述】:

我正在用 Rust 交叉编译 x86 的裸机 32 位代码,我面临的问题是,如果入口函数没有完全调用 _start,最终目标文件是空的;链接器会丢弃所有代码,因为它认为它已经死了。我很熟悉这个事实,_start 是一个众所周知的入口点名称,但问题仍然是:

Rust、LLVM 或 Linker 中的哪个部分强制执行此操作? extern "C" fn ...#[no_mangle]#[export_name = "foobar"] 等属性也不起作用(被链接器丢弃)。我的猜测是,它不是 Rust 编译器,而是链接器。如您所见,在我的例子中,我使用rust-lld 作为链接器,使用ld.lld 作为链接器风格(见下文)。

  1. 所需的_start-来自哪里?为什么链接器会丢弃我的其他代码?
  2. 为链接器指定自定义入口点的最佳选择是什么?

x86-unknown-bare_metal.json

{
  "llvm-target": "i686-unknown-none",
  "data-layout": "e-m:e-i32:32-f80:128-n8:16:32-S128-p:32:32",
  "arch": "x86",
  "target-endian": "little",
  "target-pointer-width": "32",
  "target-c-int-width": "32",
  "os": "none",
  "executables": true,
  "linker-flavor": "ld.lld",
  "linker": "rust-lld",
  "panic-strategy": "abort",
  "disable-redzone": true,
  "features": "+soft-float,+sse"
}

我每晚都使用 Rust 1.54.0,并在 Linux 5.8.0 系统上构建它。

我花了一些时间在互联网上搜索并找到了讨论,Rust 最终应该得到一个 #[entrypoint="foobar 注释或类似的东西,但不幸的是我没有找到一个可用的解决方案。

我的尝试是追加

"pre-link-args": {
  "ld.lld": [
    "-e,foobar"
  ]
}

到目标定义(也称为 foobar 的函数)但目标文件仍然是空的。另一种尝试是保留所有死代码。这可行,但这个解决方案很脏。

最小代码示例:

// disable rust standard library
#![no_std]
// disables Rust runtime init,
#![no_main]

// see https://docs.rust-embedded.org/embedonomicon/smallest-no-std.html
#![feature(lang_items)]

// see https://docs.rust-embedded.org/embedonomicon/smallest-no-std.html
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}

use core::panic::PanicInfo;
use core::sync::atomic;
use core::sync::atomic::Ordering;

#[no_mangle]
/// The name **must be** `_start`, otherwise the compiler doesn't output anything
/// to the object file. I don't know why it is like this.
/// Also `pub` or `pub extern "C"` doesn't work
fn _start() -> ! {
    loop {}
}

#[inline(never)]
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {
        atomic::compiler_fence(Ordering::SeqCst);
    }
}

【问题讨论】:

  • 你尝试过制作你想要的入口函数pub吗?
  • 是的,没有变化。此外,链接器不知道 Rust 的可见性特性。

标签: rust linker llvm


【解决方案1】:

新答案 [解决方案]

真正的解决方案非常简单,但很难找到,因为在这个相对无证的领域很难挖掘出可能的选择和解决方案。我发现llvm-ld 使用与GNU ld 相同的选项。所以我检查了GNU ld 链接选项并找到了解决方案。必须是

"pre-link-args": {
  "ld.lld": [
    "--entry=entry_32_bit"
  ]
}

值是文件中函数的名称。函数必须#[no_mangle]注解。

另见:https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html

旧答案:

一个快速且非常肮脏的解决方案是

#[no_mangle]
/// The name **must be** `_start`, otherwise the compiler doesn't output anything
/// to the object file. I don't know it is like this.
fn _start() -> ! {
    entry_32_bit();
}

#[no_mangle]
#[inline(never)]
fn entry_32_bit() -> ! {
    loop {}
}

这样,可以直接从汇编中跳转到符号entry_32_bit。但这个“解决方案”远非理想!尤其是当你想将多个 bin 链接在一起时,会出现名称冲突。

【讨论】:

  • entry_32_bit 上使用#[export_name = "_start"] 会更好
  • 如果我使用我的解决方法从另一个程序集文件(start.asm,因为我正在编写低级启动代码;它应该调用 Rust 二进制文件)调用该函数,我可以将 jmp 调用到 "entry_32bit ",否则我将不得不 jmp 到 "_start"。我想去掉“_start”符号。
  • 以上 cmets 与旧答案有关。我编辑了我的答案以包含新的和旧的。
猜你喜欢
  • 2019-03-24
  • 1970-01-01
  • 2010-12-31
  • 2019-03-18
  • 2022-01-15
  • 2013-02-28
  • 1970-01-01
  • 1970-01-01
  • 2012-05-08
相关资源
最近更新 更多