a.out的文件结构

本文解析了程序编译和链接过程中的关键概念,包括.text、.data和.bss段的作用及区别,以及a.out文件结构等内容。通过实例对比了初始化与未初始化变量在编译过程中的不同处理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.程序编译连接之后的位置分配

如果不指定编链后的文件名,linux会自动产生一个a.out的文件,大概是汇编输出的意思,实际上这是链接之后的输出,而不是汇编之后的输出,之所以这样是个历史遗留问题。

a.out文件除去魔术数字等东西,我们最关心的是程序中各个段的分配。一个程序文件一般会分成这样几个区域:

text,data,bss段


text指的是程序文本文件
data指的是所有的经过初始化后的全局变量和静态变量
bss段则是未经初始化的全局变量和静态变量,但是bss段不增加目标文件的大小,一般只是记录bss段的大小。


由const修饰的变量将存放于text文件之中。局部变量放在栈中。


例如
char a[100];
char b[100]= {100};

static int c;
static int d;
const int e = 5000;

void foo()

{

static int tmp1;

static int tmp2 = 1;

char i;

}

int main()
{
int i;
printf("hello,world!/n");

。。。。。。
}


此程序中,a[100],c,tmp1位于bss段,它们没有被初始化。
b[100],d,tmp2 位于data段,因为他们是已经初始化后的全局变量;
e 和main函数位于text文件中,e是const修饰的变量。
i则位于栈中。


在linux环境下,size a.out就可以查看各个部分的大小

 
2.a.out的结构-来自freebsd文档

一个二进制可执行(或其他相关)文件包括以下7个部分(sections)

exec header: 放有用来被操作系统载入到内存和执行,以及被ld用来进行和其他文件合并的参数。这个部分是托管的。
text segment: 放置机器码和相关数据,在程序执行的时候被载入内存,并且只读
data segment: 放置已初始化数据,被载入到内存中可写位置
text relocations: 放置编译相关的一些数据,现在不太明白
data relocations: 跟text relocation字段差不多,只不过是针对data segment的。
symbol table: 保存了命名变量以及函数符号的记录;被编译器用来在二进制文件中交叉引用这些命名变量或者函数的地址
string table: 保存对应符号名称的字符串

 

3.bss段和.data段的区别

【例一】

用cl编译两个小程序如下:

程序1:

int ar[30000];
void main()
{
    ......
}


程序2:

int ar[300000] =  {1, 2, 3, 4, 5, 6 };
void main()
{
    ......
}


发现程序2编译之后所得的.exe文件比程序1的要大得多。当下甚为不解,于是手工编译了一下,并使用了/FAs编译选项来查看了一下其各自的.asm,发现在程序1.asm中ar的定义如下:

_BSS SEGMENT
     ?ar@@3PAHA DD 0493e0H DUP (?)    ; ar
_BSS ENDS


而在程序2.asm中,ar被定义为:

_DATA SEGMENT
     ?ar@@3PAHA DD 01H     ; ar
                DD 02H
                DD 03H
                ORG $+1199988
_DATA ENDS


区别很明显,一个位于.bss段,而另一个位于.data段,两者的区别在于:全局的未初始化变量存在于.bss段中,具体体现为一个占位符;全局的已初始化变量存于.data段中;而函数内的自动变量都在栈上分配空间。.bss是不占用.exe文件空间的,其内容由操作系统初始化(清零);而.data却需要占用,其内容由程序初始化,因此造成了上述情况。

【例二】

编译如下程序(test.cpp):
#include <stdio.h>

#define LEN 1002000

int inbss[LEN];
float fA;
int indata[LEN]={1,2,3,4,5,6,7,8,9};
double dbB = 100.0;

const int cst = 100;

int main(void)
{
    int run[100] = {1,2,3,4,5,6,7,8,9};
    for(int i=0; i<LEN; ++i)
        printf("%d ", inbss[i]);
    return 0;
}

命令:cl /FA  test.cpp 回车 (/FA:产生汇编代码)
产生的汇编代码(test.asm):
    TITLE    test.cpp
    .386P
include listing.inc
if @Version gt 510
.model FLAT
else
_TEXT    SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT    ENDS
_DATA    SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA    ENDS
CONST    SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST    ENDS
_BSS    SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS    ENDS
_TLS    SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS    ENDS
FLAT    GROUP _DATA, CONST, _BSS
    ASSUME    CS: FLAT, DS: FLAT, SS: FLAT
endif
PUBLIC    ?inbss@@3PAHA                    ; inbss
PUBLIC    ?fA@@3MA                    ; fA
PUBLIC    ?indata@@3PAHA                    ; indata
PUBLIC    ?dbB@@3NA                    ; dbB
_BSS    SEGMENT
?inbss@@3PAHA DD 0f4a10H DUP (?)            ; inbss
?fA@@3MA DD    01H DUP (?)                ; fA
_BSS    ENDS
_DATA    SEGMENT
?indata@@3PAHA DD 01H                    ; indata
    DD    02H
    DD    03H
    DD    04H
    DD    05H
    DD    06H
    DD    07H
    DD    08H
    DD    09H
    ORG $+4007964
?dbB@@3NA DQ    04059000000000000r        ; 100    ; dbB
_DATA    ENDS
PUBLIC    _main
EXTRN    _printf:NEAR
_DATA    SEGMENT
$SG537    DB    '%d ', 00H
_DATA    ENDS
_TEXT    SEGMENT
_run$ = -400
_i$ = -404
_main    PROC NEAR
; File test.cpp
; Line 13
    push    ebp
    mov    ebp, esp
    sub    esp, 404                ; 00000194H
    push    edi
; Line 14
    mov    DWORD PTR _run$[ebp], 1
    mov    DWORD PTR _run$[ebp+4], 2
    mov    DWORD PTR _run$[ebp+8], 3
    mov    DWORD PTR _run$[ebp+12], 4
    mov    DWORD PTR _run$[ebp+16], 5
    mov    DWORD PTR _run$[ebp+20], 6
    mov    DWORD PTR _run$[ebp+24], 7
    mov    DWORD PTR _run$[ebp+28], 8
    mov    DWORD PTR _run$[ebp+32], 9
    mov    ecx, 91                    ; 0000005bH
    xor    eax, eax
    lea    edi, DWORD PTR _run$[ebp+36]
    rep stosd
; Line 15
    mov    DWORD PTR _i$[ebp], 0
    jmp    SHORT $L534
$L535:
    mov    eax, DWORD PTR _i$[ebp]
    add    eax, 1
    mov    DWORD PTR _i$[ebp], eax
$L534:
    cmp    DWORD PTR _i$[ebp], 1002000        ; 000f4a10H
    jge    SHORT $L536
; Line 16
    mov    ecx, DWORD PTR _i$[ebp]
    mov    edx, DWORD PTR ?inbss@@3PAHA[ecx*4]
    push    edx
    push    OFFSET FLAT:$SG537
    call    _printf
    add    esp, 8
    jmp    SHORT $L535
$L536:
; Line 17
    xor    eax, eax
; Line 18
    pop    edi
    mov    esp, ebp
    pop    ebp
    ret    0
_main    ENDP
_TEXT    ENDS
END
 ----------------------------------------
通过汇编文件可以看到,数组inbss和indata位于不同的段(inbss位于bss段,而indata位于data段)
若把test.cpp中的indata数组拿掉,查看生成的exe文件的大小,可以发现,indata拿掉之后exe文件的大小小了很多。而若拿掉的是inbss数组,exe文件大小跟没拿掉时相差无几。

说明了:
bss段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小。
data(已手动初始化的数据)段则为数据分配空间,数据保存在目标文件中。

数据段包含经过初始化的全局变量以及它们的值。BSS段的大小从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段后面。当这个内存区进入程序的地址空间后全部清零。包含数据段和BSS段的整个区段此时通常称为数据区。

 

来自http://hi.baidu.com/workbench/blog/item/f98401a8396eebb0ca130cf3.html

    http://www.atoi.cn/blog/index.php?url=archives/117-a.outAEafreebsdAE.html

    http://www.cublog.cn/u/15962/showart.php?id=287210

<think>好的,我现在需要回答用户关于“.out文件格式”的问题。首先,用户之前询问了VxWorks启动任务的text段位置,现在转向了.out文件格式,可能他们正在学习嵌入式系统或相关领域,需要了解不同的文件格式。 首先,我需要确认.out文件的具体含义,因为不同的系统可能有不同的.out文件格式。例如,Unix系统历史上使用a.out格式,而其他系统如VxWorks也可能有自己的.out文件类型。用户可能指的是Unix的a.out格式,或者是其他系统如VxWorks、DSP工具链中的.out文件。需要先明确这一点。 根据我的知识库,a.out是Unix早期的可执行文件格式,后来被COFF和ELF取代。但有些嵌入式系统或特定工具链仍可能使用.out扩展名的文件,比如TI的DSP编译器生成.out文件。因此,需要分情况讨论。 接下来,我需要组织回答的结构,确保清晰易懂。用户之前的提问结构分为几个步骤,可能希望类似的详细说明。所以,我可以分点介绍不同场景下的.out文件,并比较它们的结构、用途和特点。 首先,介绍Unix a.out格式,包括其历史、结构和被替代的原因。然后,讨论VxWorks中的.out文件,说明其可能的用途和结构差异。接着,提到DSP领域的.out文件,如TI的用例。最后,总结常见特征和注意事项,比如扩展名相同但格式不同,需要工具分析。 需要确保技术细节准确,比如a.out的段结构、魔数,以及VxWorks中的加载方式。同时,避免混淆不同系统的.out文件,明确指出它们的不同之处。还要提到如何用工具如objdump或readelf来分析文件,帮助用户实际操作。 另外,用户可能需要区分目标文件和可执行文件,以及不同格式的优缺点。例如,a.out的简单性导致扩展性差,而ELF更灵活。在嵌入式系统中,.out可能针对特定硬件优化,结构紧凑。 需要检查是否有遗漏的关键点,例如符号表、重定位信息、调试数据在不同格式中的处理方式。此外,是否需要举例说明,如使用hexdump查看文件头,或者具体编译命令生成.out文件。 最后,确保语言简洁,技术术语准确,同时易于理解。可能用户是开发者或学生,需要实际应用的信息,如如何分析.out文件内容,因此提供工具使用示例会很有帮助。 总结回答的结构: 1. 不同场景下的.out文件 - Unix a.out格式 - VxWorks的.out文件 - DSP领域的.out文件 2. 格式对比和工具使用 3. 注意事项和常见问题</think>关于`.out`文件格式,其具体含义和使用场景因系统和工具链不同而有所差异。以下是分场景的技术解析: --- ### 1. **Unix/Linux历史可执行格式(a.out)** - **历史地位** `.out`是早期Unix系统(如BSD、System V)的标准可执行文件格式,称为`a.out`(Assembler OUTput)。后被更复杂的格式(如COFF、ELF)取代,但术语"a.out"仍被沿用为默认输出文件名。 - **文件结构** | 组成部分 | 描述 | |----------------|--------------------------------------| | **文件头** | 包含魔数(Magic Number)、代码段/数据段大小等 | | **文本段(text)** | 机器指令(只读) | | **数据段(data)** | 已初始化的全局/静态变量 | | **BSS段** | 未初始化的数据(记录大小,不占文件空间) | | **符号表** | 调试和重定位信息(可选) | - **魔数标识** 通过文件头魔数区分格式版本,例如: - `0407`:普通可执行文件 - `0410`:共享库 - `0405`:目标文件(`.o`) --- ### 2. **VxWorks系统的.out文件** - **特殊用途** VxWorks(实时操作系统)中,`.out`文件通常指**可加载模块**,包含代码和数据,但需通过`ld`工具转换为二进制镜像(如`vxWorks`)运行。 - **加载方式** 使用`loadModule()`函数动态加载: ```c MODULE_ID modId = loadModule("app.out", LOAD_ALL_SYMBOLS); ``` --- ### 3. **DSP/嵌入式工具链的.out文件** - **典型场景** TI Code Composer Studio(CCS)等DSP开发工具生成的`.out`文件,是**COFF/ELF格式的变体**,包含: - **分段信息**:针对DSP内存分区(如`.text`、`.data`、`.cinit`) - **地址映射**:适配哈佛架构(代码与数据存储器分离) - **示例编译命令** ```bash cl6x -mv=xxx --output_file=app.out main.c ``` --- ### 4. **格式对比与工具支持** | 特征 | Unix a.out | VxWorks .out | DSP .out | |--------------------|------------------|-------------------|-------------------| | **目标架构** | 通用CPU | 实时系统 | DSP芯片 | | **符号表** | 简单符号 | 支持动态链接 | 复杂段重定位 | | **调试信息** | 基本支持 | 需独立符号表文件 | 集成DWARF调试数据 | | **分析工具** | `objdump -a` | `readelf -a` | `hex6x`(TI工具) | --- ### 5. **关键操作示例** - **查看文件类型** ```bash file app.out # 显示具体格式(ELF/COFF等) ``` - **反汇编代码段** ```bash objdump -d app.out # 适用于ELF格式的.out文件 ``` - **提取段信息** ```bash readelf -S app.out # 查看ELF格式的段头表 ``` --- ### 注意事项 1. **扩展名不唯一**:仅凭`.out`无法确定格式,需结合生成工具分析。 2. **兼容性问题**:不同工具链的.out文件通常不可交叉使用。 3. **嵌入式优化**:DSP的.out文件可能包含特殊段(如`.cio`用于I/O缓冲区)。 如需进一步分析具体文件,建议使用`hexdump`查看头部字节,或提供生成该文件的工具链信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值