什么是__libc_start_main和_start?

qnakjoqk  于 2023-01-04  发布在  其他
关注(0)|答案(1)|浏览(168)

从过去的几天我一直试图了解幕后发生了什么,当我们执行一个C程序。然而,即使在阅读了无数的帖子,我找不到一个详细和准确的解释相同。有人能帮我吗?

bnlyeluc

bnlyeluc1#

在编译和链接程序时,您通常会发现像这样的特殊名称用于特定用途。
请记住,这个答案是一般性的,而不是启动C环境的特定实现,您通常会有类似_start标签的东西,这将是可执行文件的 * 实际 * 入口点(从托管环境的Angular 来看)。
这可能位于某个目标文件或库中(如crt0.o用于C运行时启动代码),通常会由链接器自动添加到可执行文件中,类似于添加C运行时库的方式(a)。
用于启动程序的操作系统代码将类似于(显然是伪代码,错误检查比它应该具有的要少得多):

def spawnProg(progName):
    id = newProcess()                       # make process space
    loadProgram(pid = id, file = progName)  # load program into it
    newThread(pid, initialPc = '_start')    # make thread to run it

即使你自己在用C编写代码时创建了一个main,但这并不是事情真正开始发生的地方。甚至在你的主程序开始之前,还有一大堆事情需要做。因此,C启动代码的内容应该是这样的(最简单的):

_start:  ;; Weave magic here to set up C and libc.
    ;; Note this is example code for a mythical implementation,
    ;; intended to show how it could work. It is not specific
    ;; bound to any given implementation.
    call __setup_for_c       ; Set up C environment.
    call __libc_start_main   ; Set up standard library.
    call _main               ; Call your main.
    call __libc_stop_main    ; Tear down standard library.
    call __teardown_for_c    ; Tear down C environment.
    jmp  __exit              ; Return to OS.

"魔法的编织"是指为C程序准备好环境所需要的一切。这可能包括以下内容:

  • 设置静态数据(这应该被初始化为零,所以它可能只是一个内存块的分配,然后由启动代码清零-否则你需要在可执行文件中存储一个已经清零的内存块);
  • 在栈上准备argcargv,甚至准备栈本身(存在可用于C的特定调用约定,并且很可能操作系统在调用_start时 * 根本 * 不必设置栈,因为进程的需要是未知的);
  • 设置特定于线程的数据结构(比如每个线程的随机数生成器或错误变量);
  • 用其他方式初始化C库;等等。

只有当所有这些都完成后,才可以调用main函数。还有一些工作需要在main退出后完成,例如:

  • 调用atexit处理程序(您希望在退出时自动运行的东西,无论退出发生在哪里);
  • 从共享资源分离(例如,如果OS在关闭进程时没有自动这样做,则从共享内存分离);以及
  • 释放进程退出时未自动清除的任何其他资源,否则这些资源将挂起。

(a)例如,如果您正在编写不使用标准C库的程序,或者如果您想为底层工作提供自己的_start例程,那么许多链接器可能会被告知"不"这样做。

相关问题