arm平台的对齐问题

上传人:第*** 文档编号:30678112 上传时间:2018-01-31 格式:DOC 页数:4 大小:64.50KB
返回 下载 相关 举报
arm平台的对齐问题_第1页
第1页 / 共4页
arm平台的对齐问题_第2页
第2页 / 共4页
arm平台的对齐问题_第3页
第3页 / 共4页
arm平台的对齐问题_第4页
第4页 / 共4页
亲,该文档总共4页,全部预览完了,如果喜欢就下载吧!
资源描述

《arm平台的对齐问题》由会员分享,可在线阅读,更多相关《arm平台的对齐问题(4页珍藏版)》请在金锄头文库上搜索。

1、ARM 平台的对齐问题(有关_packed)前言ARM 流行已久,做嵌入式开发的不知道 ARM 不大可能。鉴于其所具备的较低功耗下的较高性能,也就成了大多数嵌入式设备的首选了。不过对于刚上手的人来说,有可能会遇到一些稀奇古怪的问题。毕竟大部分人都习惯了 IA-32 下的程序设计,虽然两者都是 32 位的处理器,但是体系架构完全不同,于是也导致了一些隐含的问题。这里想描述一下一个有点蛊惑的问题,即在 ARM 上访问非对齐地址内容,会出现所谓“不可预料”结果的问题。ARM 内存访问的对齐问题按照 ARM 文档上的描述,其访问规则如下:1. 一次访问 4 字节内容,该内容的起始地址必须是 4 字节对

2、齐的位置上;2. 一次访问 2 字节内容,该内容的起始地址必须是 2 字节对齐的位置上;(单字节的没有这个问题,就不用考虑啦。 )好,既然规则如此,那应该遵守。不过么,不安分的人往往喜欢破坏规则,喜欢看看不遵守规则会有什么结果;另外么,即便遵规蹈距的人,有时也难免考虑不周,犯个错也是正常现象。好,那么让我们来看看犯错的结果吧。例如下面的代码:char buff8 = 0x12, 0x34, 0x56, 0x78, 0x9a, 0xab, 0xbc, 0xcd;int v32, *p32;short v16, *p16;p32 = (int*) /unalignmentp16 = (short*

3、) /unalignmentv32 = *p32; /whats the result?v16 = *p16; /whats the result?如果上面这段代码在 IA-32 上运行,那么结果应该如下:v32 = 0x9a785634v16 = 0x5634即便非对齐地址上访问,IA-32 也就是牺牲一点性能,但是结果保证是正确的。恩,这也是我们所期望的可是 换到 ARM 上呢?我们来看看在 ADS1.2 编译后,执行的结果如下:v32 = 0x12785634v16 = 0x1234这个结果有点奇怪了吧。照理说指向 0x34,那么如果是 Big-Endian 的话,v32 应该是 0x3

4、456789a,如果是 Little-Endian 的话,就是前面 IA-32 的结果。可现在的结果呢?两者都不是,莫名地把更低地址的 0x12 给凑进来了 而如果看看编译生成的汇编 code 的话,这两个赋值很简单,分别用了 ldr 和 ldrsh 指令,指令没有问题,分别用于读取 32 位和 16 位数据,都是最基本的指令。嗯,嗯,这就是我们所要描述的访问非对齐地址的问题了。 问题的缘由(个人猜测,非官方资料)个人感觉呢,这是 ARM 体系架构实现的问题,或者说这本来就是 By Design 的。这样做简化了处理器的实现,IA-32 实现的时候肯定会对读取地址是否对齐进行判断,然后转换为相

5、应的操作 ,而 ARM 呢?没有做这个事情,默认认为大家都按照规矩办事,你要是胆敢破坏,俺就给你好看那有没有办法解决呢?这个问题其实 ARM 自己也知道,所以呢,它在编译器里面,已经添加了部分支持。不过有人会问,那上面那个情况呢?为什么结果还是不对呢?好像没有添加什么支持嘛嗯,其实 ARM 是做了一定的努力的,只是这个情况它没办法解决 它做的事情就是:在编译器能够的得知的情况下,尽量保证访问内容的正确。这句话有点笼统,那么把具体情况一个个来看看吧。编译器的努力(1) 所有局部/全局 /静态等变量都放在 4 字节对齐的地址上其实这个努力很常见,由于在 32 位平台上,一次访问 4 字节是效率最高

6、的,所以大多数 32 平台的编译器都如此处理,ARM 的 ADS 也不例外。编译器的努力(2) 填充、填充、再填充这个事情么,其实也是常见的。各类编译器上,对于某些结构定义中会产生不对齐的情况,自动填充,以提高访问效率(例如 IA-32 上访问非对齐的,会加 1 个周期的) 。而 ARM 的编译器也一样操作,不过感觉这里不单单是为了提高效率,也能够顺带解决这个不对齐的问题。编译器的努力(3) 产生特殊代码嗯,这个就是关键了,也是 ARM 编译器的与众不同之处。先来看一段代码:_packed typedef struct _testchar a;short c;int d; test;char

7、buff8 = 0x12, 0x34, 0x56, 0x78, 0x9a, 0xab, 0xbc, 0xcd;test *p = (test *)buff;v32 = p-d; /这里的 v32 借用上面的定义; 貌似多了个限定为_packed 的 struct,以此来造成不对齐的状况,看不出多大区别嘛。可是运行一下的话,就会发现这里的结果是正确的。我们来看看 ADS 生成的汇编代码吧。v32 = q-d;0xe2890003 add r0,r9,#30xeb000088 bl _rt_uread40xe1a05000 mov r5,r0看到这里的那条bl _rt_uread4的指令了吧。对

8、ARM 指令有一定了解的都知道 bl 其实就是一个函数调用。所以,这里的代码其实是调用了ADS 自己提供的 _rt_uread4 函数,该函数完成的操作就是读取四个字节。ADS 提供了类似的一系列函数,针对 signed/unsigned,以及 4 字节/2 字节的读取/写入操作。估计看到这里,大家会问,如果没有_packed 限定符呢?猜对了,没有_packed 限定符,那么编译器会对上面的情况 pending,所以这个 struct 里面的 d 所在的位置是 4 字节对齐的(编译期信息,而非实际运行期信息) 。所以就回到类似最初的例子了。那么,还有一种情况,就是在有_packed 的情况下

9、,而 struct 里的字段都是符合对齐要求的,那么生成的代码会是怎么样的呢?从实际生成的代码来看,和上面的这段汇编代码,唯一的区别就是第一条指令把#3 改成了#4,而后面仍旧调用_rt_uread4 函数。嗯,这样结论就出来了:编译器会在使用_packed 的情况下,自动对其中的 4 字节/2 字节访问添加特殊代码,以保证其结果的正确。好了,这个关于这个问题描述得差不多了,可能的话,尽量倚赖编译器的这些功能,而对于编译器无能为力的部分,就要靠万分小心了 p.s. 其实这里有很多事情可以来尽量预防此类问题,比如嵌入式项目往往喜欢自己管理内存分配,那么自己写的内存分配函数就保证返回的地址都是 4

10、 字节对齐位置上的 32 位嵌入式系统的字节对齐!(重要)32 位嵌入式系统的软件开发过程中,字节对齐问题是相当重要的。我们现在就拿 ARM 处理器和 ADS1.2 开发环境作为例子说明字节对齐的概念。 在此之前,我先声明几个基本的概念: (1)、对象:在 C 语言中使用结构体类型、共同体类型、或内部基本类型所定义的变量或常量,就称为对象。对象占据了一块实际的存储器空间,这块空间有固定的起始地址和字节数。 (2)、引用:使用对象有两种方法:“对象名”和“引用” 。当你在源代码中定义一个对象时,编译器就会为它分配一块存储器,此时你就可以使用“对象名”来操作该对象。但是对于程序运行时动态分配的某一

11、块存储器空间(对象) ,你就没法使用“对象名”了,而只能使用“引用” ,所以,“引用”就是指向特定类型的对象的指针。 好了,我们转入正题。 在 32 位嵌入式系统中,单字节对象是 1 字节对齐的;双字节对象是 2 字节对齐的;四字节对象是 4 字节对齐的;其它结构体或共同体对象是 8 字节对齐的。也就是说,当你定义一个单字节对象时,该对象的起始地址可以是任何整数;当你定义一个双字节对象时,该对象的起始地址必定是 2 的倍数的整数;当你定义一个四字节对象时,该对象的起始地址必定是 4 的倍数的整数;当你定义一个结构体或共同体对象时,该对象的起始地址必定是 8 的倍数的整数。以上说的对象包括“结构

12、体或共同体对象的成员对象” 。 字节对齐的故障只能出现在“引用”的使用过程中。当你使用“对象名”来操作对象时,根本不用担心字节对齐问题。 在 ADS 环境下,有 “ALIGN” 、 “_align(x)” 、 “_packed”关键字用于字节对齐处理。ALIGN 用于汇编语言,_align(x)用于 C 语言,_packed用于放弃字节对齐。 单字节对齐类型的引用可以操作任何对象,双字节对齐类型的引用可以操作双字节、四字节、八字节对齐的对象,。只有遵守这个规则,你的程序才可能是健壮的。 如果你确实想使用双字节对齐类型的引用来操作单字节对齐对象,那么你在定义该引用时必须使用_packed 关键字

13、! 好了,再多的东西我也说不清楚,给大家这么一个提醒已经足够了,希望大家引起注意。Accessing unaligned data from CDescriptionIt is sometimes necessary to access unaligned data in memory, in particular for embedded systems. The following describes how this can be doen from C code for ARM or Thumb code. Like other RISCs, ARM and Thumb process

14、ors are designed to efficiently access aligned data (i.e. words which lie on addresses that are multiples of 4, halfwords which lie on addresses that are multiples of 2). This is because memory controllers typically ignore A1:0 for word accesses and A0 for halfword accesses. Using a conventional C p

15、ointer (e.g. int *) to read a word, ARM compilers will use an LDR instruction in the generated code. This works as expected when the address is a multiple of 4 (i.e. on a word boundary). However, if the address is not a multiple of 4, then an LDR will return a rotated result rather than performing a true unaligned word load. For more details on how LDR works, see What does the ARM core read/write when using non-aligned addresses? and the ARM7TDMI datasheet. Generally this rotation is not what the programmer is expecting. All data accesses can be classified into the following categori

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 办公文档 > 其它办公文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号