GNU链接脚本(01) - 基本概念

原文:https://github.com/iDalink/ld-linker-script/tree/master/01%20%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5

1、概述

        Linux编译产出文件种类包括目标文件、可执行文件、动态链接文件、目标归档文件。这些文件格式常为ELF格式。

文件类型英文名扩展名生成指令
目标文件object file.ogcc -C ...
可执行文件executable file————ld ...
动态链接文件shared object file.sold -shared ...
目标归档文件archived file.aar c ...

        针对一个目标文件,其内容被划分成多个成为section的区段,比如记录指令的.text段,记录数据的.data段。每个区段组成表项,记录在ELF的头部区域。每个表项都记录该段在文件内部的偏移,段被加载的内存地址。

当一个程序被连接时,多个文件的section会按照一定规则合并成节(segment)。程序执行时,系统以节的视角加载该可执行程序。一种特殊情况是,当程序的存储载体是ROM介质,那么程序执行时,需要预先把存储数据的segment拷贝到RAM介质中。还要注意的是,这个RAM介质和ROM介质处于同一个地址空间的下不同地址段中。也就是说,节在存储时会记录两个地址,运行地址称为VMA,存储地址称为LMA。

        C函数、变量和汇编的标号在编译时都会转化为符号(symbol)。符号的值为该变量的地址。当一个源文件引用外部的符号时,编译产出得到一个未定义的符号,未定义的符号在链接决议时转化为定义的符号,并在指令区域填入正确的符号地址。

        除了函数和变量,符号还可以来自系统环境信息,比如源程序文件名,也可以来自linker script,甚至可以来自汇编源程序。

2、测试用例

        下面动手操练下上述概念。创建三个源文件app.c、tool.c和tool.h。编译过程由Makefile管理。请简要浏览Makefile以了解编译过程。这些代码位于01文件夹中。

        使用 readelf查看文件的符号表如下。

$ readelf -s tool.o

Symbol table '.symtab' contains 9 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS tool.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     8: 0000000000000000    53 FUNC    GLOBAL DEFAULT    1 math_pow

$ readelf -s app.o

Symbol table '.symtab' contains 13 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS app.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     9: 0000000000000000    67 FUNC    GLOBAL DEFAULT    1 main
    10: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND math_pow
    12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

$ readelf -s tool_base.o

Symbol table '.symtab' contains 10 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    2 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    9 
     8: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT    1 math_add
     9: 0000000000000007     0 NOTYPE  GLOBAL DEFAULT    1 math_add_end

        源程序文件名tool.capp.c也成为目标文件的符号,并且类型为FILE。app.o记录的外部符号path_pow为NOTYPE类型,并且索引类型为UND未决议类型。

        我们在汇编程序中定义了math_add_end符号。我们该如何认识这个符号?这个符号有值吗?这个符号有地址吗?这个符号的类型是什么?

        math_add_end没有长度,即不占用地址空间。所以其没有值概念,但是可以对其取地址,地址即为连接后VMA地址。由于math_add_end仅仅记录一个内存位置,所以我们把它声明为void *类型,并在其后的内存位置填入magic数据。app.c中打印了该符号的地址和值。毫无疑问符号地址位于math_add后7个字节处,其值为magic值,因为该符号本身没有值,对齐取值会导致内存越界。

        日志打印如下。

./app
2^10=1024
2+10=12
math_add func len:0x7
math_add=0xd26b47a6
math_add_end=0xf0f0f0f0

        linker script也可以设置符号。我们会在后续章节中介绍。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值