MDK链接器的符号的使用

一、符号名与C语言变量的关系

  局部变量放在栈里面,不会被其他地方引用,无需链接,故符号表里不存储局部变量;

  全局变量或静态变量的变量名与符号表里的名字是一一对应的,所不同的是,符号表里记录的是变量地址,而不是变量;C语言里操作变量是直接使用变量,而不是使用地址(虽然实际上是操作变量所在地址的内容);

  例如,

nRecv                                    0x2000004c   Data           4  debug.o(.data)

这个符号,我们在c语言里操作是这样的nRecv=0,而不是*nRecv=0;

  但是,在汇编里面操作的符号就是它后面的地址,给nRecv赋值,写法应该是:

    IMPORT nRecv
    LDR R0, =nRecv; move c variable(nRecv)'s address to R0
    MOV R1, #0
    STR R1, [R0]
    

  所以,符号表里的符号后面的值=C语言里变量的地址;

  当然,对于函数来说也是一样的:

__printf                                 0x080003cd   Thumb Code   388  __printf_flags_ss_wp.o(.text)
    strcpy                                   0x08000555   Thumb Code    72  strcpy.o(.text)
    strcasecmp                               0x0800059d   Thumb Code    42  strcasecmp.o(.text)
    strlen                                   0x080005c7   Thumb Code    62  strlen.o(.text)

  符号表后面的值=C语言函数的地址。

二、MDK链接器生成的符号

  除了全局和静态变量还有函数,MDK连接器还提供了一些段信息供我们使用,这些段信息是跟sct文件相关的;

  对于区域Region来说,Load$\$Region$\$Base这个符号的值是在该区域在ROM里面的起始地址;Load$\$Region$\$Limit这个符号的值是在该区域在ROM里面的结束地址;Load$\$Region$\$Length这个符号的值是在该区域在ROM里面的长度;实际运行地址可能与ROM里面的地址不一样,在__main函数调用main函数前,里面的函数会把ROM里面的程序复制到运行地址里面去,而Image$\$Region$\$Base就是该区域在内存里面的地址;对于需要初始化的变量,也就是RW区域里面的内容,也会被复制到运行地址里面去,如Image$\$RW_IRAM1$\$RW\$\$Base;具体参考Keil的帮助文档里的内容:MDK主菜单->Help->uVision Help会打开一个文档,在文档目录Arm Compiler 5 User's Guides->Link User's Guide->Accessing and Managing Symbos with armlink里有详细的说明;

  如果想使用链接器帮我们生成的这些符号,那么在C语言里应该怎么使用呢?请参照第一部分描述的对应关系。我们首先声明变量extern int Load\$\$Region\$\$Base,然后使用它在符号表里面的值,符号表里面的值=C语言变量地址,那么,我们的使用方法就是&Load\$\$Region\$\$Base就可以了,你可以在你的程序里面使用,发现它跟符号表map里面的值是一样的,也就是段的起始地址。

  按照这个说法,那么,我们把函数作为参数使用时,也应该加取址符,但C编译器它简化了我们的操作,让我们使用函数名就可以了。

三、作用

  这些符号对于我们的C语言程序有什么作用的呢?

  例如,我们使用的是FLASH,想在FLASH后面的地址放一些用户数据,但我们编译前我们是不知道这个程序有多大的,这个时候,这些辅助符号就派上用场了:

 声明extern int Load$\$ER_IROM1$\$Limit,然后使用&Load$\$ER_IROM1$\$Limit来确定程序结束位置,然后在它后面的一个扇区存储;

 又例如,我们想最大程度地使用剩余内存作为动态分配的话,我们也可以使用变量Image$\$RW_IRAM1$\$ZI$\$Limit作为内存分配的起始地址。

  

  

你可能感兴趣的:(编译原理)