Rust musl/.NET-Glibc简析

点击上方蓝字 江湖评谈设为关注/星标

17a78f35cb1d36f0f4a5c0c18cb9a8bb.png

前言

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

rust-glibc

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

// https://elixir.bootlin.com/glibc/glibc-2.35/source/sysdeps/x86_64/start.S
ENTRY (_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 -b
rustc`_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.c
STATIC int
LIBC_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 -b
rustc`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) di
hello-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.cpp
int 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;
}

往期精彩回顾

Rustc Compile过程+线程

.NET9 Pre7 DATAS+Rustc Compile线程续

cbc606f4faf3f132aed1d096f653c443.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值