armlink 第三章 scatter语法(二)

第三章 scatter语法(二)表达式与内置函数

scatter文件通常包含有数字常量。这些常量可以直接指定,也可以通过对表达式求值而得到

3.1 表达式中可用的运算符

+,-,*,/,AND,OR,()

AND 和OR的运算规则和C语言的一样。

例如:

#define BASE_ADDRESS 0x8000
#define ALIAS_NUMBER 0x2
#define ALIAS_SIZE 0x400
#define AN_ADDRESS (BASE_ADDRESS+(ALIAS_NUMBER*ALIAS_SIZE)) 

scatter文件可能包含如下代码:

LOAD_FLASH AN_ADDRESS

预处理之后,它等价于

LOAD_FLASH ( 0x8000 + (0x2 * 0x400))

计算之后,为:

LOAD_FLASH 0x8800

3.2 表达式使用场景

表达式可以使用在如下的地方:

  • 加载和执行region的基址
  • 加载和执行region的偏移
  • 加载和执行region的max_size
  • ALIGN,FILL或者PADVALUE的参数
  • ScatterAssert函数的参数

例如:

LR1 0x8000 (2 * 1024)
{
    ER1 +0 (1 * 1024)
    {
        *(+RO)
    }
    ER2 +0 (1 * 1024)
    {
        *(+RW +ZI)
    }
}

3.3 表达式规则

表达式遵循C预处理规则

表达式由下面组成:

  • 十进制或者十六进制的数字

  • 算术运算符:+ ,-, /, *, ~, OR, AND。OR 和AND对应于c语言中的|和&

  • 逻辑运算符:LOR,LAND,!。 LOR和LAND对应于c语言中的||和&&

  • 关系运算符:< ,<=,>,>=,== 。关系运算表达式如果为true,返回非零;否则返回零

  • 条件表达式:Expression?Expression1:Expression2。
    这个对应于c语言的条件运算符

  • 函数返回数字

所有运算符在含义和优先级上都与C对应的运算符匹配

表达式不区分大小写。

3.4 用于计算执行地址的内置函数

ImageBase(region_name)   //返回region_name的基址
ImageLenth(region_name)  //返回region_name的长度
ImageLimit(region_name)  //返回region_name的最后的地址

region_name 可以是加载或者执行region的名字。

注意:当使用.ANY 时,不能使用这些函数。因为该region只有在.ANY分配好之后,才能知道大小

例子:

LR1 0x8000
{
    ER1 0x100000
    {
        *(+RO)
    }
}
LR2 0x100000
    {
        ER2 (ImageLimit(ER1)) ;ER1结束之后放置ER2
        {
            *(+RW +ZI)
        }
}

3.5 ScatterAssert函数和加载地址相关的函数

ScatterAssert(expression)   //如果expression返回false,链接器就报错
LoadBase(region_name) //region_name 的基址
LoadLength(region_name)   //region_name 的长度
LoadLength(region_name)   //region_name 的结束地址

例子如下:

LR1 0x8000
{
    ER0 +0
    {
        *(+RO)
    }
    ER1 +0
    {
        file1.o(+RW)
    }
    ER2 +0
    {
        file2.o(+RW)
    }
    ScatterAssert((LoadLength(ER1) + LoadLength(ER2)) < 0x1000); LoadLength is compressed size
    ScatterAssert((ImageLength(ER1) + ImageLength(ER2)) < 0x2000); ImageLength is uncompressed size
}
ScatterAssert(ImageLength(LR1) < 0x3000); Check uncompressed size of load region LR1

3.6 符号相关的函数

defined(global_symbol_name)  //如果global_symbol_name 没有定义,则返回零,否则返回非零

例如:

LR1 0x8000
{
    ER1 (defined(version1) ? 0x8000 : 0x10000)
    {
        *(+RO)
    }
    ER2 +0
    {
        *(+RW +ZI)
    }
}

3.7 AlignExpr(expr,align)函数

AlignExpr(expr,align) //增加expr直到与指定的align边界对齐,函数的计算方式为:(expr + (align-1)) & ~(align-1))

例如:

ER +0
{}
ER2 AlignExpr(+0x8000,8)
{}

3.8 GetPageSize() 函数

GetPageSize() //返回页大小,本系列旨在裸机开发,此函数不做过多介绍

3.9 SizeOfHeaders()函数

SizeOfHeaders() //返回ELF header和Programe Header table的大小

3.10 最后来两个栗子

最后的最后,来两个使用函数的例子:
第一个例子,使用AlignExpr和ImageLimit紧密排列执行region:

注意:下面例子中使用的

#! armcc -E

表示使用的预处理名字为armcc,对应的预处理参数为-E,这将会在下一章中引入

InRoot$$Sections

表示root region的所有section,这也会在下一章中引入介绍

#! armcc -E
#define START_ADDRESS 0x100000
#define PAGE_ALIGNMENT 0x100000
LR1 0x8000
{
    ER0 +0
    {
        *(InRoot$$Sections)
    }
    ER1 START_ADDRESS
    {
        file1.o(*)
    }
    ER2 AlignExpr(ImageLimit(ER1), PAGE_ALIGNMENT)
    {
        file2.o(*)
    }
    ER3 AlignExpr(ImageLimit(ER2), PAGE_ALIGNMENT)
    {
        file3.o(*)
    }
}

第二个例子:

有些时候可能想在第一个加载region中放置ZI,然后在二个加载region中使用相对偏移来指定地址。你可能如下写法:

LR1 0x8000
{
    er_progbits +0
    {
        *(+RO,+RW) ; 在加载region中占空间
    }
    er_zi +0
    {
        *(+ZI) ; 在加载region中不占空间
    }
}
LR2 +0 ;LR2紧跟LR1
{
    er_moreprogbits +0
    {
        file1.o(+RO) ; 在加载region中占空间
    }
}

但是,链接器不会根据ZI来调整LR2的基址,所以这会导致er_zi覆盖er_moreprogbits,链接会产生一个错误。

为了纠正这个错误,使用ImageLimit函数,如下:

LR1 0x8000
{
    er_progbits +0
    {
        *(+RO,+RW) 
    }
    er_zi +0
    {
        *(+ZI) 
    }
}
LR2 ImageLimit(er_zi) 
{
    er_moreprogbits +0
    {
        file1.o(+RO) 
    }
}

本章完,下一章为scatter的具体使用

包括如何在指定地址放置变量,函数,如何映射寄存器等。

你可能感兴趣的:(armlink)