嵌入式Linux入门基础知识 ---- 链接脚本、汇编语言、混合编程

链接脚本

什么是链接脚本?

  • 程序链接时的参考文件,目的是描述输入文件中各段应该怎样被映射到输出文件,以及程序运行时的内存布局等 

 

为什么需要链接脚本?

  • 程序运行在OS之上时,不需要显式指定链接脚本,默认使用OS相关链接命令内置的脚本,可避免出错
  • 程序运行在OS之下时,或者本身就是OS时,链接脚本就很重要。要根据实际环境去编写链接脚本才不容易出错

 

示例代码

ENTRY(helloworld)
SECTIONS
{
    . = 0x00000000;
    .text :{
          *(.text)  
    } 
    . = ALIGN(32);
    .data :{
          *(.data)
    }
    . = ALIGN(32);
    .bss: {
          *(.bss)
    }
   .haha: {
*(.haha)
  } }
  • 点号(.):位置计数器,代表当前位置。可以赋值。会自动动态增加
  • text, data, bss分别代表代码段、数据段和bss段,其实名字可随意命名,就像haha段
  • *(.text)的意思是所有目标文件的代码段都被链接到这个区域,也可以特别地指定某个目标文件出现在代码段的最前面
  • ALIGN(N):数据和代码段对齐。例如 . = ALIGN(4)就是使位置计数器向高地址取最近4字节的整数倍。其实你也可以不使用,像上面那样,造成haha段地址紧跟着bss段,这时可能就会造成字节不对齐
  • ENTRY()命令是指定整个程序的入口地址,例如ENTRY(helloworld)就是指定hello world函数为整个程序入口地址。也就是说,helloworld就是出现在0x00000000地址上的那个函数

 

一些说明

  • 在链接操作中,参数-Ttext的作用是指定该程序的运行基地址。也就是说,链接工具将目标文件链接到某地址上,并从此处执行
  • -verbose参数可以用来查看链接脚本

 

ARM汇编语言

注意事项

  • ARM的通用寄存器R0 ~ R12随你怎么用
  • R13负责保存堆栈地址,SP
  • R14负责保存程序返回地址,LR
  • R15负责记录程序地址,PC
  • CPSR程序状态寄存器,判断程序是否溢出、负数,也包含当前处理器模式

 

示例代码

.arch armv7                         # 选择目标体系结构,这里指定编译后生成armv7体系结构的代码
.global helloworld                  # 若想在外部文件里引用函数或变量,就要用.global声明,相当于C语言extern

.equ REG_FIFI, 0x50000020       # 相当于C语言宏定义,这里是指串口地址

.text                   # 从当前位置开始的内容被归并到代码段中
.align 2                  # 在ARM下,.align后面的数是以幂出现的,这里指按4字节对齐

helloworld:
    ldr r1, =REG_FIFO         # 将常量写入寄存器中,即将串口地址存储到r1中
    adr r0, .L0             # 将相对地址写入寄存器中,这里使r0指向helloworld字符串的首地址

.L2:
    ldrb r2, [r0], #0x1        # b的意思是ldr一个字节的数据,这里意思是把r0的内容的一个字节写到r2,然后r0值加1。即使r2指向‘h‘,r0指向‘e‘
    str r2, [r1]            # 将寄存器值存储到另一个寄存器所指向的内存地址中。这里把r2的值存到r1中,即把‘h‘通过串口打印到屏幕上
    cmp r2, #0x0            # 将两个数相减,影响CPSR的零标志位,从而影响判断条件的执行
    bne .L2               # 如果r2 != 0,跳到.L2,即执行循环。 不等于0说明还没有到达字符串结尾,因为字符串最后是‘\0‘

.L1:
    b .L1

.align 2

.L0:
    .ascii "helloworld\n\0"      # 用于在内存中定义字符串,这里定义“helloworld\n”字符串
  • 伪指令不一定以.开头,例如ldr, adr
  • 伪指令与编译器相关,不同编译器的伪指令集可能不同,这里是gcc编译器,换了一种编译器不一定能完全兼容

 

 

混合编程

说明

  • 汇编语言和C语言混合编程要遵守约定的标准,叫过程调用标准(Procedure Call Standard),简称PCS
  • ARM过程调用标准叫APCS

 

示例代码

文件start.s:

.arch armv7 .
global _start .equ REG_FIFO, 0x50000020 .text .align 2 _start: ldr r0, =REG_FIFO          # r0, r1这两个值就是传给helloworld的参数 adr r1, .L0 bl helloworld            # bl的作用和b医院,只是多了保存程序返回地址的功能 .L1: b .L1 .align 2 .L0: .ascii "helloworld\n\0"
文件helloworld.c:
int helloworld(unsigned int *addr, const char *p) { while(*p) { *addr = *p++; }; return 0; }
  • AAPCS中规定,发生函数调用时,函数的参数保存在ARM核心寄存器的R0 ~ R3中,如果参数多余3个,则剩下的参数保存在堆栈中
  • 在代码中,调用helloworld之前,给R0和R1赋的值就是传给helloworld的参数
  • 因此,helloworld返回int型值,这个返回值会保存到R0中,如果返回的是64位的值,将由R0,R1同时保存
  • 最后编译的时候使用命令
    arm-elf-ld -e _start -Ttext 0x0 start.o hello world.o -o helloworld

    运行成功,这里-e命令是说以_start函数为程序入口函数,也可以在链接脚本里用ENTRY(_start)指定。

嵌入式Linux入门基础知识 ---- 链接脚本、汇编语言、混合编程,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。