当前位置: 首页 > 编程日记 > 正文

ARM的位置无关程序设计在Bootloader中的应用

http://www.mcuol.com/tech/107/26052.htm

引言

基于位置无关代码PIC(PositionIndependent Code)的程序设计在嵌入式应用系统开发中具有重要的作用。

尤其在裸机状态下开发Bootloader程序及进行内核初始化设计;利用位置无关的程序设计方法还可以在具体应用中用于构建高效率动态链接库,因而深入理解和熟练掌握位置无关的程序设计方法,有助于开发人员设计出结构简单、清晰的应用程序。本文首先介绍位置无关代码的基本概念和实现原理,然后阐述基于 ARM汇编位置无关的程序设计方法和实现过程,最后以Bootloader程序设计为例,介绍了位置无关程序设计在Bootloader程序设计中的作用。

1 位置无关代码及程序设计方法

1.1 基本概念与实现原理

应用程序必须经过编译、汇编和链接后才变成可执行文件,在链接时,要对所有目标文件进行重定位(relocation),建立符号引用规则,同时为变量、函数等分配运行地址。当程序执行时,系统必须把代码加载到链接时所指定的地址空间,以保证程序在执行过程中对变量、函数等符号的正确引用,使程序正常运行。在具有操作系统的系统中,重定位过程由操作系统自动完成。

在设计Bootloader程序时,必须在裸机环境中进行,这时Bootloader映像文件的运行地址必须由程序员设定。通常情况下,将 Bootloader程序下载到ROM的0x0地址进行启动,而在大多数应用系统中,为了快速启动,首先将Bootloader程序拷贝到SDRAM中再运行。一般情况下,这两者的地址并不相同,程序在SDRAM中的地址重定位过程必须由程序员完成。实际上,由于Bootloader是系统上电后要执行的第一段程序,Bootloader程序的拷贝和在这之前的所有工作都必须由其自身来完成,而这些指令都是在ROM中执行的。也就是说,这些代码即使不在链接时所指定的运行时地址空间,也可以正确执行。这就是位置无关代码,它是一段加载到任意地址空间都能正常执行的特殊代码。

位置无关代码常用于以下场合:

程序在运行期间动态加载到内存;
程序在不同场合与不同程序组合后加载到内存(如共享的动态链接库);
在运行期间不同地址相互之间的映射(如Bootloader程序)。
虽然在用GCC编译时,使用-fPIC选项可为C语言产生位置无关代码,但这并不能修正程序设计中固有的位置相关性缺陷。特别是汇编语言代码,必须由程序员遵循一定的程序设计准则,才能保证程序的位置无关性。

1.2 ARM处理器的位置无关程序设计要点

ARM程序的位置无关可执行文件PIE(PositionIndependent Executable)包括位置无关代码PIC和位置无关数据PID(PositionIndependent Data)两部分。

PID主要针对可读写数据段(.data段),其中保存已赋初值的全局变量。为实现其位置无关性,通常使用寄存器R9作为静态基址寄存器,使其指向该可读写段的首地址,并使用相对于基址寄存器的偏移量来对该段的变量进行寻址。这种方法常用于为可重入程序的多个实例产生多个独立的数据段。在程序设计中,一般不必考虑可读写段的位置无关性,这主要是因为可读写数据主要分配在SDRAM中。

PIC包括程序中的代码和只读数据(.text段),为保证程序能在ROM和SDRAM空间都能正确运行(如裸机状态下的Bootloader程序),必须采用位置无关代码程序设计。下面重点介绍PIC的程序设计要点。

PIC遵循只读段位置无关ROPI(ReadOnly Position Independence)的ATPCS(ARMThumb Procedure Call Standard)的程序设计规范:

(1) 程序设计规范

引用同一ROPI段或相对位置固定的另一ROPI段中的符号时,必须是基于PC的符号引用,即使用相对于当前PC的偏移量来实现跳转或进行常量访问。

① 位置无关的程序跳转。在ARM汇编程序中,使用相对跳转指令B/BL实现程序跳转。指令中所跳转的目标地址用基于当前PC的偏移量来表示,与链接时分配给地址标号的绝对地址值无关,因而代码可以在任何位置进行跳转,实现位置无关性。

另外,还可使用ADR或ADRL伪指令将地址标号值读取到PC中实现程序跳转。这是因为ADR或ADRL等伪指令会被编译器替换为对基于PC的地址值进行操作,但这种方式所能读取的地址范围较小,并且会因地址值是否为字对齐而异。 但在ARM程序中,使用LDR等指令直接将地址标号值读取到 PC中实现程序跳转不是位置无关的。例如:

LDRPC, =main

上面的LDR汇编伪指令编译后的结果为:


LDRPC, [PC, OFFSET_TO_LPOOL]

LPOOLDCD main

可见,虽然LDR是把基于PC的一个存储单元LPOOL的内容加载到PC中,但该存储单元中保存的却是链接时所决定的main函数入口的绝对地址,所以main函数实际所在的段不是位置无关。

② 位置无关的常量访问。在应用程序中,经常要读写相关寄存器以完成必要的硬件初始化。为增强程序的可读性,利用EQU伪指令对一些常量进行赋值,但在访问过程中,必须实现位置无关性。下面以PXA270的GPIO初始化介绍位置无关的常量访问方法。

程序

可见,LDR伪指令实际上使用基于PC的偏移量来对符号常量GPIO_BASE和init_GPDR0进行引用,因而是位置无关的。由此可以得出如下结论:使用LDR伪指令将一个常量读取到非PC的其他通用寄存器中可实现位置无关的常量访问;但将一个地址值读取到PC中进行程序跳转时,跳转目标则是位置相关的。

(2) 程序设计规范2

其他被ROPI段中的代码引用的必须是绝对地址,或者是基于可读写位置无关(RWPI)段的静态基址寄存器的可写数据。

使用绝对地址只能引用被重定位到特定位置的代码段中的符号,通过在位置无关代码中引入绝对地址,可以让程序跳转到指定位置。例如,假设 Bootloader的阶段1将其自身代码拷贝到链接时所指定的SDRAM地址空间后,当要跳转到阶段2的C程序入口时,可以使用指令“LDRPC, =main”跳转到程序在SDRAM中的main函数入口地址开始执行。这是因为程序在编译链接时给main函数分派绝对地址,系统通过将main函数的绝对地址直接赋给PC实现程序跳转。如果使用相对跳转指令“Bmain”,那么只会跳转到启动ROM内部的main函数入口。

2 位置无关代码在Bootloader设计中的应用

在使用GNU工具开发Bootloader时,程序在链接时会通过一个链接脚本(linker script)来设定映像文件的内存映射。一个简单的链接脚本结构如下:

程序

这里不再介绍链接脚本的语法。需要指出的是,链接脚本中所描述的输出段地址为虚拟地址VMA(Virtual Memory Address)。这里的“虚拟地址”仅指映像文件执行时,各输出段所重定位到相应的存储地址空间,与内存管理无关。因此,上面的链接脚本实际上指定了 Bootloader映像在执行时,将被重定位到BOOTADDR开始的存储地址空间,以保证在相关位置对符号进行正确引用,使程序正常运行。

ARM处理器复位后总是从0x0地址取第1条指令,因此只需把BOOTADDR设置为0,再把编译后生成的可执行二进制文件下载到ROM的 0x0地址开始的存储空间,程序便可正常引导;但是,一旦在链接时指定映像文件从0x0地址开始,那么Bootloader就只能在0x0地址开始的 ROM空间内运行,而无法拷贝到SDRAM空间运行实现快速引导。当然,对PXA270等具有MMU功能的微处理器来说,虽然可以先将 Bootloader映像整个拷贝到SDRAM中,再使用MMU功能将SDRAM空间映射到0x0地址,进而继续在SDRAM中运行;但这样一方面会使得 Bootloader的设计与实现复杂化,另一方面在一些必须屏蔽MMU功能的应用中(例如引导armlinux系统),无法使用MMU进行地址重映射。

利用ARM的基于位置无关的程序设计可以解决上述问题。只需在程序链接时,将BOOTADDR设置为SDRAM空间的地址(一般情况下利用 SDRAM中最高的1 MB存储空间作为起始地址),这样ARM处理器上电复位后Bootloader仍然可以从地址0开始执行,并将自身拷贝到指定的__boot_start 起始的SDRAM中运行。实现上述功能的链接脚本所对应的启动代码架构如下:

程序

程序入口为_start,即复位异常,所有其他异常向量都使用相对跳转指令B来实现,以保证位置无关特性。在完成基本的硬件初始化后,利用链接脚本传递过来__boot_start和__boot_end的参数,将Bootloader映像整个拷贝到指定的SDRAM空间,并清零.bss段,初始化堆栈后,程序将main函数入口的绝对地址赋给PC,进而跳转到SDRAM中继续运行。程序在跳转到main函数之前,所有的代码都在ROM中运行,因而必须要保证代码的位置无关性,所以在调用初始化GPIO、存储系统和堆栈等子程序时,都使用相对跳转指令来完成。

使用位置无关设计Bootloader程序有如下优点:

① 简化设计,方便实现系统的快速引导。位置无关代码可以避免在引导时进行地址映射,并方便地跳转到SDRAM中实现快速引导。

② 实现复位处理智能化。由于位置无关代码可以被加载到任意地址空间运行,因此其运行时的当前地址与链接时所指派的地址并不一定相同。利用这一特性,可以在复位处理程序中使处理器进入SVC模式并关闭中断后加入如下代码,便可根据当前运行时的地址进行不同的复位处理:

程序


上述代码中的ADR指令读取的_start标号地址由指令的执行地址决定。若是从SDRAM中的Bootloader启动,则上述比较结果相等,程序直接跳转到clear_bss标号地址处执行,这样可以避免存储系统的重新初始化和Bootloader的拷贝过程;若是上电或硬件复位,程序从ROM启动,则上述比较结果不等,程序便进行包括系统初始化和Bootloader拷贝等过程的全面复位处理操作。

③ 便于调试。Bootloader的调试通常也是一个繁琐的过程,使用位置无关代码,则可以将映像文件加载到SDRAM中进行调试,这既能真实地反映程序从ROM中进行系统引导的情况,又可以避免频繁烧写程序存储器。

3 结论

本文所介绍的基于位置无关的程序设计是通过基于PC或基址寄存器的符号引用规范来实现的。这种方法在实际系统开发中应用广泛,既能用于引导程序的设计,也可用于一般的应用程序或嵌入式共享库的开发。而在Bootloader的设计中引入位置无关代码,可以使程序结构更为简单清晰,并能避免地址重映射并从SDRAM进行快速系统引导;引用位置无关的设计方法使Bootloader的复位处理功能更为灵活,还使得在SDRAM中和在ROM中进行程序调试具有相同的效果。

参考文献

[1] 杜春雷. ARM体系结构与编程[M]. 北京:清华大学出版社,2003.

[2] 陈文智. 嵌入式系统开发原理与实践[M]. 北京:清华大学出版社,2005.

[3] ARM Limited. ARM Architecture Reference Manual. 2nd ed,2000.

[4] ARM Limited. ADS Developer Guide. Release 1.2,200111.

[5] ARM Limited. ADS Assembler Guide. Release 1.2,200111.

[6] Red Hat Inc. Using ld, the GNU linker. Version 2.14.

相关文章:

CentOS 7 添加系统开机服务

CentOS 7的服务systemctl脚本存放在:/usr/lib/systemd/,有系统(system)和用户(user)之分,需要开机不登陆就能运行的程序,存在系统服务里,即:/usr/lib/systemd…

UITableView HeaderView,FooterView 使用SnapKit布局导致约束异常

今天做一个APP里面设置页面(个人中心) 就是一个列表菜单 顶部是一个头像和账户标题, 底部为一个退出登录按钮 当然我第一时间就想到了UITableView, HeaderView, FooterView // 我创建了两个类文件, 用来做UITableView 的header, footer class SettingHeaderView: UIView {//里面…

【spring】专项配置文件的使用

1、编写专项配置文件(数据库专项文件) 代码实现: mysql-urljdbc:mysql://localhost:3306/test mysql-drivercom.mysql.jdbc.Driver 2、在xml文件里配置contest 代码实现: xmlns:context"http://www.springframework.org…

Ubuntu换回Gnome界面

安装了Ubuntu11.10,Unity界面实在是不怎么习惯。遂换回Gnome。 1. 首先安装Gnome sudo apt-get install gnome-shell sudo apt-get install gnome-tweak-tool 2. 设置自动登录Gnome shell classic sudo /usr/lib/lightdm/lightdm-set-defaults -s gnome-classic  …

Linux设备文件简介。

概述 设备管理是linux中比较基础的东西,但是由于Linux智能程度的越来越高,Udev的使用越来越广泛,使得越来越多的Linux新用户对 /dev 目录下的东西变得不再熟悉。有时候遇见问题就会变得抓狂。 Linux 中的设备有2种类型:字符设备(无…

羊车门问题分析

1、按照你的第一感觉回答,你觉得不换选择能有更高的几率获得汽车,还是换选择能有更高的几率获得汽车?或几率没有发生变化?答:换。2、请自己认真分析一下“不换选择能有更高的几率获得汽车,还是换选择能有更…

python多版本共存使用pip

有时候大家使用python的时候都会用到两个版本,虽说可以独立设置环境变量独立使用,但是用到pip的时候就不行了,python2和python3中的都用相同的pip。在这里我就把我的经验分享给大家。 这是python设置的环境变量 下面是python2的名字 下面是py…

Ruby开发环境

假设您已经安装了Eclipse,那么只需按照如下步骤即可:Please copy the following Update Site URL to your clipboard and then follow the steps listed below to add this URL to your Available Software Sites list. Attempting to access this URL u…

【spring】spel表达式

格式: #{ } 简单使用实例 一、DButil.java 代码实现: package springboottest.ioc.speltest;public class DBUtil {private UserService us;public UserService getUs() {return us;}public void setUs(UserService us) {this.us us;} }二、UserSe…

distinct吃亏记

distinct 是一个去掉重复数据的函数 1.此函数只能在 头部 2.此函数声明后。如果跟两个或两个以上的字段。则会导致。此函数去判断与两个字段都相同的。 例如ID为2 的字段有两个。但是这两个字的的创建时间不同。所有当你想通过DISTINCT ID,CREATTIME来把其中一个ID给筛选的话是…

input 选择框改变背景小技巧

最近在项目中遇到一个问题,想要改变input选择框的背景,然而,令我没有想到的是,竟然无法直接改变背景的颜色 通常情况下:我们都可以通过改变元素的 background-color 的值来改变元素的背景,但是在input选择框…

[转]Android JNI使用方法

本文转自:http://www.open-open.com/lib/view/open1324909652374.html 经过几天的努力终于搞定了android JNI部分,下面将我的这个小程序和大家分享一下。android JNI是连接android Java部分和C/C部分的纽带,完整使用JNI需要Java代码和C/C代码…

【spring】使用eclipse在没网时编写配置文件无法获取提示 解决方法

获得xsd文件 1、打开maven的目录 2、按照该顺序找到spring-context的目录 3、选择最新版本的进入 4、找到context的jar包 5、解压到存放xsd文件的文件夹(自己建立) 6、按照该顺序进入目录 7、将xsd文件放到cpfhxsd文件的文件夹 将xsd文件配置到ecipse中…

P4568 [JLOI2011]飞行路线

P4568 [JLOI2011]飞行路线 Description Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在n个城市设有业务,设这些城市分别标记为0到n-1,一共有m种航线,每种航线连接两个城市,并且航线有…

拿到淘宝offer后的胡思乱想plus面试总结

没想到能拿到淘宝的实习offer,心里还是很激动的。 大三以后就忙着找实习,参加了SAP和淘宝的校招,呵呵,还好,第二次就拿到了offer,剩下还有腾讯和百度的招聘,决定去看看,但是还是要走…

Map与List数据操作

为避免与数据库的多次连接,减少数据库的压力,先将所有的订货数据先从数据库中抽取出来,而后再将数据按门店进行分类汇总以备待用,Map与List混合操作,理解数据结构。提神醒脑哦。以下是原始数据结构:[{store…

《Java虚拟机规范》阅读(三):Class文件格式

每一个Class都对应着唯一的一个类或借口的定义信息。这里,我们称为"Class文件格式"只是通俗的将任意一个符合有效的类或借口的格式这么称呼,但是它并不一定是以磁盘文件的形式存在。 每个Class文件都是由8字节为单位的字节流组成,所…

【spring】自动装配

山顶洞人方法:aotowire UserDao.java 代码实现: public class UserDao {private DBUtil dbu;public DBUtil getDbu() {return dbu;}public void setDbu(DBUtil dbu) {this.dbu dbu;}public void test() {System.out.println(dbu);} }DBUtil.java 代…

扩展jquery实现客户端表格的分页、排序

下面链接中是我用jQuery的扩展来实现的表格分页和排序&#xff0c;使用这个扩展必须加上表头<thead>和<tbody>标签&#xff0c;因为我是 通过<tbody>来进行分页的&#xff0c;要是不加thead&#xff0c;那么表头也要作为分页计算时的一个行了。 下载最新代码…

Win7中如何删除word模板

Win7中如何删除word模板 计算机→本地磁盘c盘→用户→Administrator→AppData→Roaming→Microsoft→Templates转载于:https://blog.51cto.com/ilanni/555302

Spring 学习笔记

Spring 的 Ioc 容器所有的组件都是被动的&#xff08; Passive&#xff09;&#xff0c;所有的组件初始化和调用都由容器负责。组件处在一个容器当中&#xff0c;由容器负责管理。BeanFactory 根据配置文件确定容器中 bean 的实现&#xff0c;管理 bean 之间的依赖关系。通常&a…

【spring】初识aop(面向切面编程) 使用jdk动态代理

BankServiceIImple.java 代码实现&#xff1a; package com.zzxtit.aop;import java.math.BigDecimal;public interface BankServiceImple {public void transfer(String source, String target, BigDecimal money);public void withdraw(String account, BigDecimal money);…

sigaction

#include <signal.h> int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact); struct sigaction {   void (*sa_handler)(int);   void (*sa_sigaction)(int, siginfo_t *, void *);   sigset_t sa_mask;   int sa_flags;   void …

003本周总结报告

本周对java的循坏结构和条件语句以及switch分支进行了复习并通过九九乘法表和制作日历来更加熟练使用和理解循环&#xff0c;并用eclipse替代了记事本来编写程序&#xff0c;同时针对记事本编写java程序后台运行出现的GBK不可映射字符问题先后采用了 javac -encoding UTF-8 …

HDU-1203 I NEED A OFFER!-0、1背包及空间优化

I NEED A OFFER! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 5280 Accepted Submission(s): 1799Problem DescriptionSpeakless很早就想出国&#xff0c;现在他已经考完了所有需要的考试&#xff0c;准备了…

docker监控系统

第一&#xff1a;docker监控系统之命令行式监控 第二&#xff1a;docker监控系统之cadvisor 第三&#xff1a;docker监控系统之 第四&#xff1a;docker监控系统之 转载于:https://www.cnblogs.com/fengjunhua/p/8968210.html

Visual Studio 2005 Team System下载地址

注册一个msn就可以去微软下载了&#xff0c;关于替换序列号变成正版的方法我没有试&#xff0c;team suite我在用&#xff0c;但Team Foundation Server 我还没有安装好Microsoft Visual Studio 2005 简体中文正式版Visual Studio 2005 Team Suite 180 天试用版(可更换key变为正…

【spring】springAop开发

xml开发方式 springboot的使用 1、声明一个parent 代码实现&#xff1a; <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.0.RELEASE</version><relati…

使用 SqlHelperParameterCache 类管理参数

SqlHelperParameterCache类是位于 Microsoft.ApplicationBlocks.Data命名空间底下。它底下有三个方法&#xff0c;分别是&#xff1a; CacheParameterSet&#xff1a;用于将SqlParameters 数组存储到缓存中GetCachedParameterSet&#xff1a;用于检索读取缓存中SqlParameters数…

改善C#程序的建议6:在线程同步中使用信号量

所谓线程同步&#xff0c;就是多个线程之间在某个对象上执行等待&#xff08;也可理解为锁定该对象&#xff09;&#xff0c;直到该对象被解除锁定。C#中对象的类型分为引用类型和值类型。CLR在这两种类型上的等待是不一样的。我们可以简单的理解为在CLR中&#xff0c;值类型是…