精华内容
下载资源
问答
  • 行政机关公文格式要求设置大全 公司正式文件字体行距段落.xls
  • WAVE 文件格式分析

    千次阅读 2019-08-07 17:11:12
    WAVE 文件作为多媒体中使用的声音波形文件格式之一,它是以RIFF(Resource Interchange File Format)格式为标准的。每个WAVE文件的头四个字节便是“RIFF”。WAVE 文件由文件头和数据体两大部分组成。其中文件头又...

             WAVE 文件作为多媒体中使用的声音波形文件格式之一,它是以RIFF(Resource Interchange File Format)格式为标准的。每个WAVE文件的头四个字节便是“RIFF”。WAVE 文件由文件头和数据体两大部分组成。其中文件头又分为 RIFF/WAV 文件标识段和声音数据格式说明段两部分。WAVE文件各部分内容及格式见后文。

             常见的声音文件主要有两种,分别对应于单声道(11.025KHz 采样率、8Bit 的采样值)和双声道(44.1KHz 采样率、16Bit 的采样值)。采样率是指:声音信号在“模→数”转换过程中单位时间内采样的次数。采样值是指每一次采样周期 
    内声音模拟信号的积分值。

             对于单声道声音文件,采样数据为八位的短整数(short int 00H-FFH);而对于双声道立体声声音文件,每次采样数据为一个16位的整数(int),高八位和低八位分别代表左右两个声道。

            WAVE 文件数据块包含以脉冲编码调制(PCM)格式表示的样本。WAVE 文件是由样本组织而成的。在单声道 WAVE 文件中,声道0代表左声道,声道1代表右声道。在多声道WAVE文件中,样本是交替出现的。

            WAVE 文件除了前面一小段文件头对数据组织进行说明之外,Data 块就是声音的原始采样数据,WAVE 文件虽然可以压缩,但一般都使用不压缩的格式。44.1KHz 采样率、16Bit的分辨率、双声道,所以WAVE可以保存音质要求非常高的声音文件,CD 采用的也是这种格式,声音方面的专家或是音乐发烧友们应该非常熟悉。但这种文件的体积也非常大,以 44.1KHz 16bit 双声道的数据为例,一分钟的声音数据量为:4100*2byte*2channel*60s/1024/1024=10.09M 。所以不合适在网上传送。

    下面我们具体地分析 WAVE 文件的格式:

    以下是对各个字段的详细解说:

    对于Data块,根据声道数和采样率的不同情况:

    下面我们看一个具体的例子,声音文件如下:

    对应的分析如下图所示:

    1.PCM Wave格式详解

    WAVE文件格式是微软RIFF(Resource Interchange File Format,资源交换文件标准)的一种,是针对于多媒体文件存储的一种文件格式和标准。 一般而言,RIFF文件由文件头和数据两部分组成,一个WAVE文件由一个“WAVE”数据块组成,这个“WAVE”块又由一个”fmt”子数据块和一个“data”子 数据块组成,也称这种格式为“Canonical form”(权威/牧师格式),如下图所示:

    “WAVE”格式由两个子数据块构成:“fmt”块和“data”块,其中“fmt”块的详细解释如下: Subchunk1ID: 占4个字节,内容为“fmt ”的ASCII码(0x666d7420),以大端存储。
    Subchunk1Size: 占4个字节,存储该子块的字节数(不含前面的Subchunk1ID和Subchunk1Size这8个字节),以小端方式存储。
    AudioFormat:占2个字节,以小端方式存储,存储音频文件的编码格式,例如若为PCM则其存储值为1,若为其他非PCM格式的则有一定的压缩。
    NumChannels: 占2个字节,以小端方式存储,通道数,单通道(Mono)值为1,双通道(Stereo)值为2,等等。
    SampleRate: 占4个字节,以小端方式存储,采样率,如8k,44.1k等。
    ByteRate: 占4个字节,以小端方式存储,每秒存储的bit数,其值=SampleRate * NumChannels * BitsPerSample/8
    BlockAlign: 占2个字节,以小端方式存储,块对齐大小,其值=NumChannels * BitsPerSample/8
    BitsPerSample: 占2个字节,以小端方式存储,每个采样点的bit数,一般为8,16,32等。
    接下来是两个可选的扩展参数:
    ExtraParamSize: 占2个字节,表示扩展段的大小。
    ExtraParams: 扩展段其他自定义的一些参数的具体内容,大小由前一个字段给定。

    其中,对于每个采样点的bit数,不同的bit数读取数据的方式不同:

    “WAVE”格式文件的第二个子数据块是“data”,其个字段的详细解释如下:
    Subchunk2ID: 占4个字节,内容为“data”的ASCII码(0x64617461),以大端存储。
    Subchunk2Size: 占4个字节,内容为接下来的正式的数据部分的字节数,其值=NumSamples * NumChannels * BitsPerSample/8
    Data: 真正的语音数据部分。

    一个Wave文件头的实例

    设一个wave文件的前72个字节的十六进制内容如下(可以使用Ultra Edit等工具查看wave文件头):

    则其个字段的解析如下图:

    使用C语言文件操作库函数实现的Wave文件读取的实例代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    // define Wave format structure
    typedef struct tWAVEFORMATEX
    {
        short wFormatTag;         /* format type */
        short nChannels;          /* number of channels (i.e. mono, stereo...) */
        unsigned int nSamplesPerSec;     /* sample rate */
        unsigned int nAvgBytesPerSec;    /* for buffer estimation */
        short nBlockAlign;        /* block size of data */
        short wBitsPerSample;     /* number of bits per sample of mono data */
        short cbSize;             /* the count in bytes of the size of */
                                        /* extra information (after cbSize) */
    } WAVEFORMATEX, *PWAVEFORMATEX;
    
    char* wavread(char *fname, WAVEFORMATEX *wf);
    
    int main(){
      char fname[] = "test.wav";
      char *speech;
      WAVEFORMATEX wf;
      
      speech = wavread(fname, &wf);
      // afterward processing...
      
      return 0;
    }
    
    // read wave file
    char* wavread(char *fname, WAVEFORMATEX *wf){
      FILE* fp;
      char str[32];
      char *speech;
      unsigned int subchunk1size; // head size
      unsigned int subchunk2size; // speech data size
    
      // check format type
      fp = fopen(fname,"r");
      if(!fp){
          fprintf(stderr,"Can not open the wave file: %s.\n",fname);
          return NULL;
      }
      fseek(fp, 8, SEEK_SET);
      fread(str, sizeof(char), 7, fp);
      str[7] = '\0';
      if(strcmp(str,"WAVEfmt")){
          fprintf(stderr,"The file is not in WAVE format!\n");
          return NULL;
      }
      
      // read format header
      fseek(fp, 16, SEEK_SET);
      fread((unsigned int*)(&subchunk1size),4,1,fp);
      fseek(fp, 20, SEEK_SET);
      fread(wf, subchunk1size, 1, fp);
      
      // read wave data
      fseek(fp, 20+subchunk1size, SEEK_SET);
      fread(str, 1, 4, fp);
      str[4] = '\0';
      if(strcmp(str,"data")){
          fprintf(stderr,"Locating data start point failed!\n");
          return NULL;
      }
      fseek(fp, 20+subchunk1size+4, SEEK_SET);
      fread((unsigned int*)(&subchunk2size), 4, 1, fp);
      speech = (char*)malloc(sizeof(char)*subchunk2size);
      if(!speech){
          fprintf(stderr, "Memory alloc failed!\n");
          return NULL;
      }
      fseek(fp, 20+subchunk1size+8, SEEK_SET);
      fread(speech, 1, subchunk2size, fp);
    
      fclose(fp);
      return speech;
    }

     

    展开全文
  • CSV文件格式

    万次阅读 2016-11-10 10:03:00
    (逗号分隔值文件格式)  编辑 逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个...

    CSV

     

    (逗号分隔值文件格式)

      编辑
    逗号分隔值(Comma-Separated Values, CSV,有时也称为 字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个 字符序列,不含必须像二进制数字那样被解读的数据。CSV文件由任意数目的记录组成,记录间以某种换行符分隔;每条记录由 字段组成,字段间的分隔符是其它字符或字符串,最常见的是逗号或 制表符。通常,所有记录都有完全相同的字段序列。
    CSV文件格式的通用标准并不存在,但是在RFC 4180中有基础性的描述。使用的字符编码同样没有被指定,但是7-bit ASCII是最基本的通用编码。
    中文名
    CSV
    外文名
    Comma-Separated Values
    中文名
    逗号分隔值
    应    用
    在程序之间转移表格数据

    目录

    用法

    编辑
    CSV是一种通用的、相对简单的文件格式,被用户、商业和科学广泛应用。最广泛的应用是在程序之间转移表格数据,而这些程序本身是在不兼容的格式上进行操作的(往往是私有的和/或无规范的格式)。因为大量程序都支持某种CSV变体,至少是作为一种可选择的输入/输出格式。
    例如,一个用户可能需要交换信息,从一个以私有格式存储数据的数据库程序,到一个数据格式完全不同的电子表格。最可能的情况是,该数据库程序可以导出数据为“CSV”,然后被导出的CSV文件可以被电子表格程序导入。
    “CSV”并不是一种单一的、定义明确的格式(尽管RFC 4180有一个被通常使用的定义)。因此在实践中,术语“CSV”泛指具有以下特征的任何文件:
    1. 纯文本,使用某个字符集,比如 ASCIIUnicodeEBCDICGB2312
    2. 由记录组成(典型的是每行一条记录);
    3. 每条记录被 分隔符分隔为 字段(典型分隔符有逗号、分号或制表符;有时分隔符可以包括可选的空格);
    4. 每条记录都有同样的字段序列。
    在这些常规的约束条件下,存在着许多CSV变体,故CSV文件并不完全互通。然而,这些变异非常小,并且有许多应用程序允许用户预览文件(这是可行的,因为它是纯文本),然后指定分隔符、转义规则等。如果一个特定CSV文件的变异过大,超出了特定接收程序的支持范围,那么可行的做法往往是人工检查并编辑文件,或通过简单的程序来修复问题。因此在实践中,CSV文件还是非常方便的。

    规则

    编辑
    1 开头是不留空,以行为单位。
    2 可含或不含列名,含列名则居文件第一行。
    3 一行数据不跨行,无空行。
    4 以 半角逗号(即,)作分隔符,列为空也要表达其存在。
    5列内容如存在半角引号(即"),替换成半角双引号("")转义,即用半角引号(即"")将该字段值包含起来。
    6文件读写时引号,逗号操作规则互逆。
    7内码格式不限,可为 ASCII、Unicode 或者其他。
    8不支持特殊字符

    实例

    编辑
    下面是一个实际 CSV 文件中的部分内容,让大家对他有一个感性的认识。我们选的是 Sjojo_Rescan 的 CSV 文件 (Sjojo 是 ASW- 亚洲扫图风的成员之一)。
    sj_mino1001.jpg,715282,4FB55FE8,
    sj_mino1002.jpg,471289,93203C5C,
    sj_mino1003.jpg,451929,C4E80467,
    通常 CSV 文件开头是不留空的,以行为单位,每行中记录一张图片的多项数据,每项数据用逗号来分隔(标准英文逗号)。一般说来集图用的.CSV 文件的格式是这样的:
    文件名, 文件大小 (以字节为单位),CRC 校验值, 注释 (可省略)
    sj_mino1001.jpg,715282,4FB55FE8,
    | | | |
    文件名 文件大小 CRC 值 注释 (已省略)
    如果你的机器上装了  Microsoft Excel的话,.csv 文件默认是被 Excel打开的。需要注意的是,当你双击一个.CSV 文件,Excel 打开它以后即使不做任何的修改,在关闭的时候 Excel 往往会提示是否要改成正确的文件格式,这个时候如果选择“是”,因为 Excel 认为.CSV 文件中的数字是要用科学记数法来表示的,Excel 会把 CSV 文件中所有的数字用科学计数来表示(2.54932E+5 这种形式),这样操作之后,只是在 Excel 中显示的时候会不正常,而 csv 文件由于是纯文本文件,在使用上没有影响;如果选择了“否”,那么会提示你以 xls 格式另存为 Excel 的一个副本。
    所以如果你的 CSV 文件绝大部分都是用在集图上的话,建议把.CSV 的默认打开方式改成任意一个文本  编辑器,系统自带的记事本就是个不错的选择。
    好,让我们回到 CSV 文件的格式中来。见上面,从左到右 sj_mino1001.jpg 是文件名,715282 是以字节表示的文件大小。当文件名中包含逗号的时候,由于逗号在 CSV 文件中特殊的作用,为了不至于产生歧义,需要用引号把文件名括起来。比如 "The Art, Fantasy.jpg",384211,...,.... 接着是一个 8 位的 16 进制数字 4FB55FE8,这是文件的 CRC32 校验值。整个 CSV 文件的精髓都在这里。
    让我们举个小例子来说明它的意义:Sjojo 发布了一张图 sj_mino1001.jpg,假设你是从朋友手中拿到这张图的,那么你肯定常常会有这样的疑问,这张图在传播的过程中是否被改动过呢?是否无意中遭到了损坏呢?CRC 就是为解决这个问题而存在的。
    一般情况下,扫图家每发布了一套图,就同时放出一个 CSV 文件(这种 CSV 通常叫做 Official CSV,也就是官方发布的 CSV 的意思),这个 CSV 中的 CRC 值是用专门的软件通过 CRC32 算法(常用的 CRC 算法还有 CRC16)对文件运算后生成的一个值, 这个值可用作文件真身的标志。在绝大多数情况下,如果这个文件在传播过程中无论是大小还是内容被改动过。那么,用同样的 CRC32 算法再对文件进行运算后产生的 CRC 校验值就完全不一样了。如果得出的 CRC 值是一样的,则完全有理由认为这个文件是真身,没有被改动过。还有些时候,收来的图片文件名被改动过了,那么你怎么知道谁是谁呢?
    这个时候 CRC 值又起作用了:用专用的软件对图片处理后,可以得到文件的大小和 CRC 值,然后根据大小和 CRC 值在 CSV 中寻找是否有适合的图片。如果有,就会把图片的名字改成 CSV 里的。
    举个例子,你收到了一张图片,名字是 pic0001.jpg,同时你知道这张图片是 Sjojo_Rescan 这个集子里面的,但是不知道具体是哪一张。用软件得到它的大小和 CRC 分别是 715282 和 4FB55FE8,那么软件在 CSV 文件里找到一行 sj_mino1001.jpg,715282,4FB55FE8, 大小和 CRC 都符合,软件就认为这张图片的原名是 sj_mino1001.jpg,接着自动把文件的名字改成 sj_mino1001.jpg
    改动的不是 CSV 文件而是图片的文件名。接下来的是注释和说明,可以省略掉。需要注意的是注释的后面是没有逗号的,如果要省略注释的话,一定要在 CRC 值的后面保留一个逗号,否则软件会把 CRC 值认为是注释的。

    规范

    编辑
    逗号分隔列出日期之前,尽早从 个人电脑,但被广泛应用于最早前 IBM个人电脑时代的个人电脑磁带存储备份和交 换的信息的数据库机器的两种不同的架构。在这一天,负担得起的硬盘驱动器不存在,和许多小型企业试图实现 效益的计算使用软盘的软件。
    CSV没有通用标准规范。不同的程序间CSV的标准有差异很常见,这可能导致交互操作困难。互联网通信的CSV文件,资料的IETF文件( 符合RFC 4180于2005年10月)介绍了格式的“text/csv格式”MIME类型登记的IANA负责。另一个相关的规格是由派出文字其中也包括CSV格式。
    许多非正式文件的存在,说明CSV格式。如何:逗号分隔值(CSV)文件格式概述了CSV格式中使用最广泛的应用和解释它如何能最好地利用和支持。

    基本规则

    CSV格式是分隔的数据格式,有字段/列分隔的逗号字符和记录/行分隔换行符。字段包含特殊字符(逗号,换行符,或双引号),必须以双引号括住。行内包含一个项目是空字符串,可以以双引号括住。字段的值包含双引号时,要双写这个双引号(就像把一个双引号当做转义符一样)。CSV文件格式并不需要特定的字符编码,字节顺序,或行终止格式。
    • 每个记录是一个行终止了换行符( ASCII码/低频= 0×0A)或回车换行符和一双(ASCII码/ CRLF = 0×0D0×0A),然而,线路中断可以嵌入。
    • 字段用逗号分隔(虽然地点在逗号作为小数点,分号是用来代替作为定界符)
    1997,Ford,E350
    • 在某些的CSV实施,领导和尾随空格或制表符,毗邻逗号,装饰。这种做法是有争议的 , 实际上是明文禁止的RFC 4180,其中规定,“被认为是空间的一个组成部分领域 , 而不应被忽视。”
    • 各领域内的嵌入式逗号必须存放在双引号字符。
    • 各领域内的嵌入式双引号字符必须是封闭的双引号字符,每一个嵌入式双引号字符必须用一对双引号字符。
    • 各领域内的嵌入式换行符必须封闭在双引号字符。
    • 领域的前导或尾随空格内必须封闭双引号字符。 (见评论领导和尾随 空格以上。)
    • 字段可能永远是封闭的双引号字符,是否有必要或没有。
    • 第一条记录中的CSV文件中可能包含列名的每个领域。

    举例说明

    制造商
    型号
    说明
    价值
    1997
    Ford
    E350
    ac, abs, moon
    3000.00
    1999
    Chevy
    Venture "Extended Edition"
     
    4900.00
    1999
    Chevy
    Venture "Extended Edition, Very Large"
     
    5000.00
    1996
    Jeep
    Grand Cherokee
    MUST SELL!
      air, moon roof, loaded
    4799.00
    上面表格内容若以CSV格式表示就会像下列:
    年,制造商,型号,说明,价值
    1997,Ford,E350,"ac, abs, moon",3000.00
      1999,Chevy,"Venture ""Extended Edition""","",4900.00
      1999,Chevy,"Venture ""Extended Edition, Very Large""","",5000.00
      1996,Jeep,Grand Cherokee,"MUST SELL!
    air, moon roof, loaded",4799.00
    以上这个CSV的例子说明了:
    • 包含逗号, 双引号, 或是换行符的字段必须放在引号内.
    • 字段内部的引号必须在其前面增加一个引号来实现文字引号的转码.
    • 分隔符逗号前后的空格  可能不会 被修剪掉. 这是RFC 4180的要求.
    • 元素中的换行符将被保留下来. [1]  

    文件转换

    编辑
    打开包含地址数据的 Excel 工作簿。
    在地址示例中,第一个地址包含四行,第二个地址仅包含三行。此外,每个地址集之间相隔一行。
    要将地址数据成功转换为 CSV 文本文件,所有地址必须包含相同的行数并且每个地址集之间相隔的行数必须相同。例如,将地址示例更改为以下形式:
    A1:Jane Clayton
    A2:Microsoft
    A3:456 Elm Street
    A4:Sometown, USA 67890
    A5:
    A6:
    A7:Jose Saraiva
    A8:789 Oak Road
    A9:
    A10:Mytown, USA 54321
    注意:每个地址集都包含四行,相隔两行。
    要在 Excel 工作表中插入新行,请选择要在其上插入新行的行标题。在“插入”菜单上,单击“行”。
    注意:在 Excel 2007 中,要在工作表中插入新行,需选择要在其上插入新行的行,单击“主页”选项卡上“单元格”组中的“插入”,然后单击“插入工作表行”。
    在“文件”菜单上,单击“另存为”。
    注意:在 Excel 2007 中,单击“Office 按钮”,然后单击“另存为”。
    在“另存为”对话框中执行以下操作:
    在“保存类型”框中,单击“CSV (逗号分隔)(*.csv)”。
    在“文件名”框中,键入 CSV 文件的名称(例如 Address.csv),然后单击“保存”。
    如果收到下面的消息,请单击“确定”:
    选定的文件类型不支持包含多个工作表的工作簿。
    · 如果只保存活动工作表,请单击“确定”按钮。
    · 如果要保存所有工作表,请使用不同的文件名将其分别保存,或选择一种支持多工作表的文件类型。
    在收到以下消息时单击“是”:
    Address.csv 可能含有与 CSV (逗号分隔) 不 兼容的功能。是否保持工作簿的这种格式?
    · 如要保持这种格式,去掉所有不兼容的功能,请单击“是”。
    · 如要保留这些功能,请单击“否”。然后再用最新 Excel 格式保存一份副本。
    · 如想知道哪些内容会丢失,请单击“帮助”。
    在“文件”菜单上,单击“关闭”,然后退出 Microsoft Excel。
    展开全文
  • ELF文件格式详解

    万次阅读 2016-07-21 00:36:58
    ARM的可执行文件格式是ELF格式文件,下文对ELF格式做个详细的介绍。 序言 1. OBJECT文件  导言  ELF头(ELF Header)  Sections  String表(String Table)  Symbol表(Symbol Table)  重定位(Relocation) 2. ...

    ARM的可执行文件的格式是ELF格式文件,下文对ELF格式做个详细的介绍。

    序言
    1. OBJECT文件
       导言
       ELF头(ELF Header)
       Sections
       String表(String Table)
       Symbol表(Symbol Table)
       重定位(Relocation)
    2. 程序装载与动态连接
       导言
       Program头(Program Header)
       Program装载(Program Loading)
       Dynamic连接(Dynamic Linking)
    3. C LIBRARY
       C Library

       ________________________________________________________________


              导言

       ________________________________________________________________


      ELF: 可执行连接格式

    可执行连接格式是UNIX系统实验室(USL)作为应用程序二进制接口
    (Application Binary Interface(ABI)而开发和发布的。工具接口标准委
    员会(TIS)选择了正在发展中的ELF标准作为工作在32位INTEL体系上不同操
    作系统之间可移植的二进制文件格式。

    假定开发者定义了一个二进制接口集合,ELF标准用它来支持流线型的软件
    发展。 应该减少不同执行接口的数量。因此可以减少重新编程重新编译的
    代码。


    关于这片文档

    这篇文档是为那些想创建目标文件或者在不同的操作系统上执行文件的开发
    着准备的。它分以下三个部分:

    * 第一部分, “目标文件Object Files”描述了ELF目标文件格式三种主要
      的类型。
    * 第二部分, “程序转载和动态连接”描述了目标文件的信息和系统在创建
      运行时程序的行为。
    * 第三部分, “C 语言库”列出了所有包含在libsys中的符号,标准的ANSI C
      和libc的运行程序,还有libc运行程序所需的全局的数据符号。

    注意: 参考的X86体系已经被改成了Intel体系。

       ________________________________________________________________


       1. 目标文件(Object file)

       ________________________________________________________________


       ========================= 序言 =========================


    第一部分描述了iABI的object文件的格式, 被称为ELF(Executable
    and Linking Format). 在object文件中有三种主要的类型。

    * 一个可重定位(relocatable)文件保存着代码和适当的数据,用来和其他的
      object文件一起来创建一个可执行文件或者是一个共享文件。
    * 一个可执行(executable)文件保存着一个用来执行的程序;该文件指出了
      exec(BA_OS)如何来创建程序进程映象。
    * 一个共享object文件保存着代码和合适的数据,用来被下面的两个链接器
      链接。第一个是连接编辑器[请参看ld(SD_CMD)],可以和其他的可重定位和
      共享object文件来创建其他的object。第二个是动态链接器,联合一个
      可执行文件和其他的共享object文件来创建一个进程映象。

    一个object文件被汇编器和联接器创建, 想要在处理机上直接运行的object
    文件都是以二进制来存放的。那些需要抽象机制的程序,比如象shell脚本,
    是不被接受的。

    在介绍性的材料过后,第一部分主要围绕着文件的格式和关于如何建立程序。
    第二部分也描述了object文件的几个组成部分,集中在执行程序所必须的信息上。


         文件格式

    Object文件参与程序的联接(创建一个程序)和程序的执行(运行一个程序)。
    object 文件格式提供了一个方便有效的方法并行的视角看待文件的内容,
    在他们的活动中,反映出不同的需要。例 1-1图显示了一个object文件的
    组织图。

    + 图1-1: Object文件格式

      Linking 视角                      Execution 视角
      ============                      ==============
      ELF header                        ELF header
      Program header table (optional)   Program header table
      Section 1                         Segment 1
      ...                               Segment 2
      Section n                         ...
      Section header table              Section header table (optional)

    一个ELF头在文件的开始,保存了路线图(road map),描述了该文件的组织情况。
    sections保存着object 文件的信息,从连接角度看:包括指令,数据,
    符号表,重定位信息等等。特别sections的描述会出项在以后的第一部分。
    第二部分讨论了段和从程序的执行角度看文件。

    假如一个程序头表(program header table)存在,那么它告诉系统如何来创建一
    个进程的内存映象。被用来建立进程映象(执行一个程序)的文件必须要有一个程
    序头表(program header table);可重定位文件不需要这个头表。一个
    section头表(section header table)包含了描述文件sections的信息。每个
    section在这个表中有一个入口;每个入口给出了该section的名字,大小,
    等等信息。在联接过程中的文件必须有一个section头表;其他object文件可要
    可不要这个section头表。

    注意: 虽然图显示出程序头表立刻出现在一个ELF头后,section头表跟着其他
    section部分出现,事实是的文件是可以不同的。此外,sections和段(segments)
    没有特别的顺序。只有ELF头(elf header)是在文件的固定位置。


    数据表示
    object文件格式支持8位、32位不同的处理器。不过,它试图努力的在更大
    或更小的体系上运行。因此,object文件描绘一些控制数据需要用与机器
    无关的格式,使它尽可能的用一般的方法甄别object文件和描述他们的内容。
    在object文件中剩余的数据使用目标处理器的编码方式,不管文件是在哪台
    机子上创建的。

    + 图 1-2: 32-Bit Data Types

      Name           Size Alignment   Purpose
      ====           ==== =========   =======
      Elf32_Addr      4       4       Unsigned program address
      Elf32_Half      2       2       Unsigned medium integer
      Elf32_Off       4       4       Unsigned file offset
      Elf32_Sword     4       4       Signed large integer
      Elf32_Word      4       4       Unsigned large integer
      unsigned char   1       1       Unsigned small integer

    所有的object文件格式定义的数据结构是自然大小(natural size),为相关
    的类型调整指针。如果需要,数据结构中明确的包含了确保4字节对齐的填
    充字段。来使结构大小是4的倍数。数据从文件的开始也有适当的对齐。
    例如,一个包含了Elf32_Addr成员的结构将会在文件内对齐到4字节的边界上。

    因为移植性的原因,ELF不使用位字段。


       ========================== ELF Header ==========================


    一些object文件的控制结构能够增长的,所以ELF头包含了他们目前的大小。
    假如object文件格式改变,程序可能会碰到或大或小他们不希望的控制结构。
    程序也有可能忽略额外(extra)的信息。
    对待来历不明(missing)的信息依靠上下文来解释,假如扩展被定义,它们
    将会被指定。

    + 图 1-3: ELF Header

      #define EI_NIDENT       16

      typedef struct {
          unsigned char       e_ident[EI_NIDENT];
          Elf32_Half          e_type;
          Elf32_Half          e_machine;
          Elf32_Word          e_version;
          Elf32_Addr          e_entry;
          Elf32_Off           e_phoff;
          Elf32_Off           e_shoff;
          Elf32_Word          e_flags;
          Elf32_Half          e_ehsize;
          Elf32_Half          e_phentsize;
          Elf32_Half          e_phnum;
          Elf32_Half          e_shentsize;
          Elf32_Half          e_shnum;
          Elf32_Half          e_shstrndx;
      } Elf32_Ehdr;

    * e_ident

      这个最初的字段标示了该文件为一个object文件,提供了一个机器无关
      的数据,解释文件的内容。在下面的ELF的鉴别(ELF Identification)
      部分有更详细的信息。

    * e_type

      该成员确定该object的类型。

                   Name        Value  Meaning
                   ====        =====  =======
                   ET_NONE         0  No file type
                   ET_REL          1  Relocatable file
                   ET_EXEC         2  Executable file
                   ET_DYN          3  Shared object file
           ET_CORE         4  Core file
           ET_LOPROC  0xff00  Processor-specific
           ET_HIPROC  0xffff  Processor-specific

      虽然CORE的文件内容未被指明,类型ET_CORE是保留的。
      值从 ET_LOPROC 到 ET_HIPROC(包括ET_HIPROC)是为特殊的处理器保留的。
      如有需要,其他保留的变量将用在新的object文件类型上。

    * e_machine

      该成员变量指出了运行该程序需要的体系结构。

                        Name      Value  Meaning
                ====      =====  =======
                        EM_NONE       0  No machine
        EM_M32        1  AT&T WE 32100
                        EM_SPARC      2  SPARC
                        EM_386        3  Intel 80386
                        EM_68K        4  Motorola 68000
                        EM_88K        5  Motorola 88000
                        EM_860        7  Intel 80860
                        EM_MIPS       8  MIPS RS3000

      如有需要,其他保留的值将用到新的机器类型上。特殊处理器名使用机器名来
      区别他们。例如,下面将要被提到的成员flags使用前缀EF_;在一台EM_XYZ机器
      上,flag称为WIDGET,那么就称为EF_XYZ_WIDGET。

    * e_version

      这个成员确定object文件的版本。

                     Name         Value  Meaning
                     ====         =====  =======
                     EV_NONE          0  Invalid version
    EV_CURRENT       1  Current version

      值1表示原来的文件格式;创建新版本就用>1的数。EV_CURRENT值(上面给
      出为1)如果需要将指向当前的版本号。

    * e_entry

      该成员是系统第一个传输控制的虚拟地址,在那启动进程。假如文件没有
      如何关联的入口点,该成员就保持为0。

    * e_phoff

      该成员保持着程序头表(program header table)在文件中的偏移量(以字节计数)。
      假如该文件没有程序头表的的话,该成员就保持为0。

    * e_shoff

      该成员保持着section头表(section header table)在文件中的偏移量(以字节
      计数)。假如该文件没有section头表的的话,该成员就保持为0。

    * e_flags

      该成员保存着相关文件的特定处理器标志。
      flag的名字来自于EF_<machine>_<flag>。看下机器信息“Machine Information”
      部分的flag的定义。

    * e_ehsize

      该成员保存着ELF头大小(以字节计数)。

    * e_phentsize

      该成员保存着在文件的程序头表(program header table)中一个入口的大小
      (以字节计数)。所有的入口都是同样的大小。

    * e_phnum

      该成员保存着在程序头表中入口的个数。因此,e_phentsize和e_phnum
      的乘机就是表的大小(以字节计数).假如没有程序头表(program header table),
      e_phnum变量为0。

    * e_shentsize

      该成员保存着section头的大小(以字节计数)。一个section头是在section
      头表(section header table)的一个入口;所有的入口都是同样的大小。

    * e_shnum

      该成员保存着在section header table中的入口数目。因此,e_shentsize和
      e_shnum的乘积就是section头表的大小(以字节计数)。
      假如文件没有section头表,e_shnum值为0。

    * e_shstrndx

      该成员保存着跟section名字字符表相关入口的section头表(section header 
      table)索引。假如文件中没有section名字字符表,该变量值为SHN_UNDEF。
      更详细的信息 看下面“Sections”和字符串表(“String Table”) 。


      ELF 鉴别(Identification)

    在上面提到的,ELF提供了一个object文件的框架结构来支持多种处理机,多
    样的数据编码方式,多种机器类型。为了支持这个object文件家族,最初的几
    个字节指定用来说明如何解释该文件,独立于处理器,与文件剩下的内容无关。

    ELF头(也就是object文件)最初的几个字节与成员e_ident相一致。

    + 图 1-4: e_ident[] Identification Indexes

      Name           Value  Purpose
      ====           =====  =======
      EI_MAG0      0  File identification
      EI_MAG1      1  File identification
      EI_MAG2      2  File identification
      EI_MAG3      3  File identification
      EI_CLASS      4  File class
      EI_DATA      5  Data encoding
      EI_VERSION      6  File version
      EI_PAD      7  Start of padding bytes
      EI_NIDENT     16  Size of e_ident[]

    通过索引访问字节,以下的变量被定义。

    * EI_MAG0 to EI_MAG3

      文件的前4个字符保存着一个魔术数(magic number),用来确定该文件是否
      为ELF的目标文件。

                      Name       Value  Position
                      ====       =====  ========
      ELFMAG0    0x7f   e_ident[EI_MAG0]
                      ELFMAG1    'E'    e_ident[EI_MAG1]
                      ELFMAG2    'L'    e_ident[EI_MAG2]
                      ELFMAG3    'F'    e_ident[EI_MAG3]

    * EI_CLASS

      接下来的字节是e_ident[EI_CLASS],用来确定文件的类型或者说是能力。

                     Name           Value  Meaning
                     ====           =====  =======
                     ELFCLASSNONE       0  Invalid class
                     ELFCLASS32         1  32-bit objects
    ELFCLASS64         2  64-bit objects

      文件格式被设计成在不同大小机器中可移植的,在小型机上的不用大型机上
      的尺寸。类型ELFCLASS32支持虚拟地址空间最大可达4GB的机器;使用上面
      定义过的基本类型。

      类型ELFCLASS64为64位体系的机器保留。它的出现表明了object文件可能 
      改变,但是64位的格式还没有被定义。如果需要,其他类型将被定义,会
      有不同的类型和不同大小的数据尺寸。

    * EI_DATA

      字节e_ident[EI_DATA]指定了在object文件中特定处理器数据的编码
      方式。当前定义了以下编码方式。

                 Name           Value  Meaning
                 ====           =====  =======
         ELFDATANONE        0  Invalid data encoding
                 ELFDATA2LSB        1  See below
                 ELFDATA2MSB        2  See below

      更多的关于编码的信息出现在下面。其他值保留,将被分配一个新的编码
      方式,当然如果必要的话。

    * EI_VERSION

      字节e_ident[EI_VERSION]表明了ELF头的版本号。
      现在这个变量的是一定要设为EV_CURRENT,作为上面e_version的解释。

    * EI_PAD

      该变量标识了在e_ident中开始的未使用的字节。那些字节保留并被设置为
      0;程序把它们从object 文件中读出但应该忽略。假如当前未被使用的字节
      有了新的定义,EI_PAD变量将来会被改变。

    一个文件的数据编码指出了如何来解释一个基本的object文件。在上述的
    描述中,类型ELFCLAS32文件使用占用1,2和4字节的目标文件。下面定义的
    编码方式,用下面的图来描绘。数据出现在左上角。


    ELFDATA2LSB编码指定了2的补数值。
    最小有意义的字节占有最低的地址。

    + 图1-5: Data Encoding ELFDATA2LSB

                   0------+
          0x0102   |  01  |
                   +------+
                   0------1------+
        0x010204   |  02  |  01  |
                   +------+------+
                   0------1------2------3------+
      0x01020304   |  04  |  03  |  02  |  01  |
                   +------+------+------+------+

    ELFDATA2LSB编码指定了2的补数值。
    最大有意义的字节占有最低的地址。

    + 图1-6: Data Encoding ELFDATA2MSB

                   0------+
          0x0102   |  01  |
                   +------+
                   0------1------+
        0x010204   |  01  |  02  |
                   +------+------+
                   0------1------2------3------+
      0x01020304   |  01  |  02  |  03  |  04  |
                   +------+------+------+------+


    机器信息

    为了确定文件,32位Intel体系结构的需要以下的变量。

    + 图1-7: 32-bit Intel Architecture Identification, e_ident

      Position           Value
      ========           =====
      e_ident[EI_CLASS]  ELFCLASS32
      e_ident[EI_DATA]   ELFDATA2LSB

    处理器确认ELF头里的e_machine成员,该成员必须为EM_386。

    ELF报头里的e_flags成员保存了和文件相关的位标记。32位Intel体系上未
    定义该标记;所以这个成员应该为0;


       =========================== Sections ===========================


    一个object文件的section header table可以让我们定位所有的sections。
    section header table是个Elf32_Shdr结构的数组(下面描述)。一个section
    报头表(section header table)索引是这个数组的下标。ELF header table
    的e_shoff成员给出了section报头表的偏移量(从文件开始的计数)。e_shnum
    告诉我们section报头表中包含了多少个入口;e_shentsize 给出了每个
    入口的大小。

    一些section报头表索引是保留的;那些特别的索引在一个object文件中
    将没有与之对应sections。

    + 图1-8: Special Section Indexes

      Name             Value
      ====             =====
      SHN_UNDEF            0
      SHN_LORESERVE   0xff00
      SHN_LOPROC      0xff00
      SHN_HIPROC      0xff1f
      SHN_ABS         0xfff1
      SHN_COMMON      0xfff2
      SHN_HIRESERVE   0xffff

    * SHN_UNDEF

      该值表明没有定义,缺少,不相关的或者其他涉及到的无意义的section。
      例如,标号“defined”相对于section索引号SHN_UNDEF是一个没有被
      定义的标号。

    注意: 虽然索引0保留作为未定义的值,section报头表包含了一个索引0的
    入口。因此,假如ELF报头说一个文件的section报头表中有6个section入口
    的话,e_shnum的值应该是从0到5。最初的入口的内容以后在这个section中
    被指定。

    * SHN_LORESERVE

      该值指定保留的索引范围的最小值。

    * SHN_LOPROC through SHN_HIPROC

      该值包含了特定处理器语意的保留范围。


    * SHN_ABS

      该变量是相对于相应参考的绝对地址。
      例如,section号的标号是绝对地址,不被重定位影响。

    * SHN_COMMON

      该section的标号是一个公共(common)的标号,就象FORTRAN COMMON
      或者不允许的C扩展变量。

    * SHN_HIRESERVE

      该值指定保留的索引范围的上限。系统保留的索引值是从SHN_LORESERVE
      到SHN_HIRESERVE;该变量不涉及到section报头表(section header table)。
      因此,section报头表不为保留的索引值包含入口。

    sections包含了在一个object文件中的所有信息,除了ELF报头,程序报头
    表(program header table),和section报头表(section header table)。
    此外,object文件的sections满足几天条件:

    * 每个在object文件中的section都有自己的一个section的报头来描述它。
      section头可能存在但section可以不存在。
    * 每个section在文件中都占有一个连续顺序的空间(但可能是空的)。
    * 文件中的Sections不可能重复。文件中没有一个字节既在这个section中
      又在另外的一个section中。
    * object文件可以有"非活动的"空间。不同的报头和sections可以不覆盖到
      object文件中的每个字节。"非活动"数据内容是未指定的。

    一个section头有如下的结构。

    + 图1-9: Section Header

      typedef struct {
          Elf32_Word sh_name;
          Elf32_Word sh_type;
          Elf32_Word sh_flags;
          Elf32_Addr sh_addr;
          Elf32_Off sh_offset;
          Elf32_Word sh_size;
          Elf32_Word sh_link;
          Elf32_Word sh_info;
          Elf32_Word sh_addralign;
          Elf32_Word sh_entsize;
      } Elf32_Shdr;

    * sh_name

      该成员指定了这个section的名字。它的值是section报头字符表section的
      索引。[看以下的“String Table”], 以NULL空字符结束。

    * sh_type

      该成员把sections按内容和意义分类。section的类型和他们的描述在下面。

    * sh_flags

      sections支持位的标记,用来描述多个属性。  
      Flag定义出现在下面。

    * sh_addr

      假如该section将出现在进程的内存映象空间里,该成员给出了一个该section
      在内存中的位置。否则,该变量为0。

    * sh_offset

      该成员变量给出了该section的字节偏移量(从文件开始计数)。SHT_NOBITS
      类型的section(下面讨论)在文件中不占空间,它的sh_offset成员定位在
      文件中的概念上的位置。

    * sh_size
      该成员给你了section的字节大小。除非这个section的类型为SHT_NOBITS,
      否则该section将在文件中将占有sh_size个字节。SHT_NOBITS类型的section
      可能为非0的大小,但是不占文件空间。

    * sh_link

      该成员保存了一个section报头表的索引连接,它的解释依靠该section的
      类型。以下一个表描述了这些值。

    * sh_info

      该成员保存着额外的信息,它的解释依靠该section的类型。以下一个表描
      述了这些值。

    * sh_addralign

      一些sections有地址对齐的约束。例如,假如一个section保存着双字,系统
      就必须确定整个section是否双字对齐。所以sh_addr的值以sh_addralign的值
      作为模,那么一定为0。当然的,仅仅0和正的2的次方是允许的。值0和1意味
      着该section没有对齐要求。

    * sh_entsize

      一些sections保存着一张固定大小入口的表,就象符号表。对于这样一个
      section来说,该成员给出了每个入口的字节大小。如果该section没有
      保存着一张固定大小入口的表,该成员就为0。

    section头成员sh_type指出了section的语意。

    + 图1-10: Section Types, sh_type

      Name               Value
      ====               =====
      SHT_NULL               0
      SHT_PROGBITS           1
      SHT_SYMTAB             2
      SHT_STRTAB          3
      SHT_RELA          4
      SHT_HASH          5
      SHT_DYNAMIC            6
      SHT_NOTE          7
      SHT_NOBITS          8
      SHT_REL          9
      SHT_SHLIB             10
      SHT_DYNSYM            11
      SHT_LOPROC    0x70000000
      SHT_HIPROC    0x7fffffff
      SHT_LOUSER    0x80000000
      SHT_HIUSER    0xffffffff

    * SHT_NULL

      该值表明该section头是无效的;它没有相关的section。
      该section的其他成员的值都是未定义的。

    * SHT_PROGBITS

      该section保存被程序定义了的一些信息,它的格式和意义取决于程序本身。

    * SHT_SYMTAB and SHT_DYNSYM

      那些sections保存着一个符号表(symbol table)。一般情况下,一个
      object文件每个类型section仅有一个,但是,在将来,这个约束可能被放宽。
      典型的是,SHT_SYMTAB为连接器提供标号,当然它也有可能被动态连接时使用。
      作为一个完整的符号表,它可能包含了一些动态连接时不需要的标号。
      因此,一个object文件可能也包含了一个SHT_DYNSYM的section,它保存着
      一个动态连接时所需最小的标号集合来节省空间。
      看下面符号表“Symbol Table”的细节。

    * SHT_STRTAB

      该section保存着一个字符串表。一个object文件可以包含多个字符串表的
      section。看下面字符串表“String Table”的细节。

    * SHT_RELA

      该section保存着具有明确加数的重定位入口。就象object文件32位的
      Elf32_Rela类型。一个object文件可能有多个重定位的sections。具体细节
      看重定位``Relocation''部分。

    * SHT_HASH

      该标号保存着一个标号的哈希(hash)表。所有的参与动态连接的object
      一定包含了一个标号哈希表(hash table)。当前的,一个object文件
      可能只有一个哈希表。详细细节看第二部分的哈希表"Hash Table"。

    * SHT_DYNAMIC

      该section保存着动态连接的信息。当前的,一个object可能只有一个动态
      的section,但是,将来这个限制可能被取消。详细细节看第二部分的动态
      section(“Dynamic Section”)。

    * SHT_NOTE

      该section保存着其他的一些标志文件的信息。详细细节看第二部分的“Note 
      Section” 。

    * SHT_NOBITS

      该类型的section在文件中不占空间,但是类似SHT_PROGBITS。尽管该section
      不包含字节,sh_offset成员包含了概念上的文件偏移量。

    * SHT_REL

      该section保存着具有明确加数的重定位的入口。
      就象object文件32位类型Elf32_Rel类型。一个object文件可能有多个
      重定位的sections。具体细节看重定位``Relocation''部分。

    * SHT_SHLIB

      该section类型保留但语意没有指明。包含这个类型的section的程序
      是不符合ABI的。

    * SHT_LOPROC through SHT_HIPROC

      在这范围之间的值为特定处理器语意保留的。

    * SHT_LOUSER

      该变量为应用程序保留的索引范围的最小边界。

    * SHT_HIUSER

      该变量为应用程序保留的索引范围的最大边界。在SHT_LOUSER和HIUSER的
      section类型可能被应用程序使用,这和当前或者将来系统定义的section
      类型是不矛盾的。

    其他section类型的变量是保留的。前面提到过,索引0(SHN_UNDEF)的section
    头存在的,甚至索引标记的是未定义的section引用。这个入口保存着以下的
    信息。

    + 图1-11: Section Header Table Entry: Index 0

      Name            Value    Note
      ====            =====    ====
      sh_name           0      No name
      sh_type        SHT_NULL  Inactive
      sh_flags          0      No flags
      sh_addr           0      No address
      sh_offset         0      No file offset
      sh_size           0      No size
      sh_link SHN_UNDEF  No link information
      sh_info     0      No auxiliary information
      sh_addralign     0      No alignment
      sh_entsize        0      No entries

    一个section报头(section header table)的sh_flags成员保存着1位标记,
    用来描述section的属性。以下是定义的值;其他的值保留。

    + 图1-12: Section Attribute Flags, sh_flags

      Name                Value
      ====                =====
      SHF_WRITE             0x1
      SHF_ALLOC             0x2
      SHF_EXECINSTR         0x4
      SHF_MASKPROC   0xf0000000

    假如在sh_flags中的一个标记位被设置,该section相应的属性也被打开。
    否则,该属性没有被应用。未明的属性就设为0。

    * SHF_WRITE

      该section包含了在进程执行过程中可被写的数据。

    * SHF_ALLOC

      该section在进程执行过程中占据着内存。一些控制section不存在一个
      object文件的内存映象中;对于这些sections,这个属性应该关掉。

    * SHF_EXECINSTR

      该section包含了可执行的机器指令。

    * SHF_MASKPROC

      所有的包括在这掩码中的位为特定处理语意保留的。

    在section报头中,两个成员sh_link和sh_info的解释依靠该section的类型。

    + 图1-13: sh_link and sh_info Interpretation

      sh_type      sh_link                        sh_info
      =======      =======                        =======
      SHT_DYNAMIC  The section header index of    0
                   the string table used by
                   entries in the section.
      SHT_HASH     The section header index of    0
                   the symbol table to which the
                   hash table applies.
      SHT_REL,     The section header index of    The section header index of
      SHT_RELA     the associated symbol table.   the section to which the
                                                  relocation applies.
      SHT_SYMTAB,  The section header index of    One greater than the symbol
      SHT_DYNSYM   the associated string table.   table index of the last local
                                                  symbol (binding STB_LOCAL).
      other        SHN_UNDEF                      0


    Special Sections   特殊的Sections

    不同的sections保存着程序和控制信息。下面列表中的section被系统使用,
    指示了类型和属性。

    + 图1-14: Special Sections

      Name         Type           Attributes
      ====         ====           ==========
      .bss         SHT_NOBITS     SHF_ALLOC+SHF_WRITE
      .comment     SHT_PROGBITS   none
      .data        SHT_PROGBITS   SHF_ALLOC+SHF_WRITE
      .data1       SHT_PROGBITS   SHF_ALLOC+SHF_WRITE
      .debug       SHT_PROGBITS   none
      .dynamic     SHT_DYNAMIC    see below
      .dynstr      SHT_STRTAB     SHF_ALLOC
      .dynsym      SHT_DYNSYM     SHF_ALLOC
      .fini        SHT_PROGBITS   SHF_ALLOC+SHF_EXECINSTR
      .got         SHT_PROGBITS   see below
      .hash        SHT_HASH       SHF_ALLOC
      .init        SHT_PROGBITS   SHF_ALLOC+SHF_EXECINSTR
      .interp      SHT_PROGBITS   see below
      .line        SHT_PROGBITS   none
      .note        SHT_NOTE       none
      .plt         SHT_PROGBITS   see below
      .rel<name>   SHT_REL        see below
      .rela<name>  SHT_RELA       see below
      .rodata      SHT_PROGBITS   SHF_ALLOC
      .rodata1     SHT_PROGBITS   SHF_ALLOC
      .shstrtab    SHT_STRTAB     none
      .strtab      SHT_STRTAB     see below
      .symtab      SHT_SYMTAB     see below
      .text        SHT_PROGBITS   SHF_ALLOC+SHF_EXECINSTR

    * .bss

      该sectiopn保存着未初始化的数据,这些数据存在于程序内存映象中。
      通过定义,当程序开始运行,系统初始化那些数据为0。该section不占
      文件空间,正如它的section类型SHT_NOBITS指示的一样。
      
    * .comment

       该section保存着版本控制信息。

    * .data and .data1
      这些sections保存着初始化了的数据,那些数据存在于程序内存映象中。

    * .debug

      该section保存着为标号调试的信息。该内容是未指明的。

    * .dynamic

      该section保存着动态连接的信息。该section的属性将包括SHF_ALLOC位。
      是否需要SHF_WRITE是跟处理器有关。第二部分有更详细的信息。

    * .dynstr

      该section保存着动态连接时需要的字符串,一般情况下,名字字符串关联着
      符号表的入口。第二部分有更详细的信息。

    * .dynsym

      该section保存着动态符号表,如“Symbol Table”的描述。第二部分有更
      详细的信息。
      
    * .fini

      该section保存着可执行指令,它构成了进程的终止代码。
      因此,当一个程序正常退出时,系统安排执行这个section的中的代码。

    * .got

      该section保存着全局的偏移量表。看第一部分的“Special Sections”和
      第二部分的“Global Offset Table”获得更多的信息。

    * .hash

      该section保存着一个标号的哈希表。看第二部分的“Hash Table”获得更多
      的信息。

    * .init

      该section保存着可执行指令,它构成了进程的初始化代码。
      因此,当一个程序开始运行时,在main函数被调用之前(c语言称为main),
      系统安排执行这个section的中的代码。

    * .interp

      该section保存了程序的解释程序(interpreter)的路径。假如在这个section
      中有一个可装载的段,那么该section的属性的SHF_ALLOC位将被设置;否则,
      该位不会被设置。看第二部分获得更多的信息。

    * .line

      该section包含编辑字符的行数信息,它描述源程序与机器代码之间的对于
      关系。该section内容不明确的。

    * .note

      该section保存一些信息,使用“Note Section”(在第二部分)中提到的格式。

    * .plt

      该section保存着过程连接表(Procedure Linkage Table)。看第一部分的
      ``Special Sections''和第二部分的“Procedure Linkage Table”。

    * .rel<name> and .rela<name>

      这些section保存着重定位的信息,看下面的``Relocation''描述。
      假如文件包含了一个可装载的段,并且这个段是重定位的,那么该section的
      属性将设置SHF_ALLOC位;否则该位被关闭。按照惯例,<name>由重定位适用
      的section来提供。因此,一个重定位的section适用的是.text,那么该名字
      就为.rel.text或者是.rela.text。

    * .rodata and .rodata1

      这些section保存着只读数据,在进程映象中构造不可写的段。看第二部分的
      ``Program Header''获得更多的资料。

    * .shstrtab

      该section保存着section名称。

    * .strtab

      该section保存着字符串,一般地,描述名字的字符串和一个标号的入口相关
      联。假如文件有一个可装载的段,并且该段包括了符号字符串表,那么section
      的SHF_ALLOC属性将被设置;否则不设置。

    * .symtab

      该section保存着一个符号表,正如在这个section里``Symbol Table''的
      描述。假如文件有一个可装载的段,并且该段包含了符号表,那么section
      的SHF_ALLOC属性将被设置;否则不设置。

    * .text

      该section保存着程序的``text''或者说是可执行指令。


    前缀是点(.)的section名是系统保留的,尽管应用程序可以用那些保留的
    section名。应用程序可以使用不带前缀的名字以避免和系统的sections
    冲突。object文件格式可以让一个定义的section部分不出现在上面的列
    表中。一个object文件可以有多个同样名字的section。

    为处理器体系保留的section名的形成是通过置换成一个体系名的缩写。
    该名字应该取自体系名,e_machine使用的就是。例如,.Foo.psect就是
    在FOO体系上定义的名字。

    现存的扩展名是历史遗留下来的。

           Pre-existing Extensions
           =======================
    .sdata     .tdesc
    .sbss      .lit4
    .lit8      .reginfo
    .gptab     .liblist
    .conflict


       =================== String Table 字符串表=========================


    String table sections 保存着以NULL终止的一系列字符,一般我们称为字
    符串。object文件使用这些字符串来描绘符号和section名。一个字符串的
    参考是一个string table section的索引。第一个字节,即索引0,被定义保
    存着一个NULL字符。同样的,一个string table的最后一个字节保存着一个
    NULL字符,所有的字符串都是以NULL终止。索引0的字符串是没有名字或者说
    是NULL,它的解释依靠上下文。一个空的string table section是允许的;
    它的section header的成员sh_size将为0。对空的string table来说,非0的
    索引是没有用的。

    一个 settion 头的 sh_name 成员保存了一个对应于该 setion 头字符表部分
    的索引(就象ELF头的 e_shstrndx 成员所特指的那样。下表列出了一个有 25 字节
    的字符串表(这些字符串和不同的索引相关联):


           Index   +0   +1   +2   +3   +4   +5   +6   +7   +8   +9
           =====   ==   ==   ==   ==   ==   ==   ==   ==   ==   ==
              0    /0   n    a    m    e    .    /0   V    a    r     
             10    i    a    b    l    e    /0   a    b    l    e
             20    /0   /0   x    x    /0


    + Figure 1-15: String Table Indexes

      Index   String
      =====   ======
          0   none
          1   "name."
          7   "Variable"
         11   "able"
         16   "able"
         24   null string


    如上所示,一个字符串表可能涉及该 section 中的任意字节。一个字符串可能 
    引用不止一次;引用子串的情况是可能存在的;一个字符串也可能被引用若干次;而
    不被引用的字符串也是允许存在的。

       ==================== Symbol Table 符号表=========================


    一个object文件的符号表保存了一个程序在定位和重定位时需要的定义和引用的信息。
    一个符号表索引是相应的下标。0表项特指了该表的第一个入口,就象未定义的符号
    索引一样。初始入口的内容在该 section 的后续部分被指定。

                                 Name       Value
                                 ====       =====
         STN_UNDEF      0

    一个符号表入口有如下的格式:

    + Figure 1-16: Symbol Table Entry

      typedef struct {
          Elf32_Word st_name;
          Elf32_Addr st_value;
          Elf32_Word st_size;
          unsigned char st_info;
          unsigned char st_other;
          Elf32_Half st_shndx;
      } Elf32_Sym;

    * st_name

      该成员保存了进入该object文件的符号字符串表入口的索引(保留了符号名的表达字符)。  
      如果该值不为 0 ,则它代表了给出符号名的字符串表索引。否则,该符号无名。

    注意:External C 符号和object文件的符号表有相同的名称。

    * st_value

      该成员给出了相应的符号值。它可能是绝对值或地址等等(依赖于上下文);
      细节如下所述。

    * st_size

      许多符号和大小相关。比如,一个数据对象的大小是该对象所包含的字节数目。
      如果该符号的大小未知或没有大小则这个成员为 0 。

    * st_info

      成员指出了符号的类型和相应的属性。相应的列表如下所示。下面的代码说明了
      如何操作该值。

        #define ELF32_ST_BIND(i) ((i)>>4)
        #define ELF32_ST_TYPE(i) ((i)&0xf)
        #define ELF32_ST_INFO(b, t) (((b)<<4)+((t)&0xf))

    * st_other

      该成员目前为 0 ,没有含义。

    * st_shndx

      每一个符号表的入口都定义为和某些 section 相关;该成员保存了相关的 section
      头索引。就象 Figure 1-8 {*}和相关的文字所描述的那样,某些 section 索引
      指出了特殊的含义。

    一个符号的属性决定了可链接性能和行为。

    + Figure 1-17: Symbol Binding, ELF32_ST_BIND

      Name        Value
      ====        =====
      STB_LOCAL       0
      STB_GLOBAL      1
      STB_WEAK        2
      STB_LOPROC     13
      STB_HIPROC     15

    * STB_LOCAL

      在包含了其定义的object文件之外的局部符号是不可见的。不同文件中的具有相同
      名称的局部符号并不相互妨碍。

    * STB_GLOBAL

      全局符号是对所有的object目标文件可见的。一个文件中的全局符号的定义可以
      满足另一个文件中对(该文件中)未定义的全局符号的引用。 

    * STB_WEAK

      弱符号相似于全局符号,但是他们定义的优先级比较低一些。

    * STB_LOPROC through STB_HIPROC

      其所包含范围中的值由相应的处理器语义所保留。

    全局符号和弱符号的区别主要在两个方面。

    * 当链接器链接几个可重定位的目标文件时,它不允许 STB_GLOBAL 符号的同名
    多重定义。另一方面,如果一个全局符号的定义存在则具有相同名称的弱符号名不会
    引起错误。链接器将认可全局符号的定义而忽略弱符号的定义。与此相似,如果有一个
    普通符号(比如,一个符号的 st_shndx 域包含 SHN_COMMON),则一个同名的弱符号
    不会引起错误。链接器同样认可普通符号的定义而忽略弱符号。

    * 当链接器搜索档案库的时候,它选出包含了未定义的全局符号的存档成员。该成员
    的定义或者是全局的或者是一个弱符号。链接器不会为了解决一个未定义的弱符号
    选出存档成员。未定义的弱符号具有 0 值。

    在每一个符号表中,所有具有 STB_LOCAL 约束的符号优先于弱符号和全局符号。
    就象上面 "sections" 中描述的那样,一个符号表部分的 sh_info 头中的成员
    保留了第一个非局部符号的符号表索引。

    符号的类型提供了一个为相关入口的普遍分类。

    + Figure 1-18: Symbol Types, ELF32_ST_TYPE

      Name         Value
      ====         =====
      STT_NOTYPE       0
      STT_OBJECT       1
      STT_FUNC         2
      STT_SECTION      3
      STT_FILE         4
      STT_LOPROC      13
      STT_HIPROC      15

    * STT_NOTYPE

      该符号的类型没有指定。

    * STT_OBJECT

      该符号和一个数据对象相关,比如一个变量、一个数组等。

    * STT_FUNC

      该符号和一个函数或其他可执行代码相关。

    * STT_SECTION

      该符号和一个 section 相关。这种类型的符号表入口主要是为了重定位,一般的
      具有 STB_LOCAL 约束。


    * STT_FILE

      按惯例而言,该符号给出了和目标文件相关的源文件名称。一个具有 STB_LOCAL 
      约束的文件符号,其 section 索引为 SHN_ABS ,并且它优先于当前对应该文件的
      其他 STB_LOCAL 符号。

    * STT_LOPROC through STT_HIPROC

      该范围中的值是为处理器语义保留的。

    共享文件中的函数符号(具有 STT_FUNC 类型)有特殊的意义。当其他的目标文件
    从一个共享文件中引用一个函数时,链接器自动的为引用符号创建一个链接表。除了
    STT_FUNC 之外,共享的目标符号将不会自动的通过链接表引用。


    如果一个符号涉及到一个 section 的特定定位,则其 section 索引成员 st_shndx
    将保留一个到该 section 头的索引。当该 section 在重定位过程中不断
    移动一样,符号的值也相应变化,而该符号的引用在程序中指向同样的定位。某些
    特殊的 section 索引有其他的语义。


    * SHN_ABS

      该符号有一个不会随重定位变化的绝对值。

    * SHN_COMMON

      该符号标识了一个没有被分配的普通块。该符号的值给出了相应的系统参数,就象
      一个 section 的 sh_addralign 成员。也就是说,链接器将分配一个地址给
      该符号,地址的值是 st_value 的倍数。该符号的大小指出了需要的字节数。
     

    * SHN_UNDEF

      该 section 表索引表明该符号是未定义的。当链接器将该目标文件和另一个定义
      该符号的文件相装配的时候,该文件内对该符号的引用将链接到当前实际的定义。

    如上所述,符号表的 0 索引(STN_UNDEF)是保留的,它包含了如下内容:

    + Figure 1-19: Symbol Table Entry: Index 0

      Name        Value    Note
      ====        =====    ====
      st_name       0      No name
      st_value      0      Zero value
      st_size       0      No size
      st_info       0      No type, local binding
      st_other      0
      st_shndx  SHN_UNDEF  No section


        Symbol Values(符号值)

    符号表入口对于不同的目标文件而言对 st_value 成员有一些不同的解释。

    * 在可重定位文件中, st_value 保存了 section 索引为 SHN_COMMON 符号
    的强制对齐值。

    * 在可重定位文件中, st_value 保存了一个符号的 section 偏移。也就是说,
    st_value 是从 st_shndx 定义的 section 开头的偏移量。

    * 在可执行的和可共享的目标文件中, st_value 保存了一个虚拟地址。为了使
    这些文件符号对于动态链接器更为有效,文件层面上的 section 偏移让位于内存
    层面上的虚拟地址( section 编号无关的)。


    尽管符号表值对于不同的目标文件有相似的含义,相应的程序还是可以有效地访问数据。


       ====================== Relocation (重定位)==========================


    重定位是连接符号引用和符号定义的过程。比如,当一个程序调用一个函数的时候,
    相关的调用必须在执行时把控制传送到正确的目标地址。换句话说,重定位文件应当
    包含有如何修改他们的 section 内容的信息,从而允许可执行文件或共享目标文件
    为一个进程的程序映像保存正确的信息。重定位入口就是这样的数据。

    + Figure 1-20: Relocation Entries

      typedef struct {
          Elf32_Addr r_offset;
          Elf32_Word r_info;
      } Elf32_Rel;

      typedef struct {
          Elf32_Addr r_offset;
          Elf32_Word r_info;
          Elf32_Sword r_addend;
      } Elf32_Rela;

    * r_offset

      该成员给出了应用重定位行为的地址。对于一个重定位文件而言,该值是从该
      section 开始处到受到重定位影响的存储单位的字节偏移量。对一个可执行文件
      或一个共享目标而言,该值是受到重定位影响的存储单位的虚拟地址。


    * r_info

      该成员给出了具有受重定位影响因素的符号表索引和重定位应用的类型。比如,
      一个调用指令的重定位入口应当包含被调用函数的符号索引。如果该索引是 
      STN_UNDEF (未定义的符号索引),重定位将使用 0 作为该符号的值。重定位
      类型是和处理器相关的。当正文(text)引用到一个重定位入口的重定位类型或符
      号表索引,它表明相应的应用 ELF32_R_TYPE或 ELF32_R_SYM 于入口的 r_info 
      成员。

        #define ELF32_R_SYM(i) ((i)>>8)
        #define ELF32_R_TYPE(i) ((unsigned char)(i))
        #define ELF32_R_INFO(s, t) ((s)<<8+(unsigned char)(t))

    * r_addend

      该成员指定一个常量加数(用于计算将要存储于重定位域中的值)。

      如上所述,只有 Elf32_Rela 入口包含一个明确的加数。Elf32_Rel 类型
      的入口在可以修改的地址中存储一个隐含的加数。依赖于处理器结构,一种形式
      或其他形式也许是必须的或更为方便的。因此,特定机器的应用应当使用一种排他
      性的形式或依赖于上下文的另一种形式。

      一个重定位 section 关联了两个其他的 section :一个符号表和一个可修改
      的 section 。该 section 头的成员 sh_info 和 sh_link (在上文中的
      “ section ”部分中有描述)指示了这种关系。重定位入口中的成员 r_offset
      对于不同的目标文件有少许差异。

    * 在可重定位文件中,r_offset 表示了一个 section 偏移。也就是说,重定位
      section自己描述了如何修改其他在文件中的其他section; 重定位偏移量指
      明了一个在第二个section中的存储器单元。

    * 在可执行和共享的目标文件中,r_offset 表示一个虚拟地址。为了使得这些
      文件的重定位入口更为有用(对于动态链接器而言),该 section 偏移(文件
      中)应当让位于一个虚拟地址(内存中的)。

    尽管为了允许相关的程序更为有效的访问而让 r_offset 的解释对于不同的目标
    文件有所不同,重定位类型的含义是相同的。


       Relocation Types(重定位类型)

    重定位入口描述了怎样变更下面的指令和数据域(位数在表的两边角下)。

    + Figure 1-21: Relocatable Fields

        +---------------------------+
        |          word32           |
       31---------------------------0


    * word32

      指定一个以任意字节对齐方式占用 4 字节的 32 位域。这些值使用与 32 位 Intel
      体系相同的字节顺序。

                               3------2------1------0------+
         0x01020304    |  01  |  02  |  03  |  04  |
                              31------+------+------+------0

    下面的计算假设正在将一个可重定位文件转换为一个可执行或共享的目标文件。
    从概念上来说,链接器合并一个或多个可重定位文件来组成输出。它首先决定
    怎样合并、定位输入文件,然后更新符号值,最后进行重定位。对于可执行文件
    和共享的目标文件而言,重定位过程是相似的并有相同的结果。下面的描述使用
    如下的约定符号。

    * A

      表示用于计算可重定位的域值的加数。

    * B

      表示了在执行过程中一个共享目标被加载到内存时的基地址。一般情况下,一个
      共享object文件使用的基虚地址为0,但是一个可执行地址就跟共享object文件
      不同了。

    * G

      表示了在执行过程中重定位入口符号驻留在全局偏移表中的偏移。请参阅
      第二部分中的“ Global Offset Table (全局偏移表)”获得更多
      的信息。 

    * GOT

      表示了全局偏移表的地址。请参阅第二部分中的“ Global Offset Table 
      (全局偏移表)”获得更多的信息。

    * L

      表示一个符号的过程链接表入口的位置( section 偏移或地址)。一个过程
      链接表入口重定位一个函数调用到正确的目的单元。链接器创建初始的链接表,
      而动态链接器在执行中修改入口。
      请参阅第二部分中的“ Procedure Linkage Table (过程链接表)”获得更多
      的信息

    * P

      表示(section 偏移或地址)被重定位的存储单元位置(使用 r_offset 计算的)。

    * S

      表示索引驻留在重定位入口处的符号值。

    一个重定位入口的 r_offset 值指定了受影响的存储单元的首字节的偏移
    或虚拟地址。重定位类型指定了哪一位(bit)将要改变,以及怎样计算它们的值。
    在 SYSTEM V 体系中仅仅使用 Elf32_Rel 重定位入口,将要被重定位的域中
    保留了加数。在所有的情况下,加数和计算结果使用相同字节顺序。

    + Figure 1-22(表 1-22): Relocation Types(重定位类型)

      Name            Value  Field   Calculation
      ====            =====  =====   ===========
      R_386_NONE        0    none    none
      R_386_32     1    word32  S + A
      R_386_PC32     2    word32  S + A - P
      R_386_GOT32     3    word32  G + A - P
      R_386_PLT32     4    word32  L + A - P
      R_386_COPY     5    none    none
      R_386_GLOB_DAT    6    word32  S
      R_386_JMP_SLOT    7    word32  S
      R_386_RELATIVE    8    word32  B + A
      R_386_GOTOFF     9    word32  S + A - GOT
      R_386_GOTPC    10    word32  GOT + A - P

    有的重定位类型有不同于简单计算的语义。

    * R_386_GOT32

      这种重定位类型计算全局偏移表基地址到符号的全局偏移表
      入口之间的间隔。这样另外通知了 link editor 建立一个全局偏移表 。


    * R_386_PLT32

      这种重定位类型计算符号的过程链接表入口地址,并另外通知链接器建立一个
      过程链接表。


    * R_386_COPY

      链接器创建该重定位类型用于动态链接。它的偏移成员涉及一个可写段中的一个
      位置。符号表索引指定一个可能存在于当前 object file 或在一个shared object
      中的符号。在执行过程中,动态链接器把和 shared object 符号相关的数据
      拷贝到该偏移所指定的位置。

    * R_386_GLOB_DAT

      这种重定位类型用于设置一个全局偏移表入口为指定符号的地址。该特定的重定位
      类型允许你决定符号和全局偏移表入口之间的一致性。


    * R_386_JMP_SLOT {*}

      链接器创建该重定位类型用于动态链接。其偏移成员给出了一个过程链接表入口的
      位置。动态链接器修改该过程链接表入口以便向特定的符号地址传递控制。
      [参阅第二部分中的 "Procedure Linkage Table(过程链接表)"]

    * R_386_RELATIVE

      链接器创建该重定位类型用于动态链接。其偏移成员给出了包含表达相关地址值
      的一个 shared object 中的位置。动态链接器计算相应的虚拟地址(把该
      shared object 装载地址和相对地址相加)。该类型的重定位入口必须为
      符号表索引指定为 0 。


    * R_386_GOTOFF

      这种重定位类型计算符号值和全局偏移表地址之间的不同。另外还通知链接器
      建立全局偏移表(GOT)。

     

    * R_386_GOTPC

      这种重定位类型类似于 R_386_PC32 ,不同的是它在计算中使用全局偏移表。
      这种重定位中引用的符号通常是 _GLOBAL_OFFSET_TABLE_ ,该符号通知了
      链接器建立全局偏移表(GOT)。

       ________________________________________________________________


    2. PROGRAM LOADING AND DYNAMIC LINKING
                       程序装入和动态链接
       ________________________________________________________________


       ======================== Introduction(介绍) =========================


    第二部分描述了 object file 信息和创建运行程序的系统行为。其中部分信息
    适合所有的系统,其他信息是和特定处理器相关的。


    可执行和共享的 object file 静态的描绘了程序。为了执行这样的程序,系统
    用这些文件创建动态的程序表现,或进程映像。一个进程映像有用于保存其代码、
    数据、堆栈等等的段。这个部分的主要章节讨论如下的内容。


    * 程序头(Program header)。该章节补充第一部分,描述和程序运行相关的
      object file 结构。即文件中主要的数据结构、程序头表、定位段映像,也
      包含了为该程序创建内存映像所需要的信息。

    * 载入程序(Program loading)。在给定一个 object file 时,系统为了
      让它运行必须将它载入内存。

    * 动态链接(Dynamic linking)。在载入了程序之后,系统必须通过解决组
      成该进程的 object file之间的符号引用问题来完成进程映像的过程。

    注意:指定了处理器范围的 ELF 常量是有命名约定的。比如,DT_ , PT_ ,
    用于特定处理器扩展名,组合了处理器的名称(如 DT_M32_SPECIAL )。
    没有使用这种约定但是预先存在的处理器扩展名是允许的。


           Pre-existing Extensions
                           (预先存在的扩展名)
           =======================
          DT_JMP_REL


       ====================== Program Header(程序头)  ======================


    一个可执行的或共享的 object file 的程序头表是一个结构数组,每一个
    结构描述一个段或其他系统准备执行该程序所需要的信息。一个 object file
    段包含一个或多个部分(就象下面的“段目录”所描述的那样)。程序头仅仅对于
    可执行或共享的 object file 有意义。一个文件使用 ELF 头的 e_phentsize
    和 e_phnum 成员来指定其拥有的程序头大小。[参阅 第一部分中的 "ELF 头"]


    + Figure 2-1: Program Header

      typedef struct {
          Elf32_Word p_type;
          Elf32_Off p_offset;
          Elf32_Addr p_vaddr;
          Elf32_Addr p_paddr;
          Elf32_Word p_filesz;
          Elf32_Word p_memsz;
          Elf32_Word p_flags;
          Elf32_Word p_align;
      } Elf32_Phdr;

    * p_type

      该成员指出了这个数组的元素描述了什么类型的段,或怎样解释该数组元素的信息。
      类型值和含义如下所述。

    * p_offset

      该成员给出了该段的驻留位置相对于文件开始处的偏移。

    * p_vaddr

      该成员给出了该段在内存中的首字节地址。

    * p_paddr

      在物理地址定位有关联的系统中,该成员是为该段的物理地址而保留的。由于
      System V 忽略了应用程序的物理地址定位,该成员对于可执行文件和共享的
      object 而言是未指定内容的。


    * p_filesz

      该成员给出了文件映像中该段的字节数;它可能是 0 。
      
    * p_memsz

      该成员给出了内存映像中该段的字节数;它可能是 0 。

    * p_flags

      该成员给出了和该段相关的标志。定义的标志值如下所述。

    * p_align

      就象在后面“载入程序”部分中所说的那样,可载入的进程段必须有合适的
      p_vaddr 、 p_offset 值,取页面大小的模。该成员给出了该段在内存和
      文件中排列值。 0 和 1 表示不需要排列。否则, p_align 必须为正的 2 的幂,
      并且 p_vaddr 应当等于 p_offset 模 p_align 。


    某些入口描述了进程段;其他的则提供补充信息并且无益于进程映像。已经
    定义的入口可以以任何顺序出现,除非是下面明确声明的。后面是段类型值;
    其他的值保留以便将来用于其他用途。


    + Figure 2-2: Segment Types, p_type

      Name             Value
      ====             =====
      PT_NULL              0
      PT_LOAD              1
      PT_DYNAMIC           2
      PT_INTERP            3
      PT_NOTE              4
      PT_SHLIB             5
      PT_PHDR              6
      PT_LOPROC   0x70000000
      PT_HIPROC   0x7fffffff

    * PT_NULL

      该数组元素未使用;其他的成员值是未定义的。这种类型让程序头表忽略入口。

    * PT_LOAD

      该数组元素指定一个可载入的段,由 p_filesz 和 p_memsz 描述。文件中
      字节被映射到内存段中。如果该段的内存大小( p_memsz )比文件大小( p_filesz )
      要大,则多出的字节将象段初始化区域那样保持为 0 。文件的大小不会比内存大小值大。
      在程序头表中,可载入段入口是以 p_vaddr 的升序排列的。

    * PT_DYNAMIC

      该数组元素指定动态链接信息。参阅 后面的“动态部分”以获得更多信息。

    * PT_INTERP

      该数组元素指定一个 null-terminated 路径名的位置和大小(作为解释程序)。
      这种段类型仅仅对可执行文件有意义(尽管它可能发生在一个共享 object 上);
      它在一个文件中只能出现一次。如果它出现,它必须先于任何一个可载入段入口。
      参阅 后面的“程序解释器”(Program Interpreter)以获得更多的信息。


    * PT_NOTE

      该数组元素指定辅助信息的位置和大小。参阅 后面的“注意部分”以获得细节。

    * PT_SHLIB

      该段类型保留且具有未指定的语义。具有一个这种类型数组元素的程序并不
      遵守 ABI 。

    * PT_PHDR

      该数组元素(如果出现),指定了程序头表本身的位置和大小(包括在文件中
      和在该程序的内存映像中)。更进一步来说,它仅仅在该程序头表是程序内存映像
      的一部分时才有效。如果它出现,它必须先于任何可载入段入口。参阅 后面的
      “程序解释器”(Program Interpreter)以获得更多的信息。


    * PT_LOPROC through PT_HIPROC

      该范围中的值保留用于特定处理器的语义。

    注意:除非在别处的特殊要求,所有的程序头的段类型是可选的。也就是说,
    一个文件的程序头表也许仅仅包含和其内容相关的元素。

     

      Base Address(基地址)

    可执行和共享的 object file 有一个基地址,该基地址是与程序的 object file
    在内存中映像相关的最低虚拟地址。基地址的用途之一是在动态链接过程中重定位
    该程序的内存映像。


    一个可执行的 object file 或 一个共享的 object file 的基地址是在
    执行的时候从三个值计算而来的:内存载入地址、页面大小的最大值 和 程序可
    载入段的最低虚拟地址。就象在“程序载入”中所描述的那样,程序头中的虚拟地址
    也许和程序的内存映像中实际的虚拟地址并不相同。为了计算基地址,必须确定与
    PT_LOAD 段 p_vaddr 的最小值相关的内存地址。获得基地址的方法是将内存
    地址截去最大页面大小的最接近的整数倍。由于依赖载入内存中的文件类型,
    该内存地址和 p_vaddr 值可能匹配也可能不匹配。


    就象在第一部分中 "Section" 中描述的那样, .bss section 具有 SHT_NOBITS
    的类型。尽管在文件中不占用空间,它在段的内存映像中起作用。通常,没有初始化
    的数据驻留在段尾,因此使得在相关的程序头元素中的 p_memsz 比 p_filesz 大。


      Note Section(注解部分)

    有的时候供应商或系统设计者需要用特定的信息标记一个
    object file 以便其他程序检查其兼容的一致性,等等此类。 SHT_NOTE 
    类型的 section 和 PT_NOTE 类型的程序头元素能够被用于此目的。 section 
    和程序头中的注解信息包含了任意数目的入口,每一个入口的格式都是对应于特定
    处理器格式的 4-字节数组。下面的标签有助于解释注释信息的组织形式,但是这些
    标签不是规格说明的一部分。


    + Figure 2-3: Note Information

      namesz
      descsz
      type
      name ...
      desc ...

    * namesz and name

      名字中 namesz 的第一个字节包含了一个 null-terminated 字符
      表达了该入口的拥有者或始发者。没有正式的机制来避免名字冲突。从
      惯例来说,供应商使用他们自己的名称,比如 "XYZ Computer Company" ,
      作为标志。如果没有提供名字, namesz 值为 0 。 如果有必要,确定
      描述信息4-字节对齐。 这样的填充信息并不包含在namesz 中。


    * descsz and desc

      desc 中 descsz 的首字节包含了注解描述符。ABI 不会在一个描述符内容中
      放入任何系统参数。如果没有描述符, descsz 将为 0 。  如果有必要,确定
      描述信息4-字节对齐。 这样的填充信息并不包含在descsz中。


    * type

      该 word 给出了描述符的解释。每一个创造着(originator) 控制着自己的类型;
      对于单单一个类型值的多种解释是可能存在的。因此,一个程序必须辨认出该名字
      和其类型以便理解一个描述符。这个时候的类型必须是非负的。ABI 没有定义
      描述符的含义。

    为了举例说明,下面的解释段包含两个入口。

    + Figure 2-4: Example Note Segment

               +0   +1   +2   +3
              -------------------
      namesz           7
      descsz           0           No descriptor
        type           1
        name   X    Y    Z    spc 
               C    o    /0   pad
      namesz           7
      descsz           8
        type           3
        name   X    Y    Z    spc
               C    o    /0   pad
        desc         word0
                     word1

    注意:系统保留的注解信息没有名字 (namesz==0) ,有一个零长度的名字
    (name[0]=='/0') 现在还没有类型为其定义。所有其他的名字必须至少有
    一个非空的字符。


    注意:注解信息是可选的。注解信息的出现并不影响一个程序的 ABI 一致性,
    前提是该信息不影响程序的执行行为。否则,该程序将不遵循 ABI 并将出现
    未定义的行为。


       ===================== Program Loading(程序载入) =====================

    当创建或增加一个进程映像的时候,系统在理论上将拷贝一个文件的段到一个虚拟
    的内存段。系统什么时候实际地读文件依赖于程序的执行行为,系统载入等等。一个
    进程仅仅在执行时需要引用逻辑页面的时候才需要一个物理页面,实际上进程通常会
    留下许多未引用的页面。因此推迟物理上的读取常常可以避免这些情况,改良系统的
    特性。为了在实践中达到这种效果,可执行的和共享的 object file 必须具有
    合适于页面大小取模值的文件偏移和虚拟地址这样条件的段映像。

    虚拟地址和文件偏移在 SYSTEM V 结构的段中是模 4KB(0x1000) 或大的 2 的幂。
    由于 4KB 是最大的页面大小,因此无论物理页面大小是多少,文件必须去适合页面。


    + Figure 2-5: Executable File

               File Offset   File                  Virtual Address
               ===========   ====                  ===============
                         0   ELF header
      Program header table
                             Other information
                     0x100   Text segment          0x8048100
                             ...
                             0x2be00 bytes         0x8073eff
                   0x2bf00   Data segment          0x8074f00
                             ...
                             0x4e00 bytes          0x8079cff
                   0x30d00   Other information
                             ...

    + Figure 2-6: Program Header Segments(程序头段)

      Member    Text Data
      ======    ====         ====
      p_type    PT_LOAD      PT_LOAD
      p_offset  0x100 0x2bf00
      p_vaddr   0x8048100 0x8074f00
      p_paddr   unspecified unspecified
      p_filesz  0x2be00 0x4e00
      p_memsz   0x2be00 0x5e24
      p_flags   PF_R+PF_X    PF_R+PF_W+PF_X
      p_align   0x1000 0x1000

    尽管示例中的文件偏移和虚拟地址在文本和数据两方面都适合模 4KB ,但是还有
    4 个文件页面混合了代码和数据(依赖于页面大小和文件系统块的大小)。


    * 第一个文本页面包含了 ELF 头、程序头以及其他信息。
    * 最后的文本页包含了一个数据开始的拷贝。
    * 第一个数据页面有一个文本结束的拷贝。
    * 最后的数据页面也许会包含与正在运行的进程无关的文件信息。

    理论上,系统强制内存中段的区别;段地址被调整为适应每一个逻辑页面在地址空间
    中有一个简单的准许集合。在上面的示例中,包含文本结束和数据开始的文件区域将
    被映射两次:在一个虚拟地址上为文本而另一个虚拟地址上为数据。


    数据段的结束处需要对未初始化的数据进行特殊处理(系统定义的以 0 值开始)。
    因此如果一个文件包含信息的最后一个数据页面不在逻辑内存页面中,则无关的
    数据应当被置为 0 (这里不是指未知的可执行文件的内容)。在其他三个页面中
    "Impurities" 理论上并不是进程映像的一部分;系统是否擦掉它们是未指定的。
    下面程序的内存映像假设了 4KB 的页面。


    + Figure 2-7: Process Image Segments(进程映像段)

      Virtual Address  Contents            Segment
      ===============  ========            =======
            0x8048000  Header padding      Text
                       0x100 bytes
            0x8048100  Text segment
                       ...
                       0x2be00 bytes
            0x8073f00  Data padding
                       0x100 bytes
            0x8074000  Text padding        Data
                       0xf00 bytes
            0x8074f00  Data segment
                       ...
                       0x4e00 bytes
            0x8079d00  Uninitialized data
                       0x1024 zero bytes
            0x807ad24  Page padding
                       0x2dc zero bytes

    可执行文件和共享文件在段载入方面有所不同。典型地,可执行文件段包含了
    绝对代码。为了让进程正确执行,这些段必须驻留在建立可执行文件的虚拟地址
    处。因此系统使用不变的 p_vaddr 作为虚拟地址。


    另一方面,共享文件段包含与位置无关的代码。这让不同进程的相应段虚拟地址
    各不相同,且不影响执行。虽然系统为各个进程选择虚拟地址,它还要维护各个
    段的相对位置。因为位置无关的代码在段间使用相对定址,故而内存中的虚拟地址
    的不同必须符合文件中虚拟地址的不同。下表给出了几个进程可能的共享对象虚拟
    地址的分配,演示了不变的相对定位。该表同时演示了基地址的计算。


    + Figure 2-8: Example Shared Object Segment Addresses

      Sourc             Text        Data  Base Address
      =====             ====        ====  ============
      File             0x200     0x2a400           0x0
      Process 1   0x80000200  0x8002a400    0x80000000
      Process 2   0x80081200  0x800ab400    0x80081000
      Process 3   0x900c0200  0x900ea400    0x900c0000
      Process 4   0x900c6200  0x900f0400    0x900c6000


       ==================== Dynamic Linking (动态链接) =====================


    一个可执行文件可能有一个 PT_INTERP 程序头元素。在 exec(BA_OS) 的
    过程中,系统从 PT_INTERP 段中取回一个路径名并由解释器文件的段创建初始的
    进程映像。也就是说,系统为解释器“编写”了一个内存映像,而不是使用原始
    的可执行文件的段映像。此时该解释器就负责接收系统来的控制并且为应用程序
    提供一个环境变量。


    解释器使用两种方法中的一种来接收系统来的控制。首先,它会接收一个文件描述符
    来读取该可执行文件,定位于开头。它可以使用这个文件描述符来读取 并且(或者)
    映射该可执行文件的段到内存中。其次,依赖于该可执行文件的格式,系统会载入
    这个可执行文件到内存中而不是给该解释器一个文件描述符。伴随着可能的文件描述符
    异常的情况,解释器的初始进程声明应匹配该可执行文件应当收到的内容。解释器本身
    并不需要第二个解释器。一个解释器可能是一个共享对象也可能是一个可执行文件。


    * 一个共享对象(通常的情况)在被载入的时候是位置无关的,各个进程可能不同;
      系统在 mmap(KE_OS) 使用的动态段域为它创建段和相关的服务。因而,一个
      共享对象的解释器将不会和原始的可执行文件的原始段地址相冲突。


    * 一个可执行文件被载入到固定地址;系统使用程序头表中的虚拟地址为其创建段。
      因而,一个可执行文件解释器的虚拟地址可能和第一个可执行文件相冲突;这种
      冲突由解释器来解决。

     

    Dynamic Linker(动态链接器)

    当使用动态链接方式建立一个可执行文件时,链接器把一个 PT_INTERP 类型
    的元素加到可执行文件中,告诉系统把动态链接器做为该程序的解释器。

    注意:由系统提供的动态链接器是和特定处理器相关的。

    Exec(BA_OS) 和动态链接器合作为程序创建进程,必须有如下的动作:

    * 将可执行文件的内存段加入进程映像中;
    * 将共享对象的内存段加入进程映像中;
    * 为可执行文件和它的共享对象进行重定位;
    * 如果有一个用于读取可执行文件的文件描述符传递给了动态链接器,那么关闭它。
    * 向程序传递控制,就象该程序已经直接从 exec(BA_OS) 接收控制一样。

    链接器同时也为动态链接器构建各种可执行文件和共享对象文件的相关数据。就象
    在上面“程序头”中说的那样,这些数据驻留在可载入段中,使得它们在执行过程
    中有效。(再一次的,要记住精确的段内容是处理器相关的。可以参阅相应处理器
    的补充说明来获得详尽的信息。)


    * 一个具有 SHT_DYNAMIC 类型的 .dynamic section 包含各种数据。驻留在
      section 开头的结构包含了其他动态链接信息的地址。

    * SHT_HASH 类型的 .hash section 包含了一个 symbol hash table.

    * SHT_PROGBITS 类型的 .got 和 .plt section 包含了两个分离的 table:
      全局偏移表和过程链接表。 下面的 section 演示了动态链接器使用和改变
      这些表来为 object file 创建内存映像。


    由于每一个遵循 ABI 的程序从一个共享对象库中输入基本的系统服务,因此动态
    链接器分享于每一个遵循 ABI 的程序的执行过程中。


    就象在处理器补充说明的“程序载入”所解释的那样,共享对象也许会占用与记录在
    文件的程序头表中的地址不同的虚拟内存地址。动态链接器重定位内存映像,在应用程序
    获得控制之前更新绝对地址。尽管在库被载入到由程序头表指定的地址的情况下绝对地址
    应当是正确的,通常的情况却不是这样。


    如果进程环境 [see exec(BA_OS)] 包含了一个非零的 LD_BIND_NOW 变量,
    动态链接器将在控制传递到程序之前进行所有的重定位。举例而言,所有下面的
    环境入口将指定这种行为。


    * LD_BIND_NOW=1
    * LD_BIND_NOW=on
    * LD_BIND_NOW=off

    其他情况下, LD_BIND_NOW 或者不在环境中或者为空值。动态链接器可以不急于
    处理过程链接表入口,因而避免了对没有调用的函数的符号解析和重定位。参阅
    "Procedure Linkage Table"获取更多的信息。

     

       Dynamic Section(动态section)


    假如一个object文件参与动态的连接,它的程序头表将有一个类型为PT_DYNAMIC
    的元素。该“段”包含了.dynamic section。一个_DYNAMIC特别的符号,表明了
    该section包含了以下结构的一个数组。

    + Figure 2-9: Dynamic Structure

      typedef struct {
          Elf32_Sword d_tag;
          union {
              Elf32_Sword d_val;
              Elf32_Addr d_ptr;
          } d_un;
      } Elf32_Dyn;

      extern Elf32_Dyn _DYNAMIC[];

    对每一个有该类型的object,d_tag控制着d_un的解释。

    * d_val

      那些Elf32_Word object描绘了具有不同解释的整形变量。

    * d_ptr

      那些Elf32_Word object描绘了程序的虚拟地址。就象以前提到的,在执行时,
      文件的虚拟地址可能和内存虚拟地址不匹配。当解释包含在动态结构中的地址
      时是基于原始文件的值和内存的基地址。为了一致性,文件不包含在
      重定位入口来纠正在动态结构中的地址。


    以下的表格总结了对可执行和共享object文件需要的tag。假如tag被标为
    mandatory,ABI-conforming文件的动态连接数组必须有一个那样的入口。
    同样的,“optional”意味着一个可能出现tag的入口,但是不是必须的。

    + Figure 2-10: Dynamic Array Tags, d_tag

      Name               Value  d_un         Executable   Shared Object
      ====               =====  ====         ==========   =============
      DT_NULL                0  ignored mandatory    mandatory
      DT_NEEDED 1  d_val optional     optional
      DT_PLTRELSZ 2  d_val optional     optional
      DT_PLTGOT 3  d_ptr optional     optional
      DT_HASH 4  d_ptr mandatory    mandatory
      DT_STRTAB 5  d_ptr mandatory    mandatory
      DT_SYMTAB 6  d_ptr mandatory    mandatory
      DT_RELA 7  d_ptr mandatory    optional
      DT_RELASZ 8  d_val mandatory    optional
      DT_RELAENT 9  d_val mandatory    optional
      DT_STRSZ 10  d_val mandatory    mandatory
      DT_SYMENT 11  d_val mandatory    mandatory
      DT_INIT 12  d_ptr optional     optional
      DT_FINI 13  d_ptr optional     optional
      DT_SONAME 14  d_val ignored      optional
      DT_RPATH 15  d_val optional     ignored
      DT_SYMBOLIC 16  ignored ignored      optional
      DT_REL 17  d_ptr mandatory    optional
      DT_RELSZ 18  d_val mandatory    optional
      DT_RELENT 19  d_val mandatory    optional
      DT_PLTREL 20  d_val optional     optional
      DT_DEBUG 21  d_ptr optional     ignored
      DT_TEXTREL 22  ignored optional     optional
      DT_JMPREL 23  d_ptr optional     optional
      DT_LOPROC     0x70000000  unspecified  unspecified  unspecified
      DT_HIPROC     0x7fffffff  unspecified  unspecified  unspecified

    * DT_NULL

       一个DT_NULL标记的入口表示了_DYNAMIC数组的结束。

    * DT_NEEDED

      这个元素保存着以NULL结尾的字符串表的偏移量,那些字符串是所需库的名字。
      该偏移量是以DT_STRTAB 为入口的表的索引。看“Shared Object Dependencies”
      关于那些名字的更多信息。动态数组可能包含了多个这个类型的入口。那些
      入口的相关顺序是重要的,虽然它们跟其他入口的关系是不重要的。

    * DT_PLTRELSZ

      该元素保存着跟PLT关联的重定位入口的总共字节大小。假如一个入口类型
      DT_JMPREL存在,那么DT_PLTRELSZ也必须存在。

    * DT_PLTGOT

      该元素保存着跟PLT关联的地址和(或者)是GOT。具体细节看处理器补充
      (processor supplement)部分。

    * DT_HASH

      该元素保存着符号哈希表的地址,在“哈希表”有描述。该哈希表指向
      被DT_SYMTAB元素引用的符号表。

    * DT_STRTAB

      该元素保存着字符串表地址,在第一部分有描述,包括了符号名,库名,
      和一些其他的在该表中的字符串。

    * DT_SYMTAB

      该元素保存着符号表的地址,在第一部分有描述,对32-bit类型的文件来
      说,关联着一个Elf32_Sym入口。

    * DT_RELA

      该元素保存着重定位表的地址,在第一部分有描述。在表中的入口有明确的
      加数,就象32-bit类型文件的Elf32_Rela。一个object文件可能好多个重定位
      section。当为一个可执行和共享文件建立重定位表的时候,连接编辑器连接
      那些section到一个单一的表。尽管在object文件中那些section是保持独立的。
      动态连接器只看成是一个简单的表。当动态连接器为一个可执行文件创建一个
      进程映象或者是加一个共享object到进程映象中,它读重定位表和执行相关的
      动作。假如该元素存在,动态结构必须也要有DT_RELASZ和DT_RELAENT元素。
      当文件的重定位是mandatory,DT_RELA 或者 DT_REL可能出现(同时出现是
      允许的,但是不必要的)。


    * DT_RELASZ

      该元素保存着DT_RELA重定位表总的字节大小。

    * DT_RELAENT

      该元素保存着DT_RELA重定位入口的字节大小。
     

    * DT_STRSZ

      该元素保存着字符串表的字节大小。

    * DT_SYMENT

      该元素保存着符号表入口的字节大小。

    * DT_INIT

      该元素保存着初始化函数的地址,在下面“初始化和终止函数”中讨论。

    * DT_FINI

      该元素保存着终止函数的地址,在下面“初始化和终止函数”中讨论。

    * DT_SONAME

      该元素保存着以NULL结尾的字符串的字符串表偏移量,那些名字是共享
      object的名字。偏移量是在DT_STRTAB入口记录的表的索引。关于那些名字看
      Shared Object Dependencies 部分获得更多的信息。

    * DT_RPATH

      该元素保存着以NULL结尾的搜索库的搜索目录字符串的字符串表偏移量。
      在共享object依赖关系(Shared Object Dependencies)中有讨论

    * DT_SYMBOLIC

      在共享object库中出现的该元素为在库中的引用改变动态连接器符号解析的算法。
      替代在可执行文件中的符号搜索,动态连接器从它自己的共享object开始。假如
      一个共享的object提供引用参考失败,那么动态连接器再照常的搜索可执行文件
      和其他的共享object。
     

    * DT_REL

      该元素相似于DT_RELA,除了它的表有潜在的加数,正如32-bit文件类型的
      Elf32_Rel一样。假如这个元素存在,它的动态结构必须也同时要有DT_RELSZ
      和DT_RELENT的元素。

    * DT_RELSZ

      该元素保存着DT_REL重定位表的总字节大小。

    * DT_RELENT

      该元素保存着DT_RELENT重定为入口的字节大小。  

    * DT_PLTREL

      该成员指明了PLT指向的重定位入口的类型。适当地, d_val成员保存着 
      DT_REL或DT_RELA。在一个PLT中的所有重定位必须使用相同的转换。

    * DT_DEBUG

      该成员被调试使用。它的内容没有被ABI指定;访问该入口的程序不是
      ABI-conforming的。

    * DT_TEXTREL

      如在程序头表中段许可所指出的那样,这个成员的缺乏代表没有重置入
      口会引起非写段的修改。假如该成员存在,一个或多个重定位入口可能
      请求修改一个非写段,并且动态连接器能因此有准备。
     

    * DT_JMPREL

      假如存在,它的入口d_ptr成员保存着重定位入口(该入口单独关联着
      PLT)的地址。假如lazy方式打开,那么分离它们的重定位入口让动态连接
      器在进程初始化时忽略它们。假如该入口存在,相关联的类型入口DT_PLTRELSZ
      和DT_PLTREL一定要存在。

    * DT_LOPROC through DT_HIPROC

      在该范围内的变量为特殊的处理器语义保留。除了在数组末尾的DT_NULL元素,
      和DT_NEEDED元素相关的次序,入口可能出现在任何次序中。在表中不出
      现的Tag值是保留的。


      Shared Object Dependencies(共享Object的依赖关系)

    当连接器处理一个文档库时,它取出库中成员并且把它们拷贝到一个输出的
    object文件中。当运行时没有包括一个动态连接器的时候,那些静态的连接服
    务是可用的。共享object也提供服务,动态连接器必须把正确的共享object
    文件连接到要实行的进程映象中。因此,可执行文件和共享的object文件之间
    存在着明确的依赖性。

    当动态连接器为一个object文件创建内存段时,依赖关系(在动态结构的
    DT_NEEDED入口中记录)表明需要哪些object来为程序提供服务。通过
    重复的连接参考的共享object和他们的依赖关系,动态连接器可以建造一个
    完全的进程映象。当解决一个符号引用的时候,动态连接器以宽度优先搜索
    (breadth-first)来检查符号表,换句话说,它先查看自己的可实行程序
    中的符号表,然后是顶端DT_NEEDED入口(按顺序)的符号表,再接下来是
    第二级的DT_NEEDED入口,依次类推。共享object文件必须对进程是可读的;
    其他权限是不需要的。

    注意:即使当一个共享object被引用多次(在依赖列关系表中),动态连接器
    只把它连接到进程中一次。

    在依赖关系列表中的名字既被DT_SONAME字符串拷贝,又被建立object文件
    时的路径名拷贝。例如,动态连接器建立一个可执行文件(使用带DT_SONAME
    入口的lib1共享文件)和一个路径名为/usr/lib/lib2的共享object库,
    那么可执行文件将在它自己的依赖关系列表中包含lib1和/usr/bin/lib2。

    假如一个共享object名字有一个或更多的反斜杠字符(/)在这名字的如何地方,
    例如上面的/usr/lib/lib2文件或目录,动态连接器把那个字符串自己做为路径名。
    假如名字没有反斜杠字符(/),例如上面的lib1,三种方法指定共享文件的
    搜索路径,如下:

    * 第一,动态数组标记DT_RPATH保存着目录列表的字符串(用冒号(:)分隔)。
      例如,字符串/home/dir/lib:/home/dir2/lib:告诉动态连接器先搜索
      /home/dir/lib,再搜索/home/dir2/lib,再是当前目录。

    * 第二,在进程环境中(see exec(BA_OS)),有一个变量称为LD_LIBRARY_PATH
      可以保存象上面一样的目录列表(随意跟一个分号(;)和其他目录列表)。
      以下变量等于前面的例子:
        LD_LIBRARY_PATH=/home/dir/lib:/home/dir2/lib:
        LD_LIBRARY_PATH=/home/dir/lib;/home/dir2/lib:
        LD_LIBRARY_PATH=/home/dir/lib:/home/dir2/lib:;
      所以的LD_LIBRARY_PATH目录在DT_RPATH指向的目录之后被搜索。尽管一些
      程序(例如连接编辑器)不同的处理分号前和分号后的目录,但是动态连接
      不会。不过,动态连接器接受分号符号,具体语意在如上面描述。

    * 最后,如果上面的两个目录查找想要得到的库失败,那么动态连接器搜索
      /usr/lib.

    注意:出于安全考虑,动态连接器忽略set-user和set-group的程序的
    LD_LIBRARY_PATH所指定的搜索目录。但它会搜索DT_RPATH指明的目录和
    /usr/lib。


    Global Offset Table(GOT全局偏移量表)

    一般情况下,位置无关的代码不包含绝对的虚拟地址。全局偏移量表在私有数据
    中保存着绝对地址,所以应该使地址可用的,而不是和位置无关性和程序代码段
    共享能力妥协。一个程序引用它的GOT(全局偏移量表)来使用位置无关的地址并且
    提取绝对的变量,所以重定位位置无关的参考到绝对的位置。

    初始时,GOT(全局偏移量表)保存着它重定位入口所需要的信息 [看第一部分的
    “Relocation”]。在系统为一个可装载的object文件创建内存段以后,动态
    连接器处理重定位入口,那些类型为R_386_GLOB_DAT的指明了GOT(全局偏移量表)。
    动态连接器决定了相关的标号变量,计算他们的绝对地址,并且设置适当的内存
    表入口到正确的变量。虽然当连接编辑器建造object文件的时候,绝对地址
    是不知道,连接器知道所以内存段的地址并且能够因此计算出包含在那里的
    标号地址。

    假如程序需要直接访问符号的绝对地址,那么这个符号将有一个GOT(全局偏移量表)
    入口。因为可执行文件和共享文件有独立的GOT(全局偏移量表),一个符号地址
    可能出现在不同的几个表中。在交给进程映象的代码控制权以前,动态连接器处
    理所有的重定位的GOT(全局偏移量表),所以在执行时,确认绝对地址是可用的。

    该表的入口0是为保存动态结构地址保留的(参考_DYNAMIC标号)。这允许
    象动态连接程序那样来找出他们自己的动态结构(还没有处理他们的重
    定向入口)。这些对于动态连接器是重要的,因为它必要初始化自己而不
    能依赖于其他程序来重定位他们的内存映象。在32位Interl系统结构中,在
    GOT中的人口1和2也是保留的,具体看以下的过程连接表(Procedure Linkage
    Table)。

    系统可以为在不同的程序中相同的共享object选择不同的内存段;它甚至可以
    为相同的程序不同的进程选择不同的库地址。虽然如此,一旦进程映象被建立
    以后,内存段不改变地址。只要一个进程存在,它的内存段驻留在固定的虚拟
    地址。

    GOT表的格式和解释是处理器相关的。在32位Intel体系结构下,标号
    _GLOBAL_OFFSET_TABLE_可能被用来访问该表。

    + Figure 2-11: Global Offset Table

      extern Elf32_Addr _GLOBAL_OFFSET_TABLE_[];

    标号_GLOBAL_OFFSET_TABLE_可能驻留在.got section的中间,允许负的和非负
    的下标索引这个数组。


           Procedure Linkage Table(PLT过程连接表)

    就象GOT重定位把位置无关的地址计算成绝对地址一样,PLT过程连接表重定位
    位置无关的函数调用到绝对的地址。从一个可执行或者共享的object文件到另外的,
    连接编辑器不解析执行的传输(例如函数的调用)。因此,连接编辑器安排程序
    的传递控制到PLT中的入口。在SYSTEM V体系下,PLT存在共享文本中,但是它们
    使用的地址是在私有的GOT中。符号连接器决定了目标的绝对地址并且修改GOT的
    内存映象。因此,在没有危及到位置无关、程序文本的共享能力的情况下。动态
    连接器能重定位人口。


    + Figure 2-12: Absolute Procedure Linkage Table {*}
           绝对的过程连接表

      .PLT0:pushl   got_plus_4
            jmp     *got_plus_8
            nop; nop
            nop; nop
      .PLT1:jmp     *name1_in_GOT
            pushl   $offset
            jmp     .PLT0@PC
      .PLT2:jmp     *name2_in_GOT
            pushl   $offset
            jmp     .PLT0@PC
            ...

    + Figure 2-13: Position-Independent Procedure Linkage Table
           位置无关(或者说位置独立)的过程连接表
      .PLT0:pushl   4(%ebx)
            jmp     *8(%ebx)
            nop; nop
            nop; nop
      .PLT1:jmp     *name1@GOT(%ebx)
            pushl   $offset
            jmp     .PLT0@PC
      .PLT2:jmp     *name2@GOT(%ebx)
            pushl   $offset
            jmp     .PLT0@PC
            ...

    注意:如图所示,PLT的指令使用了不同的操作数地址方式,对绝对代码和
    对位置无关的代码。但是,他们的界面对于动态连接器是相同的。

    以下的步骤,动态连接器和程序协作(cooperate)通过PLT和GOT来解析符号
    引用。

    1. 当第一次创建程序的内存映象时,动态连接器为在GOT中特别的变量设置
       第二次和第三次的入口。下面关于那些变量有更多的解释。

    2. 假如PLT是位置无关的,那么GOT的地址一定是保留在%ebx中的。每个在进程
       映象中共享的object文件有它自己的PLT,并且仅仅在同一个object文件中,
       控制传输到PLT入口。从而,要调用的函数有责任在调用PLT入口前,设置PLT
       地址到寄存器中。

    3. 举例说明,假如程序调用函数name1,它的传输控制到标号.PLT1.

    4. 第一个指令跳到在GOT入口的name1地址。初始话时,GOT保存着紧跟着的push1
       指令的地址,而不是真实的name1的地址。

    5. 因此,程序在堆栈中压入(push)一个重定位的偏移量。重定位的偏移量是
       一个32位,非负的字节偏移量(从定位表算起)。指派的重定位入口将是
       一个R_386_JMP_SLOT类型,它的偏移量指明了GOT入口(在前面的jmp指令中
       被使用)。该重定位入口也包含一个符号表的索引,因此告诉动态连接器
       哪个符号要被引用,在这里是name1。

    6. 在压入(push)一个重定位的偏移量后,程序跳到.PLT0,在PLT中的第一个入口。
       push1指令在堆栈中放置第二个GOT入口(got_plus_4 or 4(%ebx))的值,
       因此,给动态连接器一个word的鉴别信息。然后程序跳到第三个GOT入口
       (got_plus_8 or 8(%ebx)),它传输控制到动态连接器。

    7. 当动态连接器接到控制权,它展开堆栈,查看指派的重定位入口,寻找符号的
       值,在GOT入口中存储真实的name1地址,然后传输控制想要目的地。

    8. PLT入口的并发执行将直接传输控制到name1,而不用第二次调用动态连接器
       了。所以,在.PLT1中的jmp指令将转到name1,代替“falling through”
       转到pushl指令。

    LD_BIND_NOW环境变量能改变动态连接器的行为。假如这个变量为非空,动态
    连接器在传输控制到程序前计算PLT入口。换句话说,动态连接器处理重定位
    类型为R_386_JMP_SLOT的入口在进程初始化时。否则,动态连接器计算PLT入口
    懒惰的,推迟到符号解析和重定位直到一个表入口的第一次执行。


    注意:一般来说,以懒惰(Lazy)方式绑定是对全应用程序执行的改进。
    因为不使用的符号就不会招致动态连接器做无用功。然而,对一些应用程序,
    两种情况使用懒惰(Lazy)方式是不受欢迎的。

    第一 初始的引用一个共享object函数比后来的调用要花的时间长,因为动
         态连接器截取调用来解析符号。一些应用程序是不能容忍这样的。
    第二 假如这个错误发生并且动态连接器不能解析该符号,动态连接器将终止
         程序。在懒惰(Lazy)方式下,这可能发生在任意的时候。一再的,一
         些应用程序是不能容忍这样的。通过关掉懒惰(Lazy)方式,在应用程
         序接到控制前,当在处理初始话时发生错误,动态连接器强迫程序,使
         之失败。

          Hash Table(哈希表)

    Elf32_Word object的哈希表支持符号表的访问。
    标号出现在下面帮助解释哈希表的组织,但是它们不是规范的一部分。

    + Figure 2-14: Symbol Hash Table

      nbucket
      nchain
      bucket[0]
      ...
      bucket[nbucket - 1]
      chain[0]
      ...
      chain[nchain - 1]


    bucket数组包含了nbucket入口,并且chain数组包含了nchain个入口;索引从0开始。
    bucket和chain保存着符号表的索引。Chain表入口类似于符号表。符号表入口的
    数目应该等于nchain;所以符号表的索引也选择chain表的入口。
    一个哈希函数(如下的)接受一个符号名并且返回一个可以被计算机使用的bucket索引
    的值。因此,假如一个哈希函数返回一些名字的值为X,那么bucket[x%nbucket]
    将给出一个索引y(既是符号表和chain表的索引)。假如符号表入口不是期望的,
    chain[y]给出下一个符号表的入口(使用相同的哈希变量)。可以沿着chain
    链直到选择到了期望名字的符号表入口或者是碰到了STN_UNDEF的入口。

    + Figure 2-15: Hashing Function

      unsigned long
      elf_hash(const unsigned char *name)
      {
          unsigned long       h = 0, g;
      
          while (*name) {
              h = (h << 4) + *name++;
              if (g = h & 0xf0000000)
                  h ^= g >> 24;
              h &= ~g;
          }
          return h;
      }


           Initialization and Termination Functions
              初始化和终止函数


    在动态连接妻建立进程映象和执行重定位以后,每一个共享object得到适当
    的机会来执行一些初始话代码。初始化函数不按特别的顺序被调用,但是
    所有的共享object初始化发生在执行程序获得控制之前。

    类似地,共享的object可能包含终止函数,它们在进程本身开始它的终止之后
    被执行(以atexit(BA_OS)的机制)。

    共享object通过设置在动态结构中的DT_INIT和DT_FINI入口来指派它们的初始化
    和终止函数,如上动态section(Dynamic Section)部分描述。典型的,那些函数
    代码存在.init和.fini section中,第一部分的“section”已经提到过。

    注意:尽管atexit(BA_OS)的终止处理一般可可正常完成,但是不保证在死进程上
    被执行。特别的,假如_exit被调用(看exit(BA_OS))或者假如进程死掉,那么
    进程是不执行终止处理的。因为它收到一个信号,该信号可捕获或忽略。

       ________________________________________________________________


         3. C LIBRARY

       ________________________________________________________________


       ========================== C Library ===========================

    C库,libc,包含了所有的符号(包含在libsys),另外,包含在在下面两个
    表中列出的运行函数。第一个表中的运行函数是ANSI C标准的。

    + Figure 3-1: libc Contents, Names without Synonyms

      abort        fputc        isprint      putc         strncmp
      abs        fputs        ispunct      putchar      strncpy
      asctime      fread        isspace      puts         strpbrk
      atof        freopen      isupper      qsort        strrchr
      atoi        frexp        isxdigit     raise        strspn
      atol        fscanf       labs         rand         strstr
      bsearch      fseek        ldexp        rewind       strtod
      clearerr     fsetpos      ldiv         scanf        strtok
      clock        ftell        localtime    setbuf       strtol
      ctime        fwrite       longjmp      setjmp       strtoul
      difftime     getc         mblen        setvbuf      tmpfile
      div        getchar      mbstowcs     sprintf      tmpnam
      fclose       getenv       mbtowc       srand        tolower
      feof        gets         memchr       sscanf       toupper
      ferror       gmtime       memcmp       strcat       ungetc
      fflush       isalnum      memcpy       strchr       vfprintf
      fgetc        isalpha      memmove      strcmp       vprintf
      fgetpos      iscntrl      memset       strcpy       vsprintf
      fgets        isdigit      mktime       strcspn      wcstombs
      fopen        isgraph      perror       strlen       wctomb
      fprintf      islower      printf       strncat   

    再加上, libc 保存着以下的服务。

    + Figure 3-2: libc Contents, Names with Synonyms

      __assert     getdate      lockf **     sleep        tell ** 
      cfgetispeed  getopt       lsearch      strdup       tempnam
      cfgetospeed  getpass      memccpy      swab         tfind
      cfsetispeed  getsubopt    mkfifo       tcdrain      toascii
      cfsetospeed  getw         mktemp       tcflow       _tolower
      ctermid      hcreate      monitor      tcflush      tsearch
      cuserid      hdestroy     nftw         tcgetattr    _toupper
      dup2        hsearch      nl_langinfo  tcgetpgrp    twalk
      fdopen       isascii      pclose       tcgetsid     tzset
      __filbuf     isatty       popen        tcsendbreak  _xftw
      fileno       isnan        putenv       tcsetattr    
      __flsbuf     isnand **    putw         tcsetpgrp    
      fmtmsg **    lfind        setlabel     tdelete     

      ** = Function is at Level 2 in the SVID Issue 3 and therefore at
           Level 2 in the ABI.

    包括上面同义(Synonyms)表列出的标号,对于<name> 入口已经存在的_<name> 
    形式(带一个下划线,上面没有列出来)优先权高于它们的名字。所以,例如,
    libc同时包含了getopt和_getopt。

    在常规的上列中,其他地方以下没有被定义。

    int __filbuf(FILE *f);
    This function returns the next input character for f, filling
    its buffer as appropriate. It returns EOF if an error occurs.

    int __flsbuf(int x, FILE *f);
    This function flushes the output characters for f as if
    putc(x, f) had been called and then appends the value of x to
    the resulting output stream. It returns EOF if an error occurs
    and x otherwise.

    int _xftw(int, char *, int (*)(char *, struct stat *, int), int);
    Calls to the ftw(BA_LIB) function are mapped to this function
    when applications are compiled. This function is identical to
    ftw(BA_LIB), except that _xftw() takes an interposed first
    argument, which must have the value 2.


    要了解更多的关于SVID,ANSI C,POSIX的知识,可看该章节其他的库section部分。
    该节“System Data Interfaces”后有更多的描述。

    Global Data Symbols
                        全局数据符号


    libc库需要一些外部的全局数据符号(为了它自己的常规工作而定义的)。
    所有向libsys库请求的数据符号一定要让libc提供,就象下面表中的数据符号。

    正式定义的数据object被他们的符号描述,看System V接口定义,第三版本
    或者第6章节的数据定义(Data Definitions)section(在适当的处理器
    补充到System V ABI)。

    在下面表中的入口有<name>-_<name>的形式。一对符号都代表了一些数据。
    下划线的synonyms假设满足ANSI C标准。


    + Figure 3-3: libc Contents, Global External Data Symbols

      getdate_err optarg
      _getdate_err opterr
      __iob optind
    optopt

    展开全文
  • HDR文件格式简介

    千次阅读 2017-08-30 10:23:01
    2、HDRI文件格式介绍(OpenEXR、Radiance RGBE、Float TIFF)  HDRI(High-Dynamic Range Image)就是记录采用了HDR技术的图象数据文件。常用的HDRI文件有OpenEXR、Radiance RGBE、FloatTIFF三种格式。    2.1...

    转载自:http://blog.csdn.net/zzzGoogle/article/details/1615691

    1、HDR简介

        HDR的全称是High-Dynamic Range(高动态范围)。在此,我们先解释一下什么是Dynamic Range(动态范围),动态范围是指图像中所包含的从“最亮”至“最暗”的比值,也就是图像从“最亮”到“最暗”之间灰度划分的等级数;动态范围越大,所能表示的层次越丰富,所包含的色彩空间也越广。那高动态范围(HDR)顾名思义就是从“最亮”到“最暗”可以达到非常高的比值。    
        在日常生活中我们经常遇到这样的情况:突然从黑暗的房间中走到阳光下,眼睛会无法睁开;清晨阳光会穿透窗帘像光柱般照射入房间;反光度较高的物体在强光下会在周围产生光晕。以上这些生活中随处可见的现象在有HDR以前无法在3D世界中呈现!最大的原因就在于我们使用8~16bit的整数数据,使用8~16bit的整数数据是整个图象处理失真的关键点,所以我们对以往的运算方法做了以下二方面的重大改进:
        1、使用16bit、32bit的数据来提高像素数据的精度。既然继续使用8bit的数据来记录像素的特征不能满足HDR数据所需要的高精度运算的要求,在这种情况下,我们考虑使用16bit、32bit的数据记录来提高像素数据的精度都是可以的。使用了更多的数据来保存像素特征之后,无论是像素的对比度还是像素可以体现的色彩数目都有了巨大的提高。
        2、图象数据采用浮点数据。HDR真正的巨大变革来自于浮点数据的引入。我们可以采用浮点方式来处理和存放亮度数据,抛弃不准确的整数数据;同时计算机在引入浮点数据来存储象素的各个参数并且在运算的全过程都使用浮点数据,这样就可以有效的提高据的精确度。
        那么采用HDR后动态范围最大可以有多大呢?我们看如下的公式,就可以知道我们到底使用了HDR后动态值可以有多大,而动态值的大小直接表现了动态范围的大小:Dynamic Range=log10(Max Intensity / Min Intensity)。公式中intensity是指强度,我们对最大亮度除以最低亮度的结果取对数,得到的结果就是动态范围的相对数值。根据公式计算,当我们在亮度通道使用8bit的的情况下,最高亮度255,最低亮度1。那么计算得出的动态范围就是数值约为2.4,加上单位就是24dB。同理可以计算得出16bit 的亮度通道的动态范围是数值约是4.8,是使用8bit亮度通道的一倍。理论上在HDR模式下,动态范围的数值最高可以到达76.8。在NVIDIA所使用的OpenEXR中表现出来的HDR动态范围的数值最大值约有12.0,远远高出单纯使用16bit亮度通道的所带来的亮度体验,这是采用了优秀算法的结果。OpenEXR所能实现的最大动态范围已经超过了人眼的9,带来了更加真实的视觉体验。

    2、HDRI文件格式介绍(OpenEXR、Radiance RGBE、Float TIFF)
         HDRI(High-Dynamic Range Image)就是记录采用了HDR技术的图象数据文件。常用的HDRI文件有OpenEXR、Radiance RGBE、FloatTIFF三种格式。 
         2.1   OpenEXR文件格式
                OpenEXR是由工业光魔(Industrial Light & Magic)开发的一种HDR标准。OpenEXR文件的扩展名为.exr,常见的OpenEXR文件是FP16(16bit Float Point,也被称为half Float Point)数据图像文件,每个通道的数据类型是FP16,一共四个通道64bpp,每个通道1个bit位用来标志“指数”,5个bit用来存放指数的值,10个bit存放色度坐标(u,v)的尾数,其动态范围从6.14 × 10 ^ -5到6.41 × 10 ^ 4。
               在OpenEXR的算法里面共使用16bit来表示光照数据。虽然看起来和使用16bit亮度通道运算位数相同,但是OpenEXR巧妙的采用了1个bit位用来标志“指数”,5个bit用来存放指数的值,10个bit存放色度坐标的尾数。这样就轻易的解决了浮点数值由于位数少而精度不高的问题。大大的拓宽的在FP16下的动态范围。根据实际的计算结果:在正规化的情况下OpenEXR可以提供和人眼基本相同的动态范围,最暗到最亮是0.00006103515625(6.14 × 10 ^ -5)到65504(6.41 × 10 ^ 4),动态范围是9.03;非正规化条件下,OpenEXR可以提供从最暗到最亮的数值从0.000000059604644775390625(5.96 × 10 ^ -8 )到65504(6.41 × 10 ^ 4),化为动态范围表示就是12。
              下面是Still写的OpenEXR读写代码,保存的.exr文件采用Zips压缩编码。

    bool COpenExr::Load(const char fileName[], int& width, int& height, float** pixels)

        std::vector<float> vecpixels;
        if(!Load(fileName, width, height, vecpixels))
            return false;
     
        int num = width * height * 3;
        *pixels = new float[num];
        if(NULL == *pixels)
            return false;
        
        std::vector<float>::pointer ptr = &vecpixels[0];
        memcpy(*pixels, ptr, num * 4); 

        return true;
    }

    bool COpenExr::Load(const char fileName[],   int& width,   int& height,   std::vector<float> &pixels)
    {
        Imf::Array<Imf::Rgba> pixelsdata;
        bool bLoad = loadImage(fileName, width, height, pixelsdata);
        if(!bLoad) return false;
        for(int y = 0; y < height; y++)
        {
            int i = y * width;
            for(int x = 0; x < width; x++)
            {
                int j = i + x;
                const Imf::Rgba &rp = pixelsdata[j];
     
                pixels.push_back( float(rp.r));
                pixels.push_back( float(rp.g));
                pixels.push_back( float(rp.b));    
            }
        }

        return true;
    }

    bool  COpenExr::loadImage (const char fileName[],  int& width, int& height,  Imf::Array<Imf::Rgba>& pixels)
    {
        Imf::RgbaInputFile in (fileName);
        Imath::Box2i dataWindow = in.dataWindow();
        int dw, dh, dx, dy;
        width = dw = dataWindow.max.x - dataWindow.min.x + 1;
        height = dh = dataWindow.max.y - dataWindow.min.y + 1;
        dx = dataWindow.min.x;
        dy = dataWindow.min.y;

        pixels.resizeErase (dw * dh);
        in.setFrameBuffer (pixels - dx - dy * dw, 1, dw);
        try
        {
            in.readPixels (dataWindow.min.y, dataWindow.max.y);
        }catch (const exception &e)
        {
           std::cerr << e.what() << std::endl;
           return false;
        }

          return true;
    }

    bool COpenExr::Save(const char fileName[], int width, int height, const float* pixels)
    {
        std::vector<float> vecpixels(pixels, pixels + width * height * 3);
        return Save(fileName, width, height, vecpixels);
    }

    bool COpenExr::Save(const char fileName[], int width, int height, const std::vector<float> pixels)
    {
        Imf::Array<Imf::Rgba> pixelsdata;
        pixelsdata.resizeErase(width * height);
        for(int y = 0; y < height; y++)
        {
            int i = y * width;
            for(int x = 0; x < width; x++)
            {
                int j = i + x;
       
                half r = pixels[j * 3 ];
                half g = pixels[j * 3 + 1];
                half b = pixels[j * 3 + 2];
                pixelsdata[j] = Imf::Rgba(r, g, b);
            }
        }

        return SaveImage(fileName, width, height, pixelsdata);
    }

    bool COpenExr::SaveImage(const char fileName[], int width, int height, const Imf::Array<Imf::Rgba>  &pixels)
    {
        Imf::RgbaOutputFile file (fileName, width, height);
        file.setFrameBuffer(pixels, 1, width);
        try
        {
             file.writePixels(height);
        }catch(const exception &e)
        {
            std::cerr<< e.what() <<std::endl;
            return false;
        }

        return true;
    }
         

        官方库链接地址:http://www.openexr.com/

        2.2  Radiance RGBE文件格式
             RGBE文件的扩展名为.hdr,RGBE正式名称为Radiance RGBE格式。这个本来是BR、FR等作为radiance材质的一种格式,也叫做radiance map,后来成为流行的一种HDR格式。所谓E,就是指数。Radiance RGBE文件每个通道为8bit BYTE数据类型,4个通道一共是32 bit。RGBE可以使用RLE压缩编码压缩,也可以不压缩。由文件头、RGBE数据组成。
             文件头如下:
             类型                                  输出格式
             char programtype[16];         //#?Radiance/n#Generated by still/n
             float gamma;                      //1.0
             float exposure;                   //1.0
             字符串常量                          //FORMAT=32-bit_rle_rgbe/n/n
             int nWidth, int nHeight         //-Y nHeight +X nWidth/n

             RGBE数据与HDR FP32(RGB)相互转换公式如下:
             1、rgbe->FP32(RGB)
                 如果e为0, R = G = B = 0.0,否则:
                 R = r * 2^(e – 128 - 8);
                 G = g * 2^(e – 128 - 8);
                 B = b * 2^(e – 128 - 8);
            
             2、FP32(RGB) -> rgbe
                 v = max(R, G, B); 
                 如果v < 1e-32, r = g = b = e = 0, 否则:
                 将v用科学计算法表示成 v = m * 2 ^ n ( 0 < m < 1):
                 r = R * m *  256.0/v; 
                 g = G * m *  256.0/v;
                 b = B * m *  256.0/v;
                 e = n + 128;

         Still注:
         1、我们一般说HDR采用FP32,指的是HDR图象运算时候的内存数据类型,而Radiance RGBE文件采用8bit BYTE类型存储HDR数据。也就是说打开Radiance RGBE文件,要使用上面的公式1将Radiance RGBE文件的8bit BYTE文件数据转换为FP 32的HDR内存数据进行运算;保存为Radiance RGBE文件时,要使用上面的公式2将HDR的FP32内存数据转换为Radiance RGBE的8bit BYTE文件数据进行保存。同理,OpenEXR文件的读写也存在将其FP 16的文件数据到HDR的 FP32图象数据的转换;而下面将要讲的Float Tiff是不需要进行数据转换,直接将HDR的FP 32图象数据保存到TIFF文件中即可。
         2、Radiance有多种文件格式,其官方库包含内容比较复杂,所以,实际的读写没有使用其官方库,而是使用了网络上一个简单的C语言读写类,Still并对其进行了部分修改(在文件头写入“Generated  by Still”)。

        读写类链接地址:http://www.graphics.cornell.edu/~bjw/rgbe.html
        官方库链接地址:http://radsite.lbl.gov/radiance/

        2.3   FloatTiff文件格式
              Tiff文件的扩展名为.tif(.tiff),FloatTiff每个通道为FP32(32bit Float Point)类型,一共3个通道96bpp。用Tiff文件存储HDR数据,直接将HDR的FP32保存到TIFF文件中,有官方库可以利用。下面是Still写的代码样例,HDR数据我采用的是LZW压缩编码:    
        
    bool  CFloatTiff::Load(const char fileName[], int& width, int& height, float** pixels)
    {
        TIFF* fp = NULL;
        if((fp = TIFFOpen(fileName, "r")) == NULL)
            return false;
     
        //获取信息
        uint16 bps,  spp, datatype, photometric, compression, planarconfig, fillorder;
     
        //每个通道占据的数据位数
        if( (TIFFGetField(fp, TIFFTAG_BITSPERSAMPLE, &bps) == 0) || (bps != 32))
            return false;

        //每个象素的通道数目
        if((TIFFGetField(fp, TIFFTAG_SAMPLESPERPIXEL, &spp) == 0) || (spp != 3))
            return false;

        //每个通道的数据类型
        if((TIFFGetField(fp, TIFFTAG_SAMPLEFORMAT, &datatype) == 0) || (datatype != AMPLEFORMAT_IEEEFP))
            return false;

        //图像的数据采用的颜色模型
        if((TIFFGetField(fp, TIFFTAG_PHOTOMETRIC, &photometric) == 0) || (photometric != PHOTOMETRIC_RGB))
            return false;

        TIFFGetField(fp, TIFFTAG_IMAGEWIDTH, &width);
        TIFFGetField(fp, TIFFTAG_IMAGELENGTH, &height);

        int num = width * height * 3;
        *pixels = new float[num];
        if(NULL == *pixels)
            return false;
     
        if( TIFFReadEncodedStrip(fp, 0, *pixels, width * height * 3 * 4) == -1)
            return false;
     
        TIFFClose(fp);

        return true;
    }

    bool  CFloatTiff::Save(const char fileName[], int width, int height, const float* pixels)
    {
        if(NULL == pixels)
            return false;
     
        TIFF *fp = NULL;
     
        if((fp = TIFFOpen(fileName, "w")) == NULL)
            return false;

        TIFFSetField(fp, TIFFTAG_IMAGEWIDTH, width);
        TIFFSetField(fp, TIFFTAG_IMAGELENGTH, height);
        TIFFSetField(fp, TIFFTAG_COMPRESSION, COMPRESSION_LZW);//COMPRESSION_DEFLATE;
        TIFFSetField(fp, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
        TIFFSetField(fp, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
        TIFFSetField(fp, TIFFTAG_BITSPERSAMPLE, 32);
        TIFFSetField(fp, TIFFTAG_SAMPLESPERPIXEL, 3);
        TIFFSetField(fp, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);

        if(TIFFWriteEncodedStrip(fp, 0, const_cast<float*>(pixels), width * height * 3 * 4) == -1)
           return false;

        TIFFClose(fp);

        return true;
    }  

       官方库链接地址:http://www.remotesensing.org/libtiff/


    Still注:
        1、这篇文章的基础知识大部分来自:《光与影的魔术棒——HDR技术解析》 
    http://www.cqumzh.cn/topic_show.php?tid=200271
        2、这段时间工作比较忙,关于OpenEXR文件格式的详细介绍需要翻译相关文档,而且这部分内容是05年接触的,重新总结需要一些时间;还有HDR合成、ToneMapping方面的技术下次再奉上。

    展开全文
  • 文件系统之格式化与挂载

    千次阅读 2017-04-25 20:42:32
    文件系统 定义 文件系统类型 Linux 文件系统 光盘iso9660 Windows fat32 ntfs Unix FFS fast UFS ...RAW 未经处理或者未经格式化产生的文件系统 根据其是否支持journal 功能  日志型文件系统 ext3 ext4 xfs 非日志
  • 深入理解 WIN32 PE 文件格式

    千次阅读 2016-11-28 15:57:26
    深入理解 WIN32 PE 文件格式
  • 常见的图像文件格式 无损压缩

    万次阅读 2016-03-03 09:38:47
    常见的图像文件格式  一、BMP格式 BMP是英文Bitmap(位图)的简写,它是Windows操作系统中的标准图像文件格 式,能够被多 种Windows应用程序所支持。随着Windows操作系统的流行与丰富的 Windows应用程序的...
  • CSV (逗号分隔值文件格式

    千次阅读 2018-08-15 07:46:57
    (逗号分隔值文件格式)  编辑 逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个字符...
  • 常见在线流媒体格式:mp4、flv、f4v、webm 移动设备格式:m4v、mov、3gp、3g2 RealPlayer :rm、rmvb 微软格式 :wmv、avi、asf MPEG 视频 :mpg、mpeg、mpe、ts DV格式 :div、dv、divx 其他格式 :vob、dat...
  • 网站上上传的格式要求是.keystore形式的,但是我用的Android Studio开发的,打包证 书是.jks的,可以转换成.keystore形式的吗?怎么转呢
  • NES文件格式

    万次阅读 2011-08-14 09:59:51
    NES文件格式 http://www.bjsgm.com/a/a.asp?B=101&ID=12 9、NES文件格式 .NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。 偏移 字节数 内容 0-3 4 字符串“N
  • FIT文件格式解析及MATLAB读取程序

    千次阅读 2015-09-09 10:43:44
    FIT文件格式解析及MATLAB读取程序 FIT文件格式解析 摘要:本文档主要介绍了用UltraEdit查看FIT文件及简要的说明了FIT文件的数据存储方式,并附上了FIT文件数据读取及保存为TXT的MATLAB程序。此外,通过读取lamda的...
  • 字节码文件格式和类加载1字节码结构*.java文件经过javac编译后得到*.class文件,称为字节码文件,字节码文件时构成各种平台虚拟机的关键基石,字节码文件包含了java虚拟机指令集和若干其他辅助信息,好多语言比如...
  • xfs文件系统:格式化以及挂载

    万次阅读 2016-07-14 22:47:06
    本例格式化xfs文件系统,有关xfs文件系统就不介绍了。倒是首先要分析mkfs.xfs命令,先得获取相应的源码,源码获取可到github搜索xfsprogs,之前找了一个util-linux的包,编译后才发觉不支持mkfs.xfs,其它的文件系统...
  • 视频、音频文件格式大全

    万次阅读 2009-08-01 08:52:00
    一、本地影像视频: ●AVI格式:(最清晰的、最常用的)它的英文全称为Audio Video Interleaved,即音频视频交错格式。它于1992年被Microsoft公司推出,随Windows3.1一起被人们所认识和熟知。所谓“音频视频交错”,...
  • 嵌入式系统应用程序升级是比较频繁的,这就需要将flash进行合理的划分,一般情况,flash 的基本分区都有这几部分:1.uboot分区2.kernel分区3.rootfs分区这三部分是最基本的,一般都有。如果只是这样分区,然后应用...
  • linux的常用文件系统格式

    万次阅读 2011-07-20 16:16:35
    文件系统指文件存在的物理空间。在Linux系统中,每个分区都是一个文件系统,都有自己的目录层次结构。Linux的最重要特征之一就是支持多种文件系统,这样它更加灵活,并可以和许多其它种操作系统共存。Virtual File ...
  • 音视频文件的容器格式和编码格式

    千次阅读 2013-02-20 14:27:58
    容器是用来区分不同文件的数据类型的,而编码格式则由音视频的压缩算法决定,一般所说的文件格式或者后缀名指的就是文件的容器。对于一种容器,可以包含不同编码格式的一种视频和音频。 以下是一些常见的文件格式...
  • 压缩文件格式.RAR和Zip的详细区别!

    千次阅读 2013-12-23 17:28:58
    一、目录表(TOC)与分卷(Volume)抛开压缩算法不谈,我认为zip、rar在文件格式上最大的差异就在目录表(Table of Contents,TOC):zip有TOC,而rar没有。TOC这个词其实是从出版界借用过来的,指的就是每一本书...
  • > 之前深入了解过,过去...摘要:对于mach-o是Mac和iOS可以执行文件格式。进程就是系统根据该格式将执行文件加载到内存后得到的结果。系统通过解析文件,建立依赖(动态库),初始化运行时环境,才能真正开始执行该Ap
  • HDR文件格式简介及其读写函数

    万次阅读 2012-08-03 21:42:24
    2、Radiance有多种文件格式,其官方库包含内容比较复杂,所以,实际的读写没有使用其官方库,而是使用了网络上一个简单的C语言读写类,Still并对其进行了部分修改(在文件头写入“Generatedby Still”)。...
  • 前几种主流高清编码方式和文件封装格式介绍  (2010-10-18 15:25:23) 转载▼ 标签:  通道   jpeg2000   mpeg-4   编码方式   熵编码   it    ...
  • osmconvert可用于转换和处理OpenStreetMap文件。它掌握的功能少于常用的Osmosis:例如,无法使用osmconvert访问数据库。但是,程序运行速度更快,并提供一些特殊功能(-all-to-nodes, -complete-multipolygons和--...
  • 常见视频编解码格式

    千次阅读 2019-07-25 15:39:06
    常见视频编解码格式 1视频文件类别 常言道:物以类聚,人以群分。视频文件也不例外,细细算起来,视频文件可以分成两大类:其一是影像文件,比如说常见的VCD便是一例。其二是流式视频文件,这是随着国际互联网的...
  • PMML模型文件在机器学习的实践经验

    万次阅读 2017-10-23 18:33:25
    XGBoost 输入的文件格式是 SVMLib 文件格式 。 1 3:1 10:1 11:1 21:1 30:1 34:1 36:1 40:1 41:1 53:1 58:1 65:1 69:1 77:1 86:1 88:1 92:1 95:1 102:1 105:1 117:1 124:1 0 3:1 10:1 20:1 21:1 23:1 34:1 36:1 39:1 ...
  • Phylip进化树的使用,偏重于文件格式的获取 Table of Contents 1 关于这个软件2 mix.exe格式3 pars.exe4 附录:phylip软件子程序说明 1 关于这个软件 这个软件包含的内容很多,里面有许多的程序...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 84,018
精华内容 33,607
关键字:

一般正式文件的格式要求