Rustmusl/.NET-Glibc简析

程序员有二十年 2024-09-02 13:24:48
前言

先看下rust-glibc/musl再看下.net-glibc。

rust-glibc

从linux内核态到用户态的第一个函数:

// https://elixir.bootlin.com/glibc/glibc-2.35/source/sysdeps/x86_64/start.SENTRY (_start) /* Clearing frame pointer is insufficient, use CFI. */ cfi_undefined (rip) /* Clear the frame pointer. The ABI suggests this be done, to mark the outermost frame obviously. */ xorl %ebp, %ebp mov %RDX_LP, %R9_LP /* Address of the shared library termination function. */#ifdef __ILP32__ mov (%rsp), %esi /* Simulate popping 4-byte argument count. */ add $4, %esp#else popq %rsi /* Pop the argument count. */#endif /* argv starts just at the current stack top. */ mov %RSP_LP, %RDX_LP /* Align the stack to a 16 byte boundary to follow the ABI. */ and $~15, %RSP_LP /* Push garbage because we push 8 more bytes. */ pushq %rax /* Provide the highest stack address to the user code (for stacks which grow downwards). */ pushq %rsp /* These used to be the addresses of .fini and .init. */ xorl %r8d, %r8d xorl %ecx, %ecx#ifdef PIC mov main@GOTPCREL(%rip), %RDI_LP#else mov $main, %RDI_LP#endif call *__libc_start_main@GOTPCREL(%rip) hlt /* Crash if somehow `exit' does return. */END (_start) .data .globl __data_start__data_start: .long 0 .weak data_start data_start = __data_start

看下它的ASM:

(lldb) di -brustc`_start:-> 0x555555555920 <+0>: f3 0f 1e fa endbr64 0x555555555924 <+4>: 31 ed xor ebp, ebp 0x555555555926 <+6>: 49 89 d1 mov r9, rdx 0x555555555929 <+9>: 5e pop rsi 0x55555555592a <+10>: 48 89 e2 mov rdx, rsp 0x55555555592d <+13>: 48 83 e4 f0 and rsp, -0x10 0x555555555931 <+17>: 50 push rax 0x555555555932 <+18>: 54 push rsp 0x555555555933 <+19>: 45 31 c0 xor r8d, r8d 0x555555555936 <+22>: 31 c9 xor ecx, ecx 0x555555555938 <+24>: 48 8d 3d c1 01 00 00 lea rdi, [rip + 0x1c1] ; main 0x55555555593f <+31>: ff 15 5b 14 00 00 call qword ptr [rip + 0x145b] 0x555555555945 <+37>: f4 hlt

此时_start里面的调用第一个参数rdi实际上已经被赋值为glibc-main。call如下

// https://elixir.bootlin.com/glibc/glibc-2.35.9000/source/csu/libc-start.cSTATIC intLIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL), int argc, char **argv,#ifdef LIBC_START_MAIN_AUXVEC_ARG ElfW(auxv_t) *auxvec,#endif __typeof (main) init, void (*fini) (void), void (*rtld_fini) (void), void *stack_end){ //部分省略 __libc_start_call_main (main, argc, argv MAIN_AUXVEC_PARAM);}

__libc_start_call_main

// https://elixir.bootlin.com/glibc/glibc-2.35.9000/source/sysdeps/nptl/libc_start_call_main.h#L23_Noreturn static void__libc_start_call_main (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL), int argc, char **argv#ifdef LIBC_START_MAIN_AUXVEC_ARG , ElfW(auxv_t) *auxvec#endif ){ //省略部分代码 result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);}

这里的main是glibc-main,即是上面的rdi参数。由 C 编译器和链接器在编译和链接用户程序时自动生成的,ASM如下:

(lldb) di -brustc`main:-> 0x555555555b00 <+0>: 50 push rax 0x555555555b01 <+1>: 48 89 f2 mov rdx, rsi 0x555555555b04 <+4>: 48 8b 05 cd 12 00 00 mov rax, qword ptr [rip + 0x12cd] 0x555555555b0b <+11>: 8a 00 mov al, byte ptr [rax] 0x555555555b0d <+13>: 48 63 f7 movsxd rsi, edi 0x555555555b10 <+16>: 48 8d 3d d9 ff ff ff lea rdi, [rip - 0x27] ; rustc_main::main at main.rs:38 0x555555555b17 <+23>: b9 03 00 00 00 mov ecx, 0x3 0x555555555b1c <+28>: e8 ef fe ff ff call 0x555555555a10 ; std::rt::lang_start::<()> at rt.rs:157 0x555555555b21 <+33>: 59 pop rcx 0x555555555b22 <+34>: c3 ret

最后就到达了lang_start

// rust/library/std/src/rt.rs#[cfg(not(any(test, doctest)))]#[lang = "start"]fn lang_start<T: crate::process::Termination + 'static>( main: fn() -> T, argc: isize, argv: *const *const u8, sigpipe: u8,) -> isize { let Ok(v) = lang_start_internal( &move || crate::sys::backtrace::__rust_begin_short_backtrace(main).report().to_i32(), argc, argv, sigpipe, );Rust-musl

rust默认是glibc,musl需要指定下:

# rustup target add x86_64-unknown-linux-musl 安装# cargo build --target x86_64-unknown-linux-musl --debug/release 编译musl主要是把glibc部分直接编译到了最终可执行的二进制文件里面去了(lldb) dihello-rust`main:-> 0x7ffff7f99a10 <+0>: push rax 0x7ffff7f99a11 <+1>: mov rdx, rsi 0x7ffff7f99a14 <+4>: lea rax, [rip + 0x570d4] ; __rustc_debug_gdb_scripts_section__ 0x7ffff7f99a1b <+11>: mov al, byte ptr [rax] 0x7ffff7f99a1d <+13>: movsxd rsi, edi 0x7ffff7f99a20 <+16>: lea rdi, [rip - 0xe7] ; hello_rust::main::h8886cd98519b4979 at main.rs:1 0x7ffff7f99a27 <+23>: xor ecx, ecx 0x7ffff7f99a29 <+25>: call 0x7ffff7f99a50 ; std::rt::lang_start::h4843853a1732883f at rt.rs:152 0x7ffff7f99a2e <+30>: pop rcx 0x7ffff7f99a2f <+31>: ret

上面的rust-main跟glibc一样的。步过其堆栈则如下:

(lldb) bt* thread #1, name = 'hello-rust', stop reason = breakpoint 5.2 * frame #0: 0x00007ffff7f99a10 hello-rust`main frame #1: 0x00007ffff7fdbed7 hello-rust`libc_start_main_stage2 at __libc_start_main.c:95:2 frame #2: 0x00007ffff7f9965f hello-rust`_start + 2

但是实际上我们在地址0x00007ffff7f9965f 会发现另一个调用,这里不做细究

(lldb) b 0x00007ffff7f9965f (lldb) r(lldb) bt* thread #1, name = 'hello-rust', stop reason = breakpoint 6.1 7.1 * frame #0: 0x00007ffff7f9965f hello-rust`_start_c at dlstart.c:22:1 frame #1: 0x00007ffff7f9965f hello-rust`_start + 22.NET-glibc

.Net glibc与rust前面部分所有都是一样

(lldb) bt* thread #1, name = 'corerun', stop reason = breakpoint 1.1 * frame #0: 0x000055555556c420 corerun`main(argc=<unavailable>, argv=<unavailable>) at corerun.cpp:616 frame #1: 0x00007ffff7829d90 libc.so.6`__libc_start_call_main(main=(corerun`main at corerun.cpp:616), argc=2, argv=0x00007fffffffde78) at libc_start_call_main.h:58:16 frame #2: 0x00007ffff7829e40 libc.so.6`__libc_start_main_impl(main=(corerun`main at corerun.cpp:616), argc=2, argv=0x00007fffffffde78, init=0x00007ffff7ffd040, fini=<unavailable>, rtld_fini=<unavailable>, stack_end=0x00007fffffffde68) at libc-start.c:392:3 frame #3: 0x000055555556bf45 corerun`_start + 37

不同点在于.NET的main是在corerun.(so/exe)的SC-corerun.cpp里面

// runtime-main/src/coreclr/hosts/corerun/corerun.cppint MAIN(const int argc, const char_t* argv[]){ configuration config{}; if (!parse_args(argc, argv, config)) return EXIT_FAILURE; if (config.self_test) return self_test(); int exit_code = run(config); return exit_code;}
0 阅读:0

程序员有二十年

简介:感谢大家的关注