(Reference Links: https://zhuanlan.zhihu.com/p/69393545 )
Linux
We try to prepare bare metal program under Linux, such linker errors may occur:
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
The problem here is that the linker will default to start the process of referencing C language runtime, or also described as _start
function. We will use no_std
a library that implements excluded under the C language standard library libc
, so the linker can not resolve the relevant references, get "undefined reference" problem. To solve this problem, we need to tell the linker that it should start the process does not refer to the use of the C language - we can add -nostartfiles
tags to do this.
To add a parameter to the linker by cargo, we use the cargo rustc
command. The role of the command and cargo build
the same, but allows the developer to the lower Rust compiler rustc
pass parameters. Further, rustc
a -C link-arg
label, it is possible to pass the required parameters to the linker. In summary, we can write the following command:
cargo rustc -- -C link-arg=-nostartfiles
After this, we should be able to successfully compile the package, as a stand-alone executable program under the Linux system. Here we do not explicitly specify the name of the entry point function, because the linker will use the default function name _start
.
Windows
In Windows system, you may have a different linker error:
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
The linker error "must be defined entry points," meaning that it does not find an entry point. In the Windows system, the default entry point determined by the function name of the subsystem [1] . Of the CONSOLE
subsystem, the linker will look for named mainCRTStartup
function; while the WINDOWS
subsystem, it will look WinMainCRTStartup
. Our _start
functions are not two names - in order to use it, we can pass to the linker /ENTRY
parameters:
cargo rustc -- -C link-arg=/ENTRY:_start
From here we also see the format of the parameters, the next link in the Windows system on the use of, and under Linux are quite different.
Run the command, we got another linker errors:
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
This error is due to Windows executable program can use different subsystems ( the Subsystem ). General Windows programs, sub-function name used by the entry point inferred from: If the entry point is the main
function that will use CONSOLE
the subsystem; if it is WinMain
a function, use the WINDOWS
subsystem. Since our _start
function name and the difference between the two, we need to explicitly specify the use of the subsystem:
cargo rustc -- -C link-args="/ENTRY:_start /SUBSYSTEM:console"
Here we use the CONSOLE
subsystem, but WINDOWS
the subsystem is feasible. Here, we use a complex argument link-args
instead of multiple -C link-arg
, because the latter requires all parameters are listed in sequence, more space.
After using this command, the executable program we should be able to run under Windows.
macOS
If you use macOS system development, we may encounter linker errors:
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 […]
The error message tells us that the linker can not find the default entry point function, it is named main
- for some reason, all the function names macOS are to be underlined _
prefix. To set the entry point to function _start
, we send a link parameters -e
:
cargo rustc -- -C link-args="-e __start"
-e
Parameter specifies the name of the entry point. Since the function under each macOS has an underscore _
prefix, we should be named as the entry point function __start
instead _start
.
Run this command, the emergence of such a link error now:
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 […]
The reason is to get this error, macOS not officially supported statically linked binary library [2] , and the default program requires a link to libSystem
the library. To link to static binary library, we put -static
tags to linkers:
cargo rustc -- -C link-args="-e __start -static"
Run the modified command. Linker seem satisfied, gave us throw a new error:
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 […]
The reason for this error is that the program is linked to default on macOS crt0
(C runtime zero) library. This problem encountered on Linux and similar systems, we can add a -nostartfiles
link parameters:
cargo rustc -- -C link-args="-e __start -static -nostartfiles"
Now, our program should be able to successfully compiled on macOS.