加载器和C运行时初始化的角色之间的差异

tap*_*and 12 c runtime loader

我正在阅读此链接中有关C运行时初始化的角色:http://www.embecosm.com/appnotes/ean9/html/ch05s02.html

它表示运行时初始化执行诸如设置堆栈之类的任务,并且在更详细的页面中,它还说它用零初始化bss段.在其他一些地方,我还读到它初始化数据和其他一些段.

这在我的脑海中产生了一个疑问,那就是装载机的功能呢?因为其中一些任务也是装载机的责任.

所以,我的问题:

  1. 运行时初始化或c运行时实际上做了什么?
  2. 装载机实际上做了什么?

编辑

好的,如果该链接具体描述了嵌入式系统的运行时初始化的作用,那么它对普通系统有什么作用.据我所知,运行时初始化将只调用main,而没有其他工作留给它.

dho*_*dho 8

  1. 运行时初始化或c运行时实际上做了什么?

Wikipedia将运行时库定义为:

编译器用于调用运行时环境的某些行为的一组低级例程,通过将对运行时库的调用插入已编译的可执行二进制文件中.

对于C程序,运行时库在引导程序之外几乎没有什么可做的.编译器调用C运行时来引导各种环境事物,然后通过调用基本上将控制权移交给用户main.

鉴于您的问题评论中的回答,您可能已经发现程序针对其环境进行自举的过程因目标环境的数量而异.鉴于现在和过去C支持的平台和操作系统的数量,没有可能的方法来枚举C运行时工作或当前工作的所有方式.

每个C库都有自己的C运行时,每个支持C的环境都可能有不同的引导问题和要求.这些要求在很大程度上取决于操作系统或硬件的功能以及C实现的完整性.但是,我可以回答一些C运行时通常在您可能熟悉的环境中执行的操作.

  • 由于C运行时负责调用main,因此注册的调用函数atexit(3)将由C运行时负责.

  • 解析和调用任何构造函数/析构函数接口(_init,_fini,等)

  • 初始化并调用实时加载器(负责解析和加载在链接时注册并在运行时加载的动态共享对象).

  • 正确处理分离线程的退出.

  • 初始化和传球argc,并argv 进入程序main.

  • 定义和初始化各种C库全局符号.例如,它errno为环境设置正确(现代系统定义errno为线程安全,因此它需要存在于TLS中).environ是另一个在调用之前需要初始化的全局符号main.

  • 就此而言,C运行时需要设置TLS.

  • 吨更多.

您可能有兴趣查看运行时glibc实现,可在"csu"(C启动)目录中找到.(此目录之外有一些特定于机器的部分.)

不同的系统会有不同的要求.正如您所读到的,嵌入式系统可能对运行时有更多的工作,因为它们可能负责从寄存器初始化到程序加载和执行(任何内核都不提供)的任务.鉴于嵌入式目标上足够复杂的独立项目,"C运行时"和"内核"之间的区别可能会变得模糊.

现在:

  1. [a]装载机实际上做了什么?

有许多类型的加载器,也取决于运行时环境.对于带有EEPROM的小型嵌入式环境,加载器可能是一些固件,可以开始执行在地址0处找到的任何内容.您可能还将自己视为加载器,手动将二进制文件写入EEPROM.

在现代操作系统中,有许多装载机.

  1. 引导程序.从历史上看,这些操作方式是BIOS选择引导设备,查看地址,将512字节数据读入内存,然后从那里开始执行.我已经离开了这个世界一段时间了,所以我不确定与EFI/UEFI有什么不同,除了它们足够完整(和复杂)的引导环境.

  2. 内核.当你执行一个程序时,大量的东西正在引擎盖下进行.假设您在类似Unix的操作系统中从shell运行程序,加载过程可能会遵循以下内容:

    • 您的shell尝试在您配置的环境中的某处查找二进制文件PATH.这是通过向内核发出许多系统调用来解析不同路径序列下的文件名来完成的.
    • 假设找到了文件,shell通常会fork(2)execve(2).该fork(2)调用使内核创建一个新进程; 该execve(2)调用将克隆的二进制文件替换为新的二进制文件.
    • 内核从其存储介质(磁盘,网络,内存等)读取文件的第一页,并尝试找出如何执行它.
      • 如果它是ELF二进制文件,它可以从二进制文件的标题中确定.然后,内核根据ELF节标题中指定的偏移量,将二进制文件的部分加载到内存中,为堆栈设置映射区域等等,然后根据条目地址(也是ELF标题的一部分)开始执行.此入口点可能_start是C运行时的一部分.
      • 如果它不是ELF二进制文件,它仍然可以通过解释器执行.内核将尝试从文件的开头解析解释器(例如#!/bin/bash),解析它并执行它.最终它会找到一个ELF可执行文件,否则它将失败.
    • 如上所述,内核可能会开始执行二进制文件_start.
    • Eli Bendersky对此进行了更全面的论述,题为" 如何在Linux上运行静态链接的程序 ".
  3. 运行时加载器/动态链接器/无论你想要什么叫它们.我将向您介绍" Linux动态库剖析 "一文,以获取有关这些工作原理的信息.当然,dlopen(3)/ dlsym(3)/ dlclose(3)/ dlerror(3)函数集只是用于与动态加载器交互的API.我强烈建议您阅读这些界面的手册页,以便深入了解Linux动态加载程序支持的功能集,以及加载程序的功能.