note1

file查看相应的文件格式

file press_test

# output
press_test: ELF 64-bit LSB pie executable, 
x86-64, 
version 1 (SYSV), 
dynamically linked, 
interpreter /lib64/ld-linux-x86-64.so.2, 
for GNU/Linux 3.2.0, 
BuildID[sha1]=b75db323b85399ea6eb40563efff1accfa048a9b, 
not stripped

整体解读

press_test 是一个 64 位、适用于 Linux 系统、可执行、动态链接 的程序文件。

逐部分解释

  1. press_test:

    • 这是被检查的文件名。
  2. ELF 64-bit LSB pie executable,

    • ELF (Executable and Linkable Format): 这是 Unix-like 系统(包括 Linux)上最常用的可执行文件、目标代码、共享库和核心转储文件的标准格式。
    • 64-bit: 表明该程序是为 64 位架构(如 x86-64)编译的。
    • LSB (Least Significant Byte): 表示文件使用小端字节序(Little Endian)存储数据。这是现代 Intel 和 AMD 处理器(x86 系列)的标准。
    • pie executable (Position Independent Executable): 表明这是一个位置无关的可执行文件。
      • 重要性: PIE 是现代安全措施的一部分。它使得程序在内存中加载时的基地址是随机的(配合 ASLR - Address Space Layout Randomization),这使得攻击者更难预测内存地址,从而阻碍了某些类型的缓冲区溢出攻击。
  3. x86-64, version 1 (SYSV),

    • x86-64: 指定了目标机器架构(即 64 位 Intel/AMD 处理器)。
    • version 1 (SYSV): 通常指的是 ELF 规范的版本。SYSV 是 System V Unix 的缩写,表示该文件遵循 System V 风格的 ELF 标准。
  4. dynamically linked,

    • 动态链接: 意味着该程序在运行时需要加载外部的共享库(如 libc.so.6)。与静态链接相比,动态链接节省了磁盘空间和内存,但依赖于系统环境中存在这些库。
  5. interpreter /lib64/ld-linux-x86-64.so.2,

    • 解释器/加载器: 这是 Linux 动态链接器(Dynamic Loader)。当系统要运行一个动态链接的程序时,内核会先加载这个解释器。解释器负责找到、加载所有必需的共享库,并将控制权交给主程序。
  6. for GNU/Linux 3.2.0,

    • 表示该程序是为运行在至少是 GNU/Linux 3.2.0 内核的系统上而编译或设计的。
  7. BuildID[sha1]=b75db323b85399ea6eb40563efff1accfa048a9b,

    • 构建 ID: 这是一个唯一的标识符,用于追踪程序的构建版本。它允许调试工具(如 GDB)准确地找到与该可执行文件匹配的调试符号文件(如果它们被单独存储)。
  8. not stripped

    • 未剥离: 意味着该可执行文件中仍然包含调试信息(例如函数名、变量名、行号等符号)。
      • 含义: 这种文件体积通常比“已剥离”的文件大,但对于调试和逆向工程非常有用。在生产环境中部署时,程序通常会被“剥离”以减小体积并略微提高安全性(因为它移除了调试符号)。

代码与目标文件映射及 File Header

I. C 代码元素到目标文件的映射

C 代码元素 存储类别/特性 目标文件段 (Section) 目的
函数代码 (main, func) 代码指令 .text 存储可执行机器码。
全局/静态变量 (已初始化) 静态存储期,有初始值 .data 存储程序加载时需载入的非零数据。
全局/静态变量 (未初始化/0) 静态存储期,无初始值 .bss 记录所需空间,运行时由系统初始化为 0。
局部自动变量 (int a) 自动存储期 运行时堆栈 (Stack) 动态分配,不占用目标文件空间。

II. File Header (ELF 格式)

文件头是目标文件的地图和身份证明,包含解析和加载文件所需的基本元数据。

类别 关键信息点 作用
身份与结构 魔数、位数 (32/64)、字节序 (LSB/MSB)、目标架构 (x86-64)。 确认文件类型,确定硬件环境和数据读取方式。
文件类型 文件类型 (ET_REL - 目标文件, ET_EXEC - 可执行文件, ET_DYN - PIE/共享库)。 确定文件用途(编译、链接或执行)。
执行信息 入口点地址 (Entry Point)。 操作系统加载程序后开始执行指令的地址。
布局指针 指向程序头表段表的偏移量及表项数量。 告知加载器和链接器文件内部关键结构的位置和规模。

区别

虽然 file 命令的输出信息大部分来自文件头,但两者之间仍有区别:

  1. 输出粒度:文件头: 是一个精确的二进制数据结构,包含偏移量、大小、版本号等技术细节file 命令: 是对这些技术细节的人类可读的总结和解释。它将二进制数据(如架构代码 0x3E)转换为易懂的文本(x86-64)。
  2. 信息来源:某些信息(如 interpreter /lib64/ld-linux-x86-64.so.2 或是否 stripped)并不直接存在于 ELF 文件头结构本身,而是存在于文件头所指向的程序头表段表中。file 命令会根据文件头提供的指针去读取这些后续结构,然后将解析结果包含在输出中。

代码元素与目标文件段的具体对应关系

int global_init_var = 54;
int global_uinit_var;

void func1(int i){
    printf("%d\n", i);
}

int main(void){
    static int static_var = 85;
    static int static_var2;
    int a = 1;
    int b;
    func1(static_var + static_var_2 + a + b);
    return 0;
}

根据提供的 C 代码和目标文件结构图,程序中的元素根据其存储类别和初始化状态,被编译器分配到目标文件的不同段中。

1. 代码段 (.text section)

.text 段用于存储程序的可执行机器指令。

  • 对应元素:
    • void func1 ( int i ) { ... } (函数代码)
    • int main (void) { ... } (主函数代码)
  • 总结: 所有函数体内的指令都被放置在代码段。

2. 已初始化数据段 (.data section)

.data 段用于存储具有静态存储期(全局变量或 static 变量)且已被非零值初始化的数据。这些值在程序加载时从文件载入内存。

  • 对应元素:
    • int global_init_var = 84; (已初始化的全局变量)
    • static int static_var = 85; (已初始化的局部静态变量)
  • 总结: 任何在编译时拥有确定初始值(非零)的全局或静态数据都位于此段。

3. 未初始化数据段 (.bss section)

.bss 段用于存储具有静态存储期但未初始化或初始化为零的数据。该段不占用目标文件的磁盘空间,但记录了运行时所需的内存大小。程序加载后,系统会将其初始化为零。

  • 对应元素:
    • int global_uninit_var; (未初始化的全局变量)
    • static int static_var2; (未初始化的局部静态变量)
  • 总结: 所有未初始化或隐式初始化为零的全局或静态数据都位于此段。

4. 运行时存储(不在目标文件段中)

局部自动变量(没有 static 修饰符的函数内变量)在程序执行时才动态分配。它们不占用目标文件中的任何固定段。

  • 对应元素:
    • int a = 1;
    • int b;
  • 存储位置: 这些变量在函数调用期间位于运行时堆栈 (Stack) 上。

关键原则

核心区别在于变量的生命周期初始化状态

  • 生命周期长(静态存储期): 对应 .data.bss 段。
  • 生命周期短(自动存储期): 对应运行时堆栈。
  • 已初始化且非零: 放入 .data 段。
  • 未初始化或零: 放入 .bss 段。
posted @ 2025-11-26 17:38  phrink  阅读(3)  评论(0)    收藏  举报