ARM非对齐内存访问解决方法

10

我需要将源代码移植到运行Linux的ARM平台上。不幸的是,我遇到了未对齐内存访问的问题。源代码使用指针转换和访问非常频繁。

像下面这样的代码在整个代码库中广泛传播,就像一种病毒。我可以通过gcc命令行选项-Wcast-align找到问题出现的位置,但是有超过一千个实例需要处理。

u = (IEC_BOOL);
(((*(IEC_LINT*)pSP).H < b.H) 
   || (((*(IEC_LINT*)pSP).H == b.H) && ((*(IEC_LINT*)pSP).L < b.L) )) ? 1 : 0);
*(IEC_DWORD OS_SPTR *)pSP = 
    (IEC_DWORD)(*(IEC_DWORD OS_SPTR *)pSP >> u);  
*(IEC_DWORD OS_SPTR *)pSP = 
    (IEC_DWORD)(*(IEC_DWORD OS_SPTR *)pSP << -u);  
u = (IEC_BYTE)((*(IEC_DINT*)pSP != b) ? 1 : 0);  
*(IEC_DWORD*)pSP = (IEC_DWORD)(*(IEC_DWORD*)pSP & w);  
(*(IEC_ULINT*)pSP).H += u.H;   
(((*(IEC_ULINT OS_SPTR *)pSP).H == b.H) 
    && ((*(IEC_ULINT OS_SPTR *)pSP).L > b.L))) ? 1 : 0);
u = (IEC_BYTE)((*(IEC_REAL*)pSP >= b) ? 1 : 0);

使用 echo 2 > /proc/cpu/alignment 命令可以让Linux内核解决问题,但应用程序的性能会降低到无法接受的程度。

我在网上搜索了像GCC(v4.4.1)编译器中的__unaligned__packed关键字之类的东西,但目前还没有找到。

我认为一些有问题的代码行可以通过更多或更少复杂的正则表达式/替换来修复,但是现在,经过一段时间的努力后,我发现这种方法也需要大量枯燥无味的工作量。

你们有任何建议如何完成这项工作吗?我觉得gcc 4.5编译器插件可能有点过头了,但除了正则表达式还有更好的建议吗?并不是所有的问题实例都必须被修复,因为我仍然可以依靠内核处理一些更罕见的情况。


11
我想开个玩笑,说这应该被转移到TheDailyWTF.com上。 - Crashworks
3
在继续研究现代语言学时,我们有一个嵌入式程序员常用的C方言样例。大致翻译成英文是“F*** YOU!”,尽管任何自然语言都无法真正传达此处所传达的恶意、对一切神圣的蔑视和对读者人性的普遍漠视。 - Dmitri
3个回答

7

__attribute__((__packed__))这个属性可以在某些情况下有所帮助,但我认为这段代码应该尽早进行清理,因为你可能会花费更多的时间来解决问题,而一次性彻底修复它所需的时间可能并不长。


2
我认为这是一个可行的解决方法,但是第一次编写代码的人应该被解雇。顺便说一下,如果代码真的有那么多问题,那么所有这些指针也可能需要-fno-strict-aliasing__attribute__((may_alias))... - R.. GitHub STOP HELPING ICE
这不会帮助IEC_DWORD* case,因为基本上是typedef uint32_t,POD类型。一旦将指针强制转换为此类型,就会推断其具有4字节对齐。当代码出现这种故障时,很容易编写一个针对目标的模拟器来解决问题 :) - John Ripley
__attribute__((__packed__)) 已经用于结构体成员,但由于代码将其转换为 POD 类型而不是结构体,因此这是无用的。但我会尝试使用测试程序来看看是否可以使用它。 - trenki
2
仅通过结构体间接实现: typedef struct { int value __attribute__((__packed__)); } unaligned_int; int foo() { unaligned_int *bar = (unligned_int *)3; return bar->value; } - Simon Richter
@SimonRichter 将 packed 放在结构体元素上对我不起作用。但是,将 packed 放在 typedef union 上(而不是结构体)使我的编译器生成了我期望的按字节读/写信息:请参阅此代码片段:unaligned.h - Jonathon Reinhart

2
哇,这真是一团乱麻。把编译器搞得乱七八糟也不会有所进展。这段代码在所有架构上都是非法的,但只是偶尔可以工作(例如x86)。我建议直接修复代码本身。
可惜没有美观的方式。但是,你可以用一长串查找和替换来解决大部分问题,然后手动处理剩余部分。我建议首先删除那些数据类型的定义,这样如果你错过了编译任何代码,它会报错。然后,使用类似“*(IEC_DWORD OS_SPTR *)pSP =”的代码片段进行查找和替换,替换成"set_dword(pSP, "。创建一个名为“set_dword”的内联函数来完成正确的操作。继续进行尽可能多的易于替换的代码片段。仍然需要手动修复大量内容。
我能想到的唯一其他方法可能是编写一个编译器插件,如你所建议的,使编译单元中的每个指针对齐为1。编译器将使用字节加载/存储一切。它可能会为你意外的代码执行此操作。这可能比听起来更难实现。

0

我们可以假设问题源于ARM是32位机器,而Linux运行在64位模式下,或者代码可能假定它正在16位机器上运行。

一种方法是查看所访问的底层结构。成员“H”和“L”可能是32位类型,但被访问时似乎是64位。

尝试修改L和H的类型以使代码表现更好。

(诚然,这只是一个猜测,因为代码片段没有透露应用程序的细节,也没有透露底层结构的细节。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接