首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >_start是否调用我的程序的主要功能和其他必要的设置函数?

_start是否调用我的程序的主要功能和其他必要的设置函数?
EN

Stack Overflow用户
提问于 2020-09-09 07:21:36
回答 3查看 466关注 0票数 2

我正在读一本描述装载机工作原理的教科书:

加载程序运行时,它会将可执行对象文件的块复制到代码和数据段中。接下来,加载程序跳转到程序的入口点,它始终是_start函数的地址。_start函数调用系统启动函数__libc_start_main

从这个问题的答案开始?中,我们得到了关于执行流程的以下伪代码:

代码语言:javascript
运行
复制
_start:
   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

我的问题是:

  1. 我使用objdump检查程序的汇编代码,发现_start只调用__libc_start_main,如下图所示:

其他的函数如call __setup_for_c_main等呢?特别是我的程序的main函数,我看不出它是如何被调用的。那么关于执行流的伪代码是正确的吗?

  1. __libc_start_main设置标准库是什么意思?为什么需要设置标准库?在加载程序时,标准库不是只需要由动态链接器链接吗?
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-09-09 07:46:50

伪代码不是代码;) _libc_start_main()可以调用应用程序的main(),因为链接器将固定main()的地址。编译器生成的代码进行初始化的顺序可能很有趣,但您不应该假设它在一个编译器到另一个编译器之间是相同的,甚至是从一个版本到另一个版本。如果你能避免的话,最好不要依赖于以一种特定的方式做的事情。

至于需要初始化什么--像glibc这样的标准C库非常复杂,很多东西都需要初始化。举一个例子,必须设置内存分配器的块表,这样malloc()就不会从内存分配的随机模式开始。

票数 3
EN

Stack Overflow用户

发布于 2020-09-09 07:45:39

  1. 链接答案中描述的其他函数调用给出了需要发生什么的概要;GNU库中的实际实现细节是不同的,要么使用“构造函数”(_dl_start_user),要么在__libc_start_main中显式地。__libc_start_main还负责调用用户的main,这就是为什么在您的反汇编中没有看到它被调用的原因--但是它的地址是传递的(参见lea,即callq)。__libc_start_main还负责程序退出,从不返回;这是hltcallq之后出现的原因,如果函数返回,这将使程序崩溃。
  2. 这个图书馆现在需要相当多的设置:
代码语言:javascript
运行
复制
- some of its own relocation
- thread-local storage setup
- pthread setup
- destructor registration
- vDSO setup (on Linux)
- ctype initialisation
- copying the program name, arguments and environment to various library variables

等等,请参阅x86-64特定的sysdeps/x86_64/start.S和泛型csu/libc-start.ccsu/init-first.cmisc/init-misc.c等.

票数 3
EN

Stack Overflow用户

发布于 2020-09-09 07:48:34

其余的函数如call __setup_for_c、_main等呢?

这些只是虚构的、可读的名字,用在链接的答案中,更好地传递答案的含义。

它是如何被调用的

您的标准库实现没有提供名为__setup_for_c_main的函数,因此它们不存在,因此不会被调用。每个实现都可以为函数选择不同的名称。

关于执行流的伪代码正确吗?

是的--你使用的“psuedo代码”一词可以推断出你知道它不是真正的代码。

__libc_start_main设置标准库是什么意思?

它的意思是一个名为__libc_start_main的符号。__libc_start_main是一个函数,它初始化所有标准库,并在glibc中运行main。它初始化libc、p线程、atexit并最终运行mainglibc是开源的,所以看看它吧。

为什么需要设置标准库?

因为它是以依赖于它的方式写的。最简单的是,当你写的时候:

代码语言:javascript
运行
复制
 int var = 42; // variable with static storage duration
 int main() {
     return var == 42;
 }

(假设优化器没有启动),那么42值必须写入var 的内存中,然后才能执行 main。因此,必须在main之前执行某些操作,并将42实际写入var的内存中。这是为什么某些东西必须在main之前执行的最简单的例子。许多地方都使用全局变量,所有这些变量都需要设置,例如,glibc中的一个名为名字的变量保存程序名称-- 所以有些代码需要实际上询问环境或内核程序的名称,并将字符串的值(并可能解析)存储到全局变量中(如果在退出时动态分配该字符串,也要记住该字符串的free() )。有些代码“必须这样做”--而且该代码处于标准库初始化阶段。

还有更多的情况--在C++和其他语言中有构造函数,还有gcc GNU扩展__attribute__((__constructor__)).init/.preinit部分--它们都在main之前执行。而且析构函数必须在exit上执行,而不是在_exit上执行--因此在atexit之前初始化atexit,并且根据实现的不同,所有析构函数都可以在其中注册。

环境需要初始化,可能需要堆栈和更多的东西。线程局部变量只需要分配给当前线程,这样当您pthread_create另一个线程时,它们就不会被非线程局部变量复制。

当程序加载时,这个标准库不是只需要由动态链接器链接吗?

是的--当程序加载时,标准库就会被链接起来。编译器在生成程序时使用crt代码在程序中包含一些启动代码,例如调用__libc_start_main

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63806608

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档