簡體   English   中英

Rust 自定義裸機編譯目標:linker 需要“_start”符號並丟棄未使用的符號:如何指定自定義條目符號?

[英]Rust custom bare metal compile target: linker expects "_start" symbol and discards unused ones: How can I specify a custom entry symbol?

I'm cross compiling bare metal 32-bit code for x86 with Rust and I'm facing the problem, that the final object file is empty, if the entry function is not exactly called _start ; linker 將所有代碼丟棄,因為它認為它已死。 我很熟悉_start是一個眾所周知的入口點名稱,但問題仍然是:

Rust、LLVM 或 Linker 中的哪個部分強制執行此操作? extern "C" fn...#[no_mangle]#[export_name = "foobar"]這樣的屬性也不起作用(被鏈接器丟棄)。 我的猜測是,它不是 Rust 編譯器,而是 linker。 如您所見,在我的例子中,我使用rust-lld作為 linker 和ld.lld作為鏈接器風格(見下文)。

  1. 所需的_start -來自哪里? 為什么 linker 會丟棄我的其他代碼?
  2. 將我的自定義入口點指定到 linker 的最佳選擇是什么?

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注釋或類似的東西,但不幸的是我沒有找到可用的解決方案。

我的嘗試是 append

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

到目標定義(也稱為 foobar 的函數),但 object 文件仍然是空的。 另一種嘗試是保留所有死代碼。 這可行,但此解決方案很臟。

最小代碼示例:

// 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);
    }
}

新答案 [解決方案]

真正的解決方案非常簡單,但很難找到,因為在這個相對無證的領域中很難挖掘出可能的選擇和解決方案。 我發現llvm-ld使用與GNU ld相同的選項。 所以我檢查了GNU ld鏈接選項並找到了解決方案。 它一定要是

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

值為文件中function的名稱。 function必須#[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 但是這種“解決方案”遠非理想,尤其是當您想將多個垃圾箱鏈接在一起時。 會有名字沖突。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM