linux中的可执行二进制文件执行流程(ELF)

linux中的可执行二进制文件执行流程

1,常见名称缩写及概念。

缩写说明
ELF可执行文件和链接格式,在计算领域,可执行和可链接格式是可执行文件、目标代码、共享库和核心转储的通用标准文件格式。1 2
bssBSS段属于静态内存分配。指用来存放程序中未初始化的全局变量和未初始化的局部静态变量。未初始化的全局变量和未初始化的局部静态变量默认值是0,本来这些变量也可以放到data段的,但是因为他们都是0,所以为他们在data段分配空间并且存放数据0是没有必要的。
INTERP对于动态二进制文件,它包含运行时链接器 ld.so 的完整路径名。该段与 ELF 链接视图中的 .interp 段相同。

2,可执行文件执行流程分析

2.1,查看Linux内核在编译的时候是否设置成支持ELF格式的文件

cat /lib/modules/`uname -r`/build/.config | grep  CONFIG_BINFMT_ELF
或者
zcat /proc/config.gz | grep CONFIG_BINFMT_ELF

2.2,bash执行可执行程序

在shell上面编辑文件,vim hello.c,并输入如下内容:

# include<stdio.h>

int main()
{
	printf("\nhello gzhuflyer\n\n");
	return 0;
}

执行gcc hello.c -o hello生成可执行文件 hello。并在当前界面上执行 echo $$ 获取当前shell的进程号,打开另外一个shell界面,执行 strace -s 512 -p 8412 -f -o strace.log。数字 8412:为执行echo $$ 获取到的进程号。
strace.log为strace命令获取到的系统调用日志
strace.log日志
从日志中可以看到,shell实际上是调用 clone() 和 execve()两个系统调用,然后执行 ./hello可执行程序。

2.3,execve函数相关调用

当shell使用execve开始执行一个可执行程序的时候,Linux内核会进行如下相关函数调用。3 4
1,sys_execve 函数会调用 do_execve 函数在用户空间处理execve系统调用。
2,do_execve 函数会调用search_binary_handler函数打开可执行文件,并做一些准备工作。
3,search_binary_handler函数获取可执行文件的二进制执行类型,并返回一个响应句柄,当执行上文中的hello可执行程序时,讲返回 load_elf_binary函数。
4,load_elf_binary 函数加载用户可执行文件到内存,为程序申请所需要的内存,调用函数padzero将程序 bss段清零。
同时也检查可执行程序是否包含 INTERP 段。
5,如果可执行程序需要链接其他动态库,在编译成可执行程序的时候,编译器会为可执行程序创建 INTERP 段,并包含 INTERP 段的解释器程序路径,通常该解释器为 ld.so。
可以通过命令 readelf -p .interp hello 查看该解释器的路径。一般为 /lib64/ld-linux-x86-64.so.2。
6,当可执行程序包含 INNTERP 段时,load_elf_binaray 将调用 load_elf_interp 加载解释器。
7,load_elf_binary函数在最后会调用 start_thread(在 arch/x86/kernel/process_64.c)并将CPU使用权给到解释器或者用户程序。

2.4, ld.so 作用

在2.3中第5步,解释器ld.so会进行一系列操作,下面分析ld.so进行做了那些工作
1,分析用户可执行程序中 DYNAMIC 段,并查找程序依赖的库。
2,定位并加载依赖库,并分析依赖库中 DYNAMIC 段,从而确定这些依赖库是否还需依赖其他库。
3,进行重定向操作并绑定所需要的依赖库。
4,最后将控制权交给可执行程序。

3, Linux X86 程序在执行main函数前前后后

3.1,程序执行与函数调用

编写程序查看在main函数之前和程序退出之后有那些函数会被执行到。

#include <stdio.h>
#include <stdlib.h>

void preinit(int argc, char **argv, char **envp) {
	printf("%s\n", __FUNCTION__);
}

void init(int argc, char **argv, char **envp) {
	printf("%s\n", __FUNCTION__);
}

void fini() {
	printf("%s\n", __FUNCTION__);
}

__attribute__((section(".init_array"))) typeof(init) *__init = init;
__attribute__((section(".preinit_array"))) typeof(preinit) *__preinit = preinit;
__attribute__((section(".fini_array"))) typeof(fini) *__fini = fini;

void  __attribute__ ((constructor)) constructor() {
	printf("%s\n", __FUNCTION__);
}

void __attribute__ ((destructor)) destructor() {
	printf("%s\n", __FUNCTION__);
}

void my_atexit() {
	printf("%s\n", __FUNCTION__);
}

void my_atexit2() {
	printf("%s\n", __FUNCTION__);
}

int main() {
	atexit(my_atexit);
	atexit(my_atexit2);
}

下面是程序的执行结果
在这里插入图片描述
从上图中打印信息可以直观的看到main函数执行前后调用的函数。
下图是gdb设置端点并运行程序,也同样能查看到main函数执行前后流程。
在这里插入图片描述

参考文章链接


  1. https://en.wikipedia.org/wiki/Executable_and_Linkable_Format ↩︎

  2. http://manpages.courier-mta.org/htmlman5/elf.5.html ↩︎

  3. https://asm.sourceforge.net/articles/startup.html ↩︎

  4. http://s.eresi-project.org/inc/articles/elf-rtld.txt ↩︎

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: 这个报错信息是指在运行一个名称为cfw的可执行文件时,由于可执行文件格式错误而无法运行。通常情况下,这可能是由于系统与可执行文件不兼容、缺少依赖项或者文件本身已经损坏。 * 如何解决这个问题? - 首先,检查可执行文件的格式是否正确。可以使用file命令检查: $ file cfw cfw: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, not stripped 上述例子表示cfw是一个64位的Linux二进制文件。 如果格式正确,可以检查系统是否已经安装了必要的依赖项。如果缺少依赖项,可以下载并安装它们。 如果以上两种方法都无效,可能就需要重新编译代码并生成新的可执行文件了。 总的来说,这个问题出现的原因可能有很多,需要根据具体情况采取相应的解决方案。 ### 回答2: 这个错误提示意味着尝试执行文件并不是有效的可执行二进制文件。这可能是由于多种原因引起的,例如: 1. 文件损坏:文件可能已损坏或下载过程出现了错误,导致它不再是完整的可执行二进制文件。 2. 不兼容的体系结构:可执行文件可能是为其他体系结构编译的,不兼容当前的操作系统或硬件环境。 3. 没有运行权限:如果您尝试执行一个没有运行权限的文件,则会出现“无法执行二进制文件”错误。 为了解决这个问题,您可以尝试以下一些方法: 1. 检查文件是否完整:尝试重新下载文件并检查其完整性,确保它没有损坏或缺失。 2. 检查文件类型:使用命令“file filename”来查看文件的类型和体系结构,确保它是适用于当前系统的有效可执行文件。 3. 设定执行权限:使用命令“chmod +x filename”来为文件添加执行权限,确保可以运行该文件。 4. 安装缺失的库:如果可执行文件需要依赖其他库文件,那么需要先安装这些库文件,以确保可执行文件可以正常运行。 总之, 您需要仔细检查文件的完整性,兼容性和权限,以确保该文件可以正常运行。 ### 回答3: 这个问题很可能是因为可执行文件对应平台不匹配导致的。在Linux系统,可执行文件需要与当前运行的系统架构匹配,在32位系统上不能运行64位可执行文件,在64位系统上不能运行32位可执行文件。因此,当我们尝试运行一个不匹配的可执行文件时,就会出现类似于“bash: ./cfw:无法执行二进制文件: 可执行文件格式错误”的错误信息。 另外,一些其他的原因也可能引起这个问题,比如可执行文件没有执行权限或者文件本身损坏等。 解决这个问题的方法有以下几种: 1. 确认文件对应平台是否匹配。在Linux系统,可以使用“uname -a”命令查看当前系统架构,并检查可执行文件是否有与之对应的版本。 2. 确认是否有执行权限。在Linux系统,可以使用“ls -l filename”命令查看文件权限,使用“chmod +x filename”命令赋予可执行权限。 3. 确认文件是否损坏。在Linux系统,可以使用“file filename”命令查看文件类型和属性,使用“md5sum filename”命令计算文件的MD5值,检查文件是否与原始文件一致。 4. 如果可执行文件是从其他系统拷贝过来的,可以尝试重新编译一下程序,以确保在当前系统下可正常运行。 除此之外,在使用Linux系统时,我们也应该注意一些细节问题,比如不同版本系统之间的差异、文件系统类型、用户权限等等,这些都可能会影响可执行文件执行情况。如果问题无法解决,可以通过搜索或向社区求助等方式获取更多的帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gzhuflyer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值