可执行文件以及其加载过程

2024-05-29 06:04

本文主要是介绍可执行文件以及其加载过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在计算机系统中,可执行文件是指包含机器代码的文件,计算机可以直接执行这些代码以运行特定的任务或程序。不同的操作系统对可执行文件有不同的定义和处理方式。本文将探讨常见操作系统中的可执行文件格式及其加载过程,特别是以ELF(Executable and Linkable Format)文件格式为例,深入了解其动态库加载过程。同时,我们还将介绍静态链接和动态链接的区别。

可执行文件简介

可执行文件是程序经过编译和链接后生成的文件,其中包含了机器指令、数据和必要的元信息,使得操作系统能够加载并执行该程序。不同操作系统使用不同的可执行文件格式:

  • Windows:使用PE(Portable Executable)格式,文件扩展名通常为.exe.dll
  • macOS:使用Mach-O(Mach Object)格式,文件扩展名通常为.app.dylib
  • Linux:使用ELF(Executable and Linkable Format)格式,文件扩展名通常为没有特定的要求,但共享库使用.so

ELF 文件格式

ELF文件格式是用于Linux和其他Unix-like操作系统的标准文件格式,适用于可执行文件、目标文件和共享库。ELF文件由多个部分组成,每个部分包含不同类型的数据,主要结构包括:

  1. ELF Header(ELF头)

    • 包含了文件类型、架构、入口点等基本信息。
    • 关键字段包括e_ident、e_type、e_machine、e_entry等。
  2. Program Header Table(程序头表)

    • 描述了文件中各个程序段的位置和属性。
    • 关键字段包括p_type、p_offset、p_vaddr、p_filesz等。
  3. Section Header Table(节头表)

    • 描述了文件中各个节的位置和属性。
    • 关键字段包括sh_name、sh_type、sh_flags、sh_addr等。
  4. Sections(节)

    • 各种类型的数据段,如代码段(.text)、数据段(.data)、只读数据段(.rodata)、未初始化数据段(.bss)。

ABI(Application Binary Interface)

ABI是定义应用程序在特定操作系统和硬件架构上运行的二进制接口的约定。它包括数据类型大小和对齐、函数调用约定、系统调用接口、二进制文件格式等。不同的操作系统和架构有各自的ABI规范,确保应用程序的二进制兼容性。

  • Windows ABI

    • 使用PE文件格式。
    • 定义了多种调用约定,如stdcall、cdecl、fastcall。
    • 使用WinAPI作为主要的系统调用接口。
    • 常用C标准库实现包括MSVCRT。
  • macOS ABI

    • 使用Mach-O文件格式。
    • 使用System V AMD64 ABI调用约定。
    • 使用POSIX标准系统调用接口,扩展了一些特有的系统调用。
    • 使用libSystem作为C标准库实现。

ELF 文件格式在 ABI 中的作用

ELF文件格式是ABI的重要组成部分,规定了二进制文件的结构,确保操作系统能够正确加载和执行应用程序。具体而言:

  • 标准文件格式:定义了可执行文件、目标文件和共享库的标准文件格式,确保二进制兼容性。
  • 程序头表和节头表:定义了二进制文件中各个段和节的布局。
  • 符号表和重定位信息:提供了符号解析和重定位的必要信息。
  • 动态链接:支持动态链接,通过动态库实现代码共享和内存节省。
  • 系统调用和C库接口:通过ELF文件描述库文件的结构,确保应用程序与操作系统接口一致。

静态链接与动态链接

在编译和链接过程中,静态链接和动态链接是两种不同的方法,用于将程序代码和库代码组合在一起。

静态链接
  • 定义:在编译时将所有使用到的库函数代码复制到最终的可执行文件中。
  • 优点
    • 运行时不需要外部库,所有代码都在一个可执行文件中。
    • 可以避免由于库版本变化导致的兼容性问题。
  • 缺点
    • 可执行文件体积较大,因为包含了所有依赖的库代码。
    • 无法在运行时更新或替换库,更新程序需要重新编译整个可执行文件。
动态链接
  • 定义:在运行时将库代码加载到内存中,并与可执行文件链接。
  • 优点
    • 可执行文件体积较小,因为库代码不包含在可执行文件中。
    • 可以在运行时更新或替换库,更新程序只需要替换库文件而不需要重新编译整个可执行文件。
    • 多个程序可以共享同一个库的实例,节省内存。
  • 缺点
    • 运行时依赖于外部库文件,缺少库文件或库版本不兼容可能导致程序无法运行。
    • 动态链接和符号解析会增加一些运行时开销。

ELF 动态库加载过程

在Linux系统中,加载共享库(.so文件)的过程涉及多个步骤,包括解析、映射、符号解析和重定位。以下是详细的加载过程:

  1. 启动程序

    • 操作系统首先加载可执行文件到内存中,读取其ELF头部信息,找到动态段(.dynamic)。
  2. 加载器(Loader)

    • 加载器读取程序头表,找到动态段信息,指示需要加载哪些共享库。
  3. 查找共享库

    • 加载器根据动态段信息查找共享库,路径包括DT_RUNPATH、LD_LIBRARY_PATH和系统默认路径。
  4. 映射共享库

    • 使用mmap将共享库的各个段映射到进程的虚拟地址空间。
  5. 解析符号

    • 加载器解析共享库中的符号,确保所有符号都能在内存中找到。
  6. 重定位(Relocation)

    • 处理重定位条目,调整代码和数据指针,使其指向正确的内存地址。
  7. 调用初始化函数

    • 调用共享库的初始化函数,完成库的初始化。

动态链接器(Dynamic Linker)

动态链接器(如ld.sold-linux.so)是负责加载和链接共享库的程序。在程序启动时,操作系统会先加载动态链接器,然后由动态链接器负责加载所有需要的共享库并进行符号解析和重定位。

示例:动态库加载过程

假设有一个可执行文件app依赖于共享库libfoo.solibbar.so,加载过程如下:

  1. 启动app

    • 操作系统加载app的ELF文件,读取其程序头表,找到动态段。
    • 动态段指示需要加载libfoo.solibbar.so
  2. 查找libfoo.solibbar.so

    • 加载器按顺序查找LD_LIBRARY_PATHDT_RUNPATH和系统默认路径,找到共享库文件。
  3. 映射共享库

    • 使用mmap将共享库的各个段映射到进程的虚拟地址空间。
  4. 解析符号

    • 动态链接器解析共享库中的符号,确保所有符号都能在内存中找到。
  5. 重定位

    • 处理重定位条目,更新内存中的地址使其指向正确的目标。
  6. 调用初始化函数

    • 调用共享库的初始化函数,完成库的初始化。

总结

可执行文件是操作系统运行程序的核心文件类型,不同操作系统有不同的可执行文件格式和加载方式。ELF文件格式是Linux和其他Unix-like操作系统的标准文件格式,通过详细的结构和动态链接支持,实现了程序的高效加载和运行。静态链接和动态链接是两种不同的链接方法,各有优缺点。了解这些技术和过程,对于系统编程和跨平台开发至关重要。

这篇关于可执行文件以及其加载过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1012848

相关文章

C/C++的编译和链接过程

目录 从源文件生成可执行文件(书中第2章) 1.Preprocessing预处理——预处理器cpp 2.Compilation编译——编译器cll ps:vs中优化选项设置 3.Assembly汇编——汇编器as ps:vs中汇编输出文件设置 4.Linking链接——链接器ld 符号 模块,库 链接过程——链接器 链接过程 1.简单链接的例子 2.链接过程 3.地址和

加载资源文件失败

背景         自己以前装了一个海康的深度学习算法平台,试用期是一个月,过了一个月之后,因为没有有效注册码或者加密狗的支持了导致无法使用,于是打算卸载掉,在卸载一个软件的时候,无论是使用控制面板还是软件自带的卸载功能,总是卸载不掉,提示“加载资源文件失败”。该软体主要包括以下两部分: 用自带卸载功能卸载的时候分别提示如下:     用控制面板卸载的时候反应很慢,最后也是提示这个

vue同页面多路由懒加载-及可能存在问题的解决方式

先上图,再解释 图一是多路由页面,图二是路由文件。从图一可以看出每个router-view对应的name都不一样。从图二可以看出层路由对应的组件加载方式要跟图一中的name相对应,并且图二的路由层在跟图一对应的页面中要加上components层,多一个s结尾,里面的的方法名就是图一路由的name值,里面还可以照样用懒加载的方式。 页面上其他的路由在路由文件中也跟图二是一样的写法。 附送可能存在

Pycharm配置conda环境(解决新版本无法识别可执行文件问题)

引言: 很多小伙伴在下载最新版本的pycharm或者更新到最新版本后为项目配置conda环境的时候,发现文件夹目录中无法显示可执行文件(一般为python.exe),以下就是本人遇到该问题后试验和解决该问题的一些方法和思路。 一般遇到该问题的人群有两种,一种是刚入门对pycharm进行conda环境配置的小白(例如我),不熟悉相关环境配置的操作和过程,还有一种是入坑pycharm有段时间的老手

VS2012加载失败

1、通过命令提示行工具进入VS安装目录下的Common7\IDE 2、执行devenv.exe /setup /resetuserdata /resetsettings 3、重启VS

mysql中存储过过程和游标的联合使用

1.SQL如下: DELIMITER //DROP PROCEDURE IF EXISTS PrintAllEmployeeNames5;CREATE PROCEDURE PrintAllEmployeeNames5()BEGINDECLARE error_count INT DEFAULT 0;DECLARE num INT ;DECLARE done INT DEFAULT

【服务器08】之【游戏框架】之【加载主角】

首先简单了解一下帧率 FixedUpdate( )   >   Update( )   >   LateUpdate( ) 首先FixedUpdate的设置值 默认一秒运行50次 虽然默认是0.02秒,但FiexedUpdate并不是真的0.02秒调用一次,因为在脚本的生命周期内,FixedUpdate有一个小循环,这个循环也是通过物理时间累计看是不是大于0.02了,然后调用一次。有

Web容器启动时加载Spring分析

在应用程序web.xml中做了以下配置信息时,当启动Web容器时就会自动加载Spring容器。 [java]  view plain copy print ? <listener>          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

Android 10.0 系统开机重启桌面时钟小部件widget加载慢解决方案

1.前言 在10.0的系统rom产品定制化开发中,在Launcher3桌面系统默认会有时钟widget小部件显示在首屏的,但是发现在开机过程 中会显示的好慢,等进入桌面了 还没显示,所以接下来分析下相关的源码流程,来实现相应的功能 2.系统开机重启桌面时钟小部件widget加载慢解决方案的核心类 frameworks\base\services\appwidget\java\com\andr

spring的bean加载

我们经常使用Spring,并且也都了解其大概原理。我想我们一定会对Spring源码的解读有迫切的渴望。 我也如此。所以,我打算阅读一下Spring的源码。再此之前,我也为此准备了很多。包括,去复习熟练java反射,理解常用的设计模式。当然,这些复习笔记也会在今后的复习中顺便记录在我的csdn博客。(当然,可能写的不好,也可能理解不正确(可以一起交流嘛)。但是乐于分享总归是好的。)