scatter file 全功能介绍

转载自:

https://blog.csdn.net/dake_160413/article/details/73162766

http://blog.csdn.net/zhoujiaxq/article/details/8102587 非常重要

http://blog.csdn.net/lw13018088517/article/details/5758377

http://blog.csdn.net/zhoujiaxq/article/details/8102587

 

 

分散加载文件(即scatter file 后缀为.scf)是一个文本文件,通过编写一个分散加载文件来指定ARM连接器在生成映像文件时如何分配RO,RW,ZI等数据的存放地址。如果不用SCATTER文件指定,那么ARM连接器会按照默认的方式来生成映像文件,一般情况下我们是不需要使用分散加载文件的。也即是说,scf文件是armlink生成bin文件memory layout时的输入文件。

首先,我们了解bin(image)的组成成份

***************************************** RO,RW,ZI **********************************************************

 

(1) ARM程序的组成
此处所说的“ARM程序”是指在ARM系统中正在执行的程序,而非保存在ROM中的bin映像(image)文件,这一点清注意区别。
一个ARM程序包含3部分:RO,RW和ZI
RO是程序中的指令和常量
RW是程序中的已初始化变量
ZI是程序中的未初始化的变量

由以上3点说明可以理解为:
RO就是readonly,
RW就是read/write,
ZI就是zero

(2) ARM映像文件的组成
所谓ARM映像文件就是指烧录到ROM中的bin文件,也成为image文件。以下用Image文件来称呼它。
Image文件包含了RO和RW数据。
之所以Image文件不包含ZI数据,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可。包含进去反而浪费存储空间。
Q:为什么Image中必须包含RO和RW?
A:因为RO中的指令和常量以及RW中初始化过的变量是不能像ZI那样“无中生有”的。
(3) ARM程序的执行过程
从以上两点可以知道,烧录到ROM中的image文件与实际运行时的ARM程序之间并不是完全一样的。因此就有必要了解ARM程序是如何从ROM中的image到达实际运行状态的。
实际上,ROM中的指令至少应该有这样的功能:
1. 将RW从ROM中搬到RAM中,因为RW是变量,变量不能存在ROM中。
2. 将ZI所在的RAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI地址及大小来将相应得RAM区域清零。ZI中也是变量,同理:变量不能存在ROM中
在程序运行的最初阶段,RO中的指令完成了这两项工作后C程序才能正常访问变量。否则只能运行不含变量的代码。

 

总结:
1; C中的指令以及常量被编译后是RO类型数据。
2; C中的未被初始化或初始化为0的变量编译后是ZI类型数据。
3; C中的已被初始化成非0值的变量编译后市RW类型数据。

 

 

(4)
ROM主要指:NAND Flash,Nor Flash
RAM主要指:PSRAM,SDRAM,SRAM,DDRAM

(5) Image

Limit 的含义

对于刚学习ARM的人来说,如果分析它的启动代码,往往不明白下面几个变量的含义

|Image RO Limit|

|Image RW Base|

|Image ZI Base|

 

当把程序编写好以后,就要进行编译和链接了。假如编译没有错误,在文件的最后应该能看到Image component sizes,后面紧跟的依次是Code,RO Data ,RW Data ,ZI Data ,Debug 各个项目的字节数,最后会有他们的一个统计数据:

Code 163632 ,RO Data 20939 ,RW Data 53 ,ZI Data 17028

Tatal RO size (Code+ RO Data) 184571 (180.25kB)

Tatal RW size(RW Data+ ZI Data) 17081(16.68 kB)

Tatal ROM size(Code+ RO Data+ RW Data) 184624(180.30 kB)

 

(在ADS的Debug Settings中有一栏是Linker/ARM Linker,在output选项中有一个RO base选项,假如RO base设置为0x0c100000,后面的RW base 设置为0x0c200000,然后在Options选项中有Image entry point ,是一个初始程序的入口地址,设置为0x0c100000。)

 

 

|Image RO Base|= Image entry point =RO base =0x0c100000 ;表示程序代码存放的起始地址

 

|Image RO Limit|=程序代码起始地址+代码长度+1=0x0c100000+Tatal RO size+1= 0x0c100000 + 184571 + 1 = 0x0c100000 +0x2D0FB + 1

= 0x0c12d0fc

|Image RW Base|= 0x0c200000=RW base 地址指定

 

|Image RW Limit|=|Image RW Base|+ RW Data 53 = 0x0c200000+0x37(4的倍数,0到55,共56个单元)=0x0c200037

|Image ZI Base|= |Image RW Limit| + 1 =0x0c200038 

|Image ZI Limit|= |Image ZI Base| + ZI Data 17028=0x0c200038 + 0x4284=0x0c2042bc

也可以由此计算:

|Image Tatal RW  Limit| = |Image RW Base| +TatalRWsize(RWData+ZIData) 17081=0x0c200000+0x42b9+3(要满足4的倍数)=0x0c2042bc

 

****************************************** Scatter file ********************************************************

Part1 简介

一 概述

一般来说,ARMCC和ARMLINK会生成两个文件:*.axf和*.hex,进行ARM的debug的时候,下载的是*.axf文件,而成品代码的话,下载的就是hex文件。

Scatter file (分散加载描述文件)用于armlink的输入参数,他指定映像文件内部各区域的download与运行时位置。Armlink将会根据scatter file生成一些区域相关的符号,他们是全局的供用户建立运行时环境时使用。(注意:当使用了scatter file 时将不会生成以下符号

 Image RW Base,

Image RW Limit, 

Image RO Base, 

Image RO Limit, 

Image ZI Base, 

Image ZI Limit

 

二 什么时候使用scatter file

当然首要的条件是你在利用ADS进行项目开发,下面我们看看更具体的一些情况。

1 存在复杂的地址映射:例如代码和数据需要分开放在在多个区域。

2 存在多种存储器类型:例如包含 Flash,ROM,SDRAM,快速SRAM。我们根据代码与数据的特性把他们放在不同的存储器中,比如中断处理部分放在快速SRAM内部来提高响应速度,而把不常用到的代码放到速度比较慢的Flash内。

3 函数的地址固定定位:可以利用Scatter file实现把某个函数放在固定地址,而不管其应用程序是否已经改变或重新编译。

4 利用符号确定堆与堆栈:

5 内存映射的IO:采用scatter file可以实现把某个数据段放在精确的地指处。

因此对于嵌入式系统来说scatter file是必不可少的,因为嵌入式系统采用了ROM,RAM,和内存映射的IO。

 

三 scatter file 实例

1 简单的内存映射

LOAD_ROM(下载区域名称) 0x0000(下载区域起始地址) 0x8000(下载区域最大字节数)

{

EXEC_ROM(第一执行区域名称) 0x0000(第一执行区域起始地址) 0x8000(第一执行区域最大字节数)

{

*( RO(代码与只读数据))

}

RAM(第二执行区域名称) 0x10000(第二执行区域起始地址) 0x6000(第二执行区域最大字节数)

{

*( RW(读写变量), ZI(未初始化变量))

}

}


2 复杂内存映射

 

LOAD_ROM_1 0x0000(下载区域一起始地址)

{

EXEC_ROM_1 0x0000(第一执行区域开始地址)

{

program1.o( RO) (program1.o内的Code与RO data 放在第一执行区域)

}

DRAM 0x18000(第二执行区域开始地址) 0x8000(第二执行区域最大字节数)

{

program1.o ( RW, ZI) (program1.o内的RW data与 ZI data 放在第二执行区域)

}

}

LOAD_ROM_2 0x4000(下载区域二起始地址)

{

EXEC_ROM_2 0x4000

{

program2.o( RO) (program2.o内的Code与RO data 放在第一执行区域)

}

SRAM 0x8000 0x8000

{

program2.o ( RW, ZI) (program2.o内的RW data与 ZI data 放在第二执行区域)

}

}

 

Part2 基本语法

2.1 BNF 符号与语法

:由引号赖标示的符号保持其字面原意,如A” ”B标示A B。

A ::= B :定义A为B。

[A] :标示可选部分,如A[B]C用来标示ABC或AC。

:用来标示A可以重复任意次,如A 可标示A,AA,AAA, …

A* :同A 。

A | B :用来标示选择其一,不能全选。如A|B用来标示A或者B。

(A B) :标示一个整体,当和|符号或复杂符号的多次重复一起使用时尤其强大,如(AB) (C|D)标示ABC,ABD,ABABC,ABABD, …

2.2 分散加载文件各部分描述

(2.1)

如图2.1所示为一个完整的分散加载脚本描述结构图。下面我们对图示中各个部分进行讲述

 

2.2.1 加载区描述

每个加载区有:

ó名称:供连接器确定不同下载区域

ó基地址:相对或绝对地址

ó属性:可选

ó最大字节数:可选

ó执行区域列:确定执行时各执行区域的类型与位置

 

load_region_name (base_address | (" " offset)) [attribute_list] [ max_size ]

"{"

execution_region_description

"}"

 

load_region_name:下载区域名称,最大有效字符数31。(并不像执行区域段名用于Load$$region_name,而是仅仅用于标示下载区域)。

base_address:本区域内部目标被连接到的地址(按字对齐)。

offset:相对前一个下载区域的偏移量(4的整数倍,如果为第一个区域)

 

2.2.2 执行区描述

每个执行区有:

ó名称:供连接器确定不同下载区域

ó基地址:相对或绝对地址

ó属性:确定执行区域的属性

ó最大字节数:可选

ó输入段:确定放在该执行区域的模块

exec_region_name (base_address | " " offset) [attribute_list] [max_size]

"{"

input_section_description

"}"

exec_region_name:执行区域名称,最大有效字符数31。

base_address:本执行区域目标要被联接到的位置,按字对齐。

offset:相对于前一个执行区域结束地址的偏移量,4的整数倍;如果没有前继之能够行区域(本执行区域为该下载区域的第一个执行区域),则该偏移量是相对于该下载区域的基址偏移量。

attribute_list:PI,OVERLAY,ABSOLUTE,FIXED,UNINIT。

 

PI: 位置独立。

OVERLAY: 覆盖。

ABSOLUTE: 绝对地址。

FIXED: 固定地址,下载地址与执行地址具有该地址指示确定。

UNINIT: 未初始化数据。

RELOC:无法明确指定执行区域具有该属性,而只能通过继承前一个执行区或父区域获得。

对于PI,OVERLAY,ABSOLUTE,FIXED,我们只能选择一个,缺省属性为ABSOLUTE。一个执行区域要么直接继承其前面的执行区域的属性或者具有属性为ABSOLUTE。

具有PI,OVERLAY,RELOC属性的执行区域允许其地址空间重叠,对于BSOLUTE,FIXED 属性执行区域地址空间重叠Armlink会报错。

max_size:可选,他用于指使Armlink在实际分配空间大于指定值时报错。

input_section_description:指示输入段的内容

 

2.2.3 输入段描述

输入段:

ó模块名:目标文件名,库成员名,库文件名。名称可以使用通配符。

ó输入段名,或输入段属性(READ-ONLY,CODE)。

module_select_pattern["("(" " input_section_attr | input_section_pattern)([","] " " input_section_attr | "," input_section_pattern))*")"]

2.2.3.1

module_select_pattern:选择的模块名称(目标文件,库文件成员,库文件),模块名可以使用通配符(*匹配任意多个字符,?匹配任意一个字符),名称不区分字母大小写,它是供选择的样本。

例1:*libtx.a ( RO)

libtx.a为threadX库文件。

例2:tx_ill.o (INIT)

tx_ill.o为threadX中断向量目标文件。

 

2.2.3.2

input_section_attr:输入段属性选择子,每个选择子以” ”开头,选择子不区分大小写字符。

选择子可选RO-CODE,RO-DATA,RO( selects both RO-CODE and RO-DATA),RW-DATA,RW-CODE,RW( selects both RW-CODE and RW-DATA),ZI,ENTRY( that is a section containing an ENTRY point)。

以下同义词可以选择:CODE (for RO-CODE),CONST( for RO-DATA),TEXT (for RO),DATA (for RW),BSS (for ZI)。

还有两个伪属性:FIRST,LAST。如果各段的先后顺序比较重要时,可以使用FIRST,LAST标示一个执行区域的第一个和最后一个段。

例1:os_main_init.o (INIT , FIRST)

FIRST表示放于本执行区域的开始处。

例2:*libtx.a ( RO)

RO 表示*libtx.a的只读部分。

 

2.2.3.3

input_section_pattern:输入段名。

例1:os_main_init.o (INIT , FIRST)

INIT 为os_main_init.o的一个段。

例2:os_stackheap.o (heap)

heap 为os_stackheap.o的一个段。

例3:os_stackheap.o (stack)

stack为os_stackheap.o的一个段。

 

//--------------------------------------------------------------------------------------------------------------------------

分散加载文件事例

 

ADS下的分散加载文件应用实例

load_region_name start_address | " "offset [attributes] [max_size]

{

execution_region_name start_address | " "offset [attributes][max_size]

{

module_select_pattern ["("(" " input_section_attr | input_section_pattern)([","] " " input_section_attr | "," input_section_pattern)) *")"]

}

}

load_region_name: 加载区域名,用于“Linker”区别不同的加载区域,最多31个字符;

start_address: 起始地址,指示区域的首地址;

offset: 前一个加载区域尾地址+offset 做为当前的起始地址,且“offset”应为“0”或“4”的倍数;

attributes: 区域属性,可设置如下属性:

PI 与地址无关方式存放;

RELOC 重新部署,保留定位信息,以便重新定位该段到新的执行区;

OVERLAY 覆盖,允许多个可执行区域在同一个地址,ADS不支持;

ABSOLUTE 绝对地址(默认);

max_size: 该区域的大小;

 

execution_region: 执行区,程序执行时,从加载区域将数据复制到相应执行区后才能被正确执行;

execution_region_name:执行区域名;

start_address: 该执行区的首地址,必须字对齐;

offset: 同上;

attributes: 同上;

PI 与地址无关,该区域的代码可任意移动后执行;

OVERLAY 覆盖;

ABSOLUTE 绝对地址(默认);

FIXED 固定地址;

UNINIT 不用初始化该区域的ZI段;

 

module_select_pattern: 目标文件滤波器,支持通配符“*”和“?”;

*.o匹配所有目标,* (或“.ANY”)匹配所有目标文件和库。

input_section_attr: 每个input_section_attr必须跟随在“+”后;且大小写不敏感;

RO-CODE 或 CODE

RO-DATA 或 CONST

RO或TEXT, selects both RO-CODE and RO-DATA

RW-DATA

RW-CODE

RW 或 DATA, selects both RW-CODE and RW-DATA

ZI 或 BSS

ENTRY, that is a section containing an ENTRY point.

FIRST,用于指定存放在一个执行区域的第一个或最后一个区域;

LAST,同上;

 

input_section_pattern: 段名;

汇编中指定段:

AREA vectors, CODE, READONLY

C中指定段:

#pragma arm section [sort_type[[=]"name"]] [,sort_type="name"]*

sort_type: code、rwdata、rodata、zidata

如果“sort_type”指定了但没有指定“name”,那么之前的修改的段名将被恢复成默认值。

#pragma arm section // 恢复所有段名为默认设置。

 

input_section_pattern应用:

#pragma arm section rwdata = "SRAM",zidata = "SRAM"

static OS_STK SecondTaskStk[256]; // “rwdata”“zidata”将定位在“sram”段中。

#pragma arm section // 恢复默认设置

分散加载文件中定义如下:

Exec_Sram 0x80000000 0x40000

{

* (sram)

}

 

“PI” 属性使用示例:

 

LR_1 0x010000 PI ; The first load region is at 0x010000.

{

ER_RO 0 ; The PI attribute is inherited from parent.

; The default execution address is 0x010000, but the code can be moved.

{

*( RO) ; All the RO sections Go here.

}

ER_RW 0 ABSOLUTE ; PI attribute is overridden by ABSOLUTE.

{

*( RW) ; The RW sections are placed next. They cannot be moved.

}

ER_ZI 0 ; ER_ZI region placed after ER_RW region.

{

*( ZI) ; All the ZI sections are placed consecutively here.

}

}

LR_1 0x010000 ; The first load region is at 0x010000.

{

ER_RO 0 ; Default ABSOLUTE attribute is inherited from parent. The execution address

; is 0x010000. The code and ro data cannot be moved.

{

*( RO) ; All the RO sections go here.

}

ER_RW 0x018000 PI ; PI attribute overrides ABSOLUTE

{

*( RW) ; The RW sections are placed at 0x018000 and they can be moved.

}

ER_ZI 0 ; ER_ZI region placed after ER_RW region.

{

*( ZI) ; All the ZI sections are placed consecutively here.

}

}

 

 

 

程序中对某区域地址等的引用方法:

Load

regionnameregionname

Base Load address of the region.

Image

regionnameregionname

Base Execution address of the region.

Image

regionnameregionname

Length Execution region length in bytes (multiple of 4).

Image

regionnameregionname

Limit Address of the byte beyond the end of the execution region.

Image

regionnameregionname

ZI$$Base Execution address of the ZI output section in this region.

Image

regionnameregionname

ZI$$Length Length of the ZI output section in bytes (multiple of 4).

Image

regionnameregionname

ZI$$Limit Address of the byte beyond the end of the ZI output sectionin the execution

region.

SectionName$$Base Input Address of the start of the consolidated section called SectionName.

SectionName$$Limit Input Address of the byte beyond the end of the consolidated section called SectionName.

 

Load: 加载区,即存放地址;

Image: 执行区,即运行地址;

Base: 区首地址;

Limit: 区尾地址;

Length: 区长度;

region_name: RO、RW、ZI、load_region_name、execution_region_name;

 

例如:

“RAM1”区域的首地址: Image

RAM1RAM1

Base

上例中“sram”段首地址: sram$$Base

汇编引用示例:

IMPORT |Load

ExecRAM1ExecRAM1

Base| // Exec_RAM1 为“RW”段

IMPORT |Image

ExecRAM1ExecRAM1

Base|

IMPORT |Image

ExecRAM1ExecRAM1

Length|

IMPORT |Image

ExecRAM1ExecRAM1

Limit|

LDR R0, =|Load

ExecRAM1ExecRAM1

Base|

LDR R1, =|Image

ExecRAM1ExecRAM1

Base|

LDR R2, =|Image

ExecRAM1ExecRAM1

Limit|

CMP R1, R2

LDRCC R3, [R0], #4

STRCC R3, [R1], #4

BCC %b0

 

C 引用:

extern unsigned char Load

ExecRAM1ExecRAM1

Base;

extern unsigned char Image

ExecRAM1ExecRAM1

Base;

extern unsigned char Image

ExecRAM1ExecRAM1

Length;

void MoveRO(void)

{

unsigned char * psrc, *pdst;

unsigned int count;

 

count = (unsigned int) &Image

ExecRAM1ExecRAM1

Length;

psrc = (unsigned char *)&Load

ExecRAM1ExecRAM1

Base;

pdst = (unsigned char *)&Image

ExecRAM1ExecRAM1

Base;

 

while (count--) {

*pdst = *psrc ;

}

}

 

 

加载文件示例一:

起始地址 大小

ROM: 0x00000000 256K ;0x1fc 保留为加密字,程序在ROM中运行;

RAM 0x40000000 16K ;用于全局变量及任务堆栈;

SRAM 0x80000000 512K ;SRAM速度慢,主要用于存放大的数据表;

LOAD_ROM1 0x00000000 0x1f8 ; 指定该加载区域首地址、大小

{

EXEC_ROM1 0 0x1f8 ; 没有前一加载区域,所以该执行区域首地址为加载去首地址

; 并指定该区域长度

{

Startup.o (vectors, FIRST) ; 目标文件的“vectors”段放在该执行区域的第一段

irq.o ( RO) ; 目标文件的所有“RO”段放在该执行区域

}

}

LOAD_ROM2 0x00000200 ; 第二个加载区域

{

EXEC_ROM2 0 0x3e600

{

* ( RO) ; 所有目标文件和库文件中的“RO”段存放在该区域

}

RAM1 0x40000000 0x4000

{

* ( RW, ZI) ; 所有目标文件和库文件的“RW”和“ZI”段存放在该区域

}

SRAM2 0x80000000 0x80000

{

* (sram) ; 所有目标文件中的“sram”段存放在该区域

}

}

 

示例二:

“iap.o”定义在“Exec_RAM1”中运行,所以设置“PI”属性;

在调用“iap.c”中函数之前应该将其从“Load

Base”复制到指定的“Exec_RAM1”区域;

 

Load_region1 0x00000000 0x1fc

{

EXEC_ROM1 0

{

Startup.o (vectors, FIRST)

irq.o ( RO)

}

}

Load_region2 0x00000200 0x3e600

{

EXEC_ROM2 0

{

* ( RO)

}

Exec_IAP 0 PI // 可能引起链接器未使用该属性警告,忽略

{

iap.o ( RO)

}

Exec_RAM1 0x40000000 0x4000

{

* ( RW, ZI)

}

Exec_Sram 0x80000000 0x40000

{

* (SRAM)

}

}

// 移动“IAP.o”中的所有函数到“ImageExecIAPBase”加载区,并调用其中的函数

extern unsigned char Load

Base;

 

extern unsigned char Image

Length;

 

#define ImageExecIAPBase (0x40000000 0x1000) // 加载区首址

void MoveIAPRO(void)

{

unsigned char * psrc, *pdst;

unsigned int count;

count = (unsigned int) &Image

Length;

 

psrc = (unsigned char *)&Load

Base;

 

pdst = (unsigned char *)ImageExecIAPBase;

while (count--) {

*pdst = *psrc ;

}

}

// 调用“IAP.O”中的某函数

{

void (* pfnIAPWrite)(unsigned long, int);

pfnIAPWrite = (void (*)(unsigned long, int))

(ImageExecIAPBase

(unsigned int)IAPWrite - // 被调用函数名

(unsigned int)&Load

Base);

 

pfnIAPWrite((int)((CUPDATA *)CODESTARTADDR)->data,

((CUPDATA *)CODESTARTADDR)->length);

 

 

#Pragma  指令可能是最复杂的了,他的作用是设定编译器的状态或是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持和C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。  
其格式一般为:        #Pragma  Para  
       其中Para  为参数,下面来看一些常用的参数。

  
 #pragma arm section [sort_type[[=]"name"]] [,sort_type="name"]*
sort_type:      code、rwdata、rodata、zidata
                如果“sort_type”指定了但没有指定“name”,那么之前的修改的段名将被恢复成默认值。
#pragma arm section     // 恢复所有段名为默认设置。
应用:
    #pragma arm section rwdata = "SRAM",zidata = "SRAM"
        static OS_STK  SecondTaskStk[256];              // “rwdata”“zidata”将定位在“sram”段中。
    #pragma arm section                                 // 恢复默认设置

 

#pragma arm section [section_sort_list]
       (1)message  参数。  Message  参数是我最喜欢的一个参数,他能够在编译信息输出窗  
口中输出相应的信息,这对于原始码信息的控制是非常重要的。其使用方法为:  
             #Pragma  message(“消息文本”)  
             当编译器遇见这条指令时就在编译输出窗口中将消息文本打印出来。
       当我们在程式中定义了许多宏来控制原始码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们能用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在原始码的什么地方定义了_X86这个宏能用下面的方法  
             #ifdef  _X86  
             #Pragma  message(“_X86  macro  activated!”)  
             #endif  
             当我们定义了_X86这个宏以后,应用程式在编译时就会在编译输出窗口里显示“_  
X86  macro  activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了  
。  
       
     (2)另一个使用得比较多的pragma参数是code_seg。格式如:  
           #pragma  code_seg(  ["section-name"[,"section-class"]  ]  )  
           他能够设置程式中函数代码存放的代码段,当我们研发驱动程式的时候就会使用到他。  
 
     (3)#pragma  once  (比较常用)  
           只要在头文件的最开始加入这条指令就能够确保头文件被编译一次,这条指令实际上在VC6中就已有了,不过考虑到兼容性并没有太多的使用他。  
       
     (4)#pragma  hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB能预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。    
         有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你能用#pragma  startup指定编译优先级,如果使用了#pragma  package(smart_init)  ,BCB就会根据优先级的大小先后编译。    
       
     (5)#pragma  resource  "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体  
外观的定义。    
         
     (6)#pragma  warning(  disable  :  4507  34;  once  :  4385;  error  :  164  )  
           等价于:  
           #pragma  warning(disable:4507  34)    //  不显示4507和34号警告信息  
           #pragma  warning(once:4385)                //  4385号警告信息仅报告一次  
           #pragma  warning(error:164)                //  把164号警告信息作为一个错误。  
           同时这个pragma  warning  也支持如下格式:  
           #pragma  warning(  push  [  ,n  ]  )  
           #pragma  warning(  pop  )  
           这里n代表一个警告等级(1---4)。  
           #pragma  warning(  push  )保存所有警告信息的现有的警告状态。  
           #pragma  warning(  push,  n)保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。    
           #pragma  warning(  pop  )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的一切改动取消。例如:  
           #pragma  warning(  push  )  
           #pragma  warning(  disable  :  4705  )  
           #pragma  warning(  disable  :  4706  )  
           #pragma  warning(  disable  :  4707  )  
           //.......  
           #pragma  warning(  pop  )    
           在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。  
       (7)pragma  comment(...)  
             该指令将一个注释记录放入一个对象文件或可执行文件中。  
           常用的lib关键字,能帮我们连入一个库文件。  
   
   
每个编译程式能用#pragma指令激活或终止该编译程式支持的一些编译功能。例如,对循环优化功能:  
#pragma  loop_opt(on)            //  激活  
#pragma  loop_opt(off)    //  终止  
有时,程式中会有些函数会使编译器发出你熟知而想忽略的警告,如“Parameter  xxx  is  never  used  in  function  xxx”,能这样:  
#pragma  warn  ?100            //  Turn  off  the  warning  message  for  warning  #100  
int  insert_record(REC  *r)  
{  /*  function  body  */  }  
#pragma  warn  +100                        //  Turn  the  warning  message  for  warning  #100  back  on  
函数会产生一条有唯一特征码100的警告信息,如此可暂时终止该警告。  
每个编译器对#pragma的实现不同,在一个编译器中有效在别的编译器中几乎无效。可从编译器的文件中查看。

此编译指示指定要用于后续函数或对象的节名称。这包括编译器为进行初始化而创建的匿名对象的定义。

Note

可以将 __attribute__((section(..))) 用于函数或变量以替代 #pragma arm section

语法

#pragma arm section [section_sort_list]

其中:

section_sort_list

指定要用于后续函数或对象的节名称的可选列表。section_sort_list 的语法为:

section_type[[=]"name"] [,section_type="name"]*

有效的节类型是:

  • code

  • rodata

  • rwdata

  • zidata。

用法

可以将分散加载描述文件与 ARM 链接器配合使用,以控制将已命名的节放在特定内存地址的方式。

限制

此选项对以下内容无效:

  • 内联函数及其局部静态变量。

  • 模板实例化及其局部静态变量。

  • 删除未使用的变量和函数。但是,可通过使用 #pragma arm section,使链接器能够删除本来可能会保留的函数或变量,因为它与使用的函数或变量位于相同的节中。

  • 将定义写入对象文件的顺序。

示例

int x1 = 5;                     // in .data (default)
int y1[100];                    // in .bss (default)
int const z1[3] = {1,2,3};      // in .constdata (default)
#pragma arm section rwdata = "foo", rodata = "bar"
int x2 = 5;                     // in foo (data part of region)
int y2[100];                    // in .bss
int const z2[3] = {1,2,3};      // in bar
char *s2 = "abc";               // s2 in foo, "abc" in .conststring
#pragma arm section rodata
int x3 = 5;                     // in foo
int y3[100];                    // in .bss
int const z3[3] = {1,2,3};      // in .constdata
char *s3 = "abc";               // s3 in foo, "abc" in .conststring
#pragma arm section code = "foo"
int add1(int x)                   // in foo (code part of region)
{
    return x+1;
} 
#pragma arm section code

 


用#pragma data_seg建立一个新的数据段并定义共享数据,其具体格式为:

  #pragma data_seg ("shareddata")

  HWND sharedwnd=NULL;//共享数据

  #pragma data_seg()


1,#pragma data_seg()一般用于DLL中。也就是说,在DLL中定义一个共享的,有名字的数据段。最关键的是:这个数据段中的全局变量能被多个进程共享。否则多个进程之间无法共享DLL中的全局变量。

2,共享数据必须初始化,否则微软编译器会把没有初始化的数据放到.BSS段中,从而导致多个进程之间的共享行为失败。

3。如果在一个DLL中这么写:

#pragma data_seg("MyData")

int g_Value; // Note that the global is not initialized.

#pragma data_seg()

DLL提供两个接口函数:

int GetValue()
{
    return g_Value;
}

void SetValue(int n)
{
    g_Value = n;
}

然后启动两个进程A和B,A和B都调用了这个DLL,如果A调用了SetValue(5); B接着调用int m = GetValue(); 那么m的值不一定是5,而是个未定义的值。因为DLL中的全局数据对于每一个调用他的进程而言,是私有的,不能共享的。如果你对g_Value进行了初始化,那么g_Value就一定会被放进MyData段中。换句话说,如果A调用了SetValue(5); B接着调用int m = GetValue(); 那么m的值就一定是5!这就实现了跨进程之间的数据通信!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值