error: linking with `cc` failed: exit code: 1


(參考鏈接:https://zhuanlan.zhihu.com/p/69393545)

Linux

我們在Linux下嘗試編寫裸機程序,可能出現這樣的鏈接器錯誤:

error: linking with `cc` failed: exit code: 1
  |
  = note: "cc" […]
  = note: /usr/lib/gcc/../x86_64-linux-gnu/Scrt1.o: In function `_start':
          (.text+0x12): undefined reference to `__libc_csu_fini'
          /usr/lib/gcc/../x86_64-linux-gnu/Scrt1.o: In function `_start':
          (.text+0x19): undefined reference to `__libc_csu_init'
          /usr/lib/gcc/../x86_64-linux-gnu/Scrt1.o: In function `_start':
          (.text+0x25): undefined reference to `__libc_start_main'
          collect2: error: ld returned 1 exit status

這里遇到的問題是,鏈接器將默認引用C語言運行時的啟動流程,或者也被描述為_start函數。它將使用我們在no_std下被排除的C語言標准庫實現庫libc,因此鏈接器不能解析相關的引用,得到“undefined reference”問題。為解決這個問題,我們需要告訴鏈接器,它不應該引用C語言使用的啟動流程——我們可以添加-nostartfiles標簽來做到這一點。

要通過cargo添加參數到鏈接器,我們使用cargo rustc命令。這個命令的作用和cargo build相同,但允許開發者向下層的Rust編譯器rustc傳遞參數。另外,rustc提供一個-C link-arg標簽,它能夠傳遞所需的參數到鏈接器。綜上所述,我們可以編寫下面的命令:

cargo rustc -- -C link-arg=-nostartfiles

這樣之后,我們的包應該能成功編譯,作為Linux系統下的獨立式可執行程序了。這里我們沒有顯式指定入口點函數的名稱,因為鏈接器將默認使用函數名_start


 

Windows

在Windows系統下,可能有不一樣的鏈接器錯誤:

error: linking with `link.exe` failed: exit code: 1561
  |
  = note: "C:\\Program Files (x86)\\…\\link.exe" […]
  = note: LINK : fatal error LNK1561: entry point must be defined

鏈接器錯誤提示“必須定義入口點”,意味着它沒有找到入口點。在Windows系統下,默認的入口點函數名由使用的子系統決定[1]。對CONSOLE子系統,鏈接器將尋找名為mainCRTStartup的函數;而對WINDOWS子系統,它將尋找WinMainCRTStartup。我們的_start函數並非這兩個名稱——為了使用它,我們可以向鏈接器傳遞/ENTRY參數:

cargo rustc -- -C link-arg=/ENTRY:_start

我們也能從這里的參數的格式中看到,Windows系統下的鏈接器在使用方法上,與Linux下的有較大不同。

運行命令,我們得到了另一個鏈接器錯誤:

error: linking with `link.exe` failed: exit code: 1221
  |
  = note: "C:\\Program Files (x86)\\…\\link.exe" […]
  = note: LINK : fatal error LNK1221: a subsystem can't be inferred and must be
          defined

產生這個錯誤,是由於Windows可執行程序可以使用不同的子系統(subsystem)。對一般的Windows程序,使用的子系統將由入口點的函數名推斷而來:如果入口點是main函數,將使用CONSOLE子系統;如果是WinMain函數,則使用WINDOWS子系統。由於我們的_start函數名稱與上兩者不同,我們需要顯式指定使用的子系統:

cargo rustc -- -C link-args="/ENTRY:_start /SUBSYSTEM:console"

這里我們使用CONSOLE子系統,但WINDOWS子系統也是可行的。這里,我們使用復數參數link-args代替多個-C link-arg,因為后者要求把所有參數依次列出,比較占用空間。

使用這行命令后,我們的可執行程序應該能在Windows下運行了。


 

macOS

如果使用macOS系統開發,我們可能遇到這樣的鏈接器錯誤:

error: linking with `cc` failed: exit code: 1
  |
  = note: "cc" […]
  = note: ld: entry point (_main) undefined. for architecture x86_64
          clang: error: linker command failed with exit code 1 […] 

這個錯誤消息告訴我們,鏈接器不能找到默認的入口點函數,它被命名為main——出於一些原因,macOS的所有函數名都被加以下划線_前綴。要設置入口點函數到_start,我們傳送鏈接器參數-e

cargo rustc -- -C link-args="-e __start"

-e參數指定了入口點的名稱。由於每個macOS下的函數都有下划線_前綴,我們應該命名入口點函數為__start,而不是_start

運行這行命令,現在出現了這樣的鏈接器錯誤:

error: linking with `cc` failed: exit code: 1
  |
  = note: "cc" […]
  = note: ld: dynamic main executables must link with libSystem.dylib
          for architecture x86_64
          clang: error: linker command failed with exit code 1 […]

得到這個錯誤的原因是,macOS並不官方支持靜態鏈接的二進制庫[2],而要求程序默認鏈接到libSystem庫。要鏈接到靜態二進制庫,我們把-static標簽傳送到鏈接器:

cargo rustc -- -C link-args="-e __start -static"

運行修改后的命令。鏈接器似乎並不滿意,又給我們拋出新的錯誤:

error: linking with `cc` failed: exit code: 1
  |
  = note: "cc" […]
  = note: ld: library not found for -lcrt0.o
          clang: error: linker command failed with exit code 1 […]

出現這個錯誤的原因是,macOS上的程序默認鏈接到crt0(C runtime zero)庫。這和Linux系統上遇到的問題相似,我們可以添加一個-nostartfiles鏈接器參數:

cargo rustc -- -C link-args="-e __start -static -nostartfiles"

現在,我們的程序應該能夠在macOS上成功編譯了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM