• GDB 本地调试 Microsoft .NET Native(CoreRT) 原生编译的 C#/VB.NET PE可执行文件


    本人看了似乎国内基本没有几个人聊过这个方面的话题,那就本人来简单的聊一聊把,.NET Native 大家都知道它是 “微软公司提供的 .NET 技术开发平台的原生程序编译器”,按照大多数人喜欢的叫法,就叫AOT原生编译器把。

    但需警告,大多数的开发人员,AOT程序不意味着,代码执行的性能就能比,JIT即时编译的托管程序性能更强悍,一旦人们采用AOT编译程序,那就是放弃了JIT为每个目标机器CPU,利用CPU Instruction Set(指令集)高度优化硬件加速的特性,因为AOT要保证通用平台上的代码可执行,就不可以使用更为先进的 “CPU指令集” 操作数,否则老旧平台的CPU可能没法处理这样的指令而抛出 “CPU指令操作数错误” 的中断信号。

    而且还有一个很大的问题,AOT编译由于实现上的复杂性,就导致高度的 “Inline 内链” 代码变得相对于JIT编译更加困难,还需要多说吗?显然的如果代码到处都在要求 Inline,希望编译器尽可能优化,减少调用堆栈的开销,体积变大是一方面,但多数情况下,所获得优化效果微乎其微,令人感到无比的挫败。

    AOT编译另外引发的问题为:.NET Expression、.NET Emit 动态编程技术,变得不在可能,因为以微软当前实现的 AOT 原生编译器,并没有把 JIT/CLR 部分做进去,不能支撑.NET强大特性,另外也暂时放弃了.NET动态程序集载入/编译的能力,算是 .NET 开发平台的强大,削了一大截。

    但AOT程序带来了什么硬性优势?

    1、更快的可执行程序启动速度,客户端适用

    2、更低的内存功耗负载,低内存服务器适用

    3、更好的 C/C++ 动态链接库的兼容性,.NET CLR上执行某些特殊 C/C++ 动态链接库函数会导致 .NET CLR执行引擎崩溃,影响范围覆盖 Microsoft 全线 .NET 平台运行时(.NET Core CLR、.NET Framework[MSCorEE])

    而原生编译后的 .NET Native 可执行程序可以调用这些 C/C++ 动态链接库函数而不致崩溃,这是最令人满意的一点技术特性。

    4、玄学的性能,或许比托管跑的快,也可能比托管跑的慢,取决于多个因素。

         .NET/JIT即时编译对性能影响较大,编译后在执行相同的代码,.NET/AOT未必代码的执行效率会优于.NET/JIT即时编译的代码效率。

        .NET/AOT静态编译不要过度迷信它可能带来的性能提升,这个世界上最强的原生编译器就是汇编编译器,比如MASM,其次是中级BASIC编译器(1982年),再次就是C/C++,.NET/JIT动态编译的某些函数与C/C++语言编写的同功能函数,可执行代码性能超过不少的情况也是不少的,代码性能强不强还是人的因素占了很大成份。

    以下两个文章链接可以看一看:

    gdb 常用命令 - 知乎 (zhihu.com) # 可自行搜索GDB调试相关的命令说明。

    .NET CoreRT AOT原生程序编译使用的一些小建议_liulilittle的博客-CSDN博客_corert

    判定一个 native 程序二进制文件是否为:.NET CoreRT 编译_liulilittle的博客-CSDN博客

    先部署系统环境,以Ubuntu为例,需要 gdb、gcc 环境,因为调试需要链接 gcc 的动态共享库的符号表信息: 

    apt-get update && apt-get install lrzsz zip unzip libkrb5-dev libicu-dev screen iftop openssl libssl-dev libunwind8 iftop net-tools gcc gdb cmake curl wget -y

    .NET Native 原生编译的程序符号表,它的函数栈命名很有规范的,但缺点是不可以原生编译选项配置上面不得开启大量的删减调用堆栈符号表信息与优化裁切堆栈(优化过后的函数堆栈检索相对要麻烦很多,有些是根本看不懂的)

    补充:

    GDB有三种调试方法,dump、附加进程、命令行设置参数运行,我记得貌似不能载入 dump 的方式调试,因为 .Net Native 编译是开启了ASLR技术,从 dump 上面载入堆栈坏了没法在还原堆栈信息,当然人们可以自行实验的,.NET程序若非必要的情况下,基本不存在崩溃的问题,开发人员元在编写的程序代码时基本就已把异常捕获处理掉了。

    举例命名的符号规范一般为:(注意是C风格,而不是C++符号风格)

    例如:

    FCL/BCL Microsoft .NET运行时框架函数:

    一般性:命名空间 + 类名 + 函数名(基本是编译后的IL元数据美化为C风格的命名)

    System_Text_RegularExpressions_System_Text_RegularExpressions_RegexParser__ScanRegex System_Text_RegularExpressions_System_Text_RegularExpressions_RegexParser__ScanCharClass 

    Lambda(闭包类):

    特殊裁切优化过后的:b__296_02【只出现在.NET框架内部实现】其它格式与内嵌类格式差不多的,IL上面怎么编的 Lambda,编译出来的原生程序符号表就是什么样的,上面也说会被美化为C风格,但对审阅上是不存在障碍的。

    内部类:

    System_Net_Sockets_System_Net_CallbackClosure

    内嵌类

    System_Net_Sockets_System_Net_Sockets_Socket_CacheSet

    泛型类

    System_Net_Sockets_System_Net_Sockets_Socket_TaskSocketAsyncEventArgs_1

    用户工程:

    用户工程名 + 命名空间 + 类名 + 函数名(规则与上面相似)

    此为迭代器的原生符号,可以很清楚的看到存在两个实例构造器函数声明。

    ppp_SimpleJSON_JSONNode_Enumerator__get_IsValid ppp_SimpleJSON_JSONNode_Enumerator___ctor ppp_SimpleJSON_JSONNode_Enumerator___ctor_0 ppp_SimpleJSON_JSONNode_Enumerator__get_Current 

    查看更多 .NET Native 编译后的原生C#/VB程序的符号表信息:

    1、nm -D -C 可执行程序文件路径

    2、objdump -tT -C 可执行程序文件路径

  • 相关阅读:
    如何使用java雪花算法在分布式环境中生成唯一ID?
    Nginx R31 doc-17-debugging 调试
    【JavaWeb】Servlet身份验证过滤器
    【Linux基础知识点】内核、系统调用、Shell、指令集、微架构、硬件架构、用户态、内核态、进程、用户线程、内核线程
    VScode使用M5stack-c plus基于arduino-环境搭建
    linux下golang环境安装教程(学习笔记)
    《C++避坑神器·二十六》结构体报重定义错误问题和std::variant同时存储不同类型的值使用方式
    YOLOv7改进策略:一种新颖的可扩张残差(DWR)注意力模块,增强多尺度感受野特征,助力小目标检测
    Docker中出现bash: vim: command not found解决方案
    mysql主从复制与读写分离
  • 原文地址:https://blog.csdn.net/liulilittle/article/details/126388253