从.NET9到Rust

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

1790d01ac9c798c3fb66755d53cd5e74.png

前言

前一篇:从.NET9看Golang,里面了解了下Go可执行文件,Main入口并没有经过Glibc,而是有Go自己的库调用的。本篇继续扩展,看下Rust。先说下结论,实际上经过个人测试,除了Go,Rust/.NET/C/C++都是经过Glibc来调用Main的。

问题

首先解决下上一篇从.NET9看Golang的一个问题,也即是Go的main调用地方实际上是/usr/lib/go-1.18/src/runtime/proc.go:259堆栈处,实际249处,代码如下:

func main() {
 146         g := getg()
 147 
 148         // Racectx of m0->g0 is used only as the parent of the main goroutine.
 149         // It must not be used for anything else.
 150         g.m.g0.racectx = 0
 151 
 152         // Max stack size is 1 GB on 64-bit, 250 MB on 32-bit.
 153         // Using decimal instead of binary GB and MB because
 154         // they look nicer in the stack overflow failure message.
 155         if goarch.PtrSize == 8 {
 156                 maxstacksize = 1000000000
 157         } else {
 158                 maxstacksize = 250000000
 159         }
             //中间省略
 249         fn := main_main // make an indirect call, as the linker doesn't know the address o     f the main package when laying down the runtime
 250         fn()
 251         if raceenabled {
 252                 racefini()
 253         }


 }

可以看到Go实际是自举自己调用了main,然后通过这个main调用了用户写的那个main.

249         fn := main_main // make an indirect call, as the linker doesn't know the address o     f the main package when laying down the runtime
 250         fn()

而在这之前实际上它还调用了一个syscall进行一些用户main调用之前的操作。

306     MOVL  size+24(FP), R10
   307     MOVL  $SYS_rt_sigprocmask, AX
   308     SYSCALL
-> 309     CMPQ  AX, $0xfffffffffffff001
   310     JLS  2(PC)
   311     MOVL  $0xf1, 0xf1  // crash
   312     RET

那么用户态下的linux第一个启动Go的是谁呢?

1568 // The top-most function running on a goroutine
1569 // returns to goexit+PCQuantum.
1570 TEXT runtime·goexit(SB),NOSPLIT|TOPFRAME,$0-0
1571         BYTE    $0x90   // NOP
1572         CALL    runtime·goexit1(SB)     // does not return
1573         // traceback from goexit1 must hit code range of goexit
1574         BYTE    $0x90   // NOP

在goroutine上运行的最顶级函数,返回goexit+PCQuantum。实际上Linux用户态的main就是有它(runtime·goexit(SB))调用的,后者则调用go的用户态main,运行整个go程序。

Rust

#curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 安装
#cargo new rust-hello
#cd rust-hello
#cargo build
#cd target/debug
#./hello-rust

注意如果单个.rs文件Compile,是不被lldb识别的没有sysmbol,所以这里需要通过cargo创建项目。也可以rust-lldb,但需要脚本支撑,这里不做讨论。

#lldb hello-rust
(lldb)b main
(lldb)r
(lldb)bt
* thread #1, name = 'hello-rust', stop reason = breakpoint 1.2
  * frame #0: 0x000055555555b7e0 hello-rust`main
    frame #1: 0x00007ffff7c29d90 libc.so.6`__libc_start_call_main(main=(hello-rust`main), argc=1, argv=0x00007fffffffdec8) at libc_start_call_main.h:58:16
    frame #2: 0x00007ffff7c29e40 libc.so.6`__libc_start_main_impl(main=(hello-rust`main), argc=1, argv=0x00007fffffffdec8, init=0x00007ffff7ffd040, fini=<unavailable>, rtld_fini=<unavailable>, stack_end=0x00007fffffffdeb8) at libc-start.c:392:3
    frame #3: 0x000055555555b635 hello-rust`_start + 37

看到了亲切的libc-start.c。下面是Rust调用ASM,注意引入use core::arch::asm;    cargo build的时候会有两个警告。mut是变量,结果为8。

use core::arch::asm;


fn main() {
    let mut x: u32 = 5;
    unsafe {
        asm!(
            "add {0}, {1}",
            inout(reg) x,
            in(reg) 3
        );
    }
    println!("Result: {}", x);
}

.NET9调用Rust lib,如下命令

#cargo new hello-rust --lib 创建一个lib项目
#cd hello-rust
#vim src/lib.rs
#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
    a + b
}

把Cargo.toml添加一个Lib项

[lib]
crate-type = ["cdylib"]  # 或 "dylib" 根据需要选择

然后Cargo build --relese,生成的.so在hello-rust根目录的target/release下面名称规范:lib+项目名称.so,那么本例是:librust_hello.so。新建一个NET9项目,把librust_hello.so复制到bin/Debug/net9.0/,Program.cs如下:

using System;
using System.Runtime.InteropServices;
class Program
{
    [DllImport("libhello_rust.so", CallingConvention = CallingConvention.Cdecl)]
    public static extern int add(int a, int b);
    static void Main()
    {
       int result = add(10, 20);
       Console.WriteLine($"Result: {result}"); // 输出: Result: 30
    }
}

往期精彩回顾

.NET9现代化编程长啥样?

推荐两个.NET JIT顶级安全加密工具

42d3f38cf1b500495e3844656a0cb8a3.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值