您的位置:首页 > 运维架构 > Linux

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

2014-03-25 19:58 531 查看

链接脚本

什么是链接脚本?

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

为什么需要链接脚本?

程序运行在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)指定。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: