How to use GNU ld with rustc-compiled obj file without cargo?

I am trying to replicate each step in the build process as detailed here, but with rustc and without cargo, as follows:

$ /home/foo/rustc_source/installed/usr/local/bin/rustc -v -g -C opt-level=0 --emit obj main.rs

The preceding step generates main.o as expected. I know I could do the following to link it as well, but as mentioned earlier, I don't want rustc to compile AND link (instead, I'd like to explicitly invoke GNU ld to link):

$ /home/foo/rustc_source/installed/usr/local/bin/rustc -v -g -C opt-level=0 --emit obj,link main.rs

Now, I would like to link this with other necessary object files to generate an ELF executable I can run. I tried doing that with the following cmd, but it fails:

$ ld -o main.elf -Map=main.map /usr/lib/x86_64-linux-gnu/crti.o /usr/lib/x86_64-linux-gnu/crtn.o /usr/lib/x86_64-linux-gnu/crt1.o main.o

ld: /usr/lib/x86_64-linux-gnu/crt1.o: in function `_start':
(.text+0x21): undefined reference to `__libc_start_main'
ld: main.o: in function `std::rt::lang_start':
/home/foo/rustc_source/rust/library/std/src/rt.rs:194: undefined reference to `std::rt::lang_start_internal'
ld: main.o: in function `core::ops::function::FnOnce::call_once':
/home/foo/rustc_source/rust/library/core/src/ops/function.rs:250: undefined reference to `_Unwind_Resume'
ld: main.o: in function `main::main':
/media/foo/main.rs:2: undefined reference to `std::io::stdio::_print'
ld: main.o:(.data.DW.ref.rust_eh_personality[DW.ref.rust_eh_personality]+0x0): undefined reference to `rust_eh_personality'

My system/env info:

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 22.04.4 LTS
Release:	22.04
Codename:	jammy
$ uname -r
6.5.0-44-generic
$ /home/foo/rustc_source/installed/usr/local/bin/rustc --version
rustc 1.84.0-nightly (32b17d56e 2024-10-28)

If you want to do the linking yourself it is highly recommended to build a staticlib (--crate-type staticlib) instead and link this staticlib in whichever way you want. The staticlib crate type bundles the object file for the local crate with those for all rust dependencies and an additional object file that provides a couple of symbols liballoc needs which --emit obj doesn't provide a way to give you.

Thanks for your response. I wrote a simple hello world program in Rust (main.rs) as shown below:

fn main() {
        println!("Hello world");
}

Using --crate-type staticlib like you suggested generates libmain.a, rendering the fn main() function useless.

Your suggestion requires me to define a new main function elsewhere and link that in with this generated libmain.a, which is not what I want. What I want is to let fn main() be the entry point, just like how I did with a simple hello world C program in here by linking the generated main.o with crt*.o.

Thanks!

Don't do it, is the correct answer.
Also with c code, don't, you should use (g)cc to link and not call ld.

If your just interested in how it works you can see under the hood with forkstat command (or strace)

If wanting to link with other objects you should still be using cargo and add lines to build.rs (assuming there isn't an existing crate for such task.)