做人呢,最紧要就系开心啦

ARM64基础10:GNU LD链接器介绍

626次阅读
没有评论

链接器 Linker:是一个程序,将一个或多个编译器或汇编生成的目标文件,及依赖库,链接为一个可执行文件。

GNU Linker 采用 AT&T 链接脚本语言;

链接脚本文件:包含 ld 程序链接的规则,其决定输出可执行文件的内存布局;

LD 命令:arm64 版本的连接器是 aarch64-linux-gnu-ld
查看命令参数:

aarch64-linux-gnu-ld --help

LD 命令的参数有很多,常用的如下:

$(ARMGNU)-ld -T $(SRC_DIR)/linker.ld -Map xxx.map -o $(BUILD_DIR)/xxx.elf  $(OBJ_FILES)

常用参数:
-T:指定连接脚本;
-Map:输出一个符号表文件;
-o: 输出最终可执行二进制文件;

基本概念

输入段(input section),输出段(output section)
每个段包括 name 和大小;

段的属性:
loadable: 运行时会加载这些段内容到内存中;
allocatable: 运行时不加载段的内容;

段的地址:
VMA(virtual memory address): 虚拟地址,运行地址;
LMA(load memory address): 加载地址
通常 ROM 的地址为加载地址,而 RAM 地址为 VMA;

链接脚本命令

Entry(symbol): 设置程序的入口函数;
GNU-AS 程序入口点有以下几种:
a. 使用 - e 参数;
b. 使用 ENTRY(symbol);
c. 在.text 的最开始的地方;
d.0 地址;

INCLUDE filename: 引入 filename 的链接脚本;

OUTPUT filename:输出二进制文件,类似命令行的 "-o filename"

OUTPUT_FORMAT(bfd):输出 BFD 格式;
OUTPUT_ARCH(bfdarch): 输出处理器体系架构格式

OUTPUT_ARCH(aarch64)
ENTRY(_text)

赋值符号

符号可以像 C 语言一样赋值;
"." 表示当前位置;

symbol = expression;
symbol += expression;
symbol -= expression;
symbol *= expression;
symbol /= expression;
symbol <<= expression;
symbol >>= expression;
symbol &= expression;
symbol |= expression;

符号的引用

在 C 语言定义一个变量并初始化,编译器会分配一个符号,且在内存中分配空间存储;

在链接脚本中定义的变量,仅仅是在符号表里定义这个符号,没有分配存储空间;

xxx.ld
start_of_ROM = .ROM;
end_of_ROM = .ROM + sizeof(.ROM)

可以在每个段中设置一些符号,以方便 C 语言访问每个段的起始地址和结束地址;

 C 语言中,对链接脚本里定义的符号引用
extern char start_of_ROM[],end_of_ROM[];
char buf[4096];
memcpy(buf, start_of_ROM, end_of_ROM - start_of_ROM);// 引用的是地址

SECTIONS 命令

告诉链接器如何把输入段 (input sections) 映射到输出段(output sections),决定输出程序的内存布局;

输出 section 的描述符:
ARM64 基础 10:GNU LD 链接器介绍

LMA 加载地址

每个段都有 VMA 和 LMA;
在输出段描述符中使用“AT”指定 LMA, 如果没有指定,通常 LMA=VMA;
构造一个基于 ROM 的映像文件,常常会设置输出端的虚拟地址和加载地址不一样;

SECTIONS
{.text 0x1000:{*(.text)_etext=.;}
    .mdata 0x2000:
    AT(ADDR(.text)+SIZEOF(.text))  // 指定加载地址
    {
        _data=.;
        *(.data);
        _edata=.;  //_data 和_edata 只是一个符号,用来记录 mdata 段的 VMA 地址
    }
    .bss 0x3000;
    {
        _bstart=.;
        *(.bss)*(COMMON);
        -bend=.;
    }
}

mdata 段的加载地址和链接地址不一样,因此程序初始化代码需要把 data 段内容复制到 SDRAM 中的虚拟地址中;
ARM64 基础 10:GNU LD 链接器介绍

常见的内建函数(builtin functions)

ADDR(section): 返回前面已经定义过的段的 VMA 地址;
ALIGN(n): 返回下一个与 n 字节对齐的地址;
SIZEOF(section): 返回一个段的大小;
MAX/MIN(exp1,exp2): 返回较大 / 较小值;

实例练习:设置代码段的 LMA!=VMA

TEXT_ROM = 0X90000;
SECTIONS
{
    /*
     * -->location, current addr
     *  0x80000 is entrance address
     */
    . = 0x80000,
    /*
     * first instruction
     **/
    _text_boot = .;
    .text.boot : {*(.text.boot) }  
    _etext_boot = .;

    /*
     * code segment
     */
    _text = .;
    .text : AT(TEXT_ROM)
    {*(.text) }
    _etext = .;

    /*
     * readonly data segment
     */
    _rodata = .;
    .rodata : AT(ADDR(.rodata))
    {*(.rodata) }
    _erodata = .;

    /*
     * data segment
     */
    _data = .;
    .data : {*(.data) }
    _edata = .;

    /*
     * bss segment
     * 8bytes algine
     */
    . = ALIGN(0x8);
    _bss = .;
    .bss : {*(.bss*) } 
    _ebss = .;

    /*
     * alloc a page memory, store page table
     * aligne 4096
     */
    . = ALIGN(4096);
    init_pg_dir = .;
    . +=4096;
}

在启动代码,将代码段从 LMA(ROM 地址)拷贝到 VMA(SDRAM 地址)

master:
/*
 * copy code segment from rom(LMA) TO ram(VMA)
 */
    adr x0, TEXT_ROM
    adr x1, _text
    adr x2, _etext
1:
    ldr x4, [x0], #8
    str x4, [x1], #8
    cmp x1, x2
    b.cc 1b

正文完
 
admin
版权声明:本站原创文章,由 admin 2021-11-02发表,共计2539字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)