【问题标题】:ARM unaligned memory access workaroundARM 未对齐内存访问解决方法
【发布时间】:2011-07-04 10:03:57
【问题描述】:

我必须将源代码移植到运行 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 &gt; /proc/cpu/alignment on 可使 Linux 内核修复问题,但应用程序的性能下降到无法再接受的程度。

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

我认为可以通过或多或少复杂的正则表达式/替换来修复许多有问题的代码行,但现在,在这样做了一段时间后,我发现这种方法也需要大量繁琐的工作。

你们对如何完成这项工作有什么建议吗?我认为 gcc 4.5 编译器插件会有点矫枉过正,但有什么比正则表达式更好的吗?你还能提出什么其他建议?不一定要修复所有问题实例,因为在少数情况下我仍然可以依赖内核。

【问题讨论】:

  • 我很想开玩笑说这应该转移到 TheDailyWTF.com。
  • 继续我们对现代语言学的研究,这里我们有一个嵌入式程序员常用的 C 方言样本。大致翻译成英文,上面的文字意思是“F *** YOU!”,尽管没有任何自然语言能真正表达出这里所传达的怨恨、对所有神圣事物的蔑视以及对读者人性的普遍漠视。

标签: c memory gcc arm


【解决方案1】:

__attribute__((__packed__)) 在某些情况下可能会有所帮助,但我真的认为应该尽快清理这段代码,因为您可能会花费更多时间来解决问题而不是修复它一劳永逸。

【讨论】:

  • 我认为这是一个可行的解决方法,但首先编写代码的人应该被解雇。顺便说一句,代码可能还需要-fno-strict-aliasing__attribute__((may_alias)) 在所有这些指针上,如果它是那么坏的话......
  • 它不会帮助 IEC_DWORD* 案例,因为这基本上是 typedef uint32_t,POD 类型。将指针强制转换为它的那一刻,它被推断为具有 4 字节对齐。当代码被破坏时,很想为​​目标编写一个模拟器,它确实可以工作:)
  • __attribute__((__packed__)) 已用于结构成员,但由于代码转换为 POD 类型而不是结构,因此这是无用的。但我会尝试用一个测试程序来看看我是否可以使用它。
  • 仅间接,通过结构:typedef struct { int value __attribute__((__packed__)); } unaligned_int; int foo() { unaligned_int *bar = (unligned_int *)3; return bar-&gt;value; }
  • @SimonRichter 将packed 放在结构元素本身对我不起作用。但是,将packed 放在typedef union(而不是结构)上会使我的编译器生成我想要的逐字节读取/写入:请参阅此要点:unaligned.h
【解决方案2】:

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

一种方法是查看被访问的底层结构。成员“H”和“L”可能是 32 位类型,可以像 64 位一样访问。

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

(诚然,这是一次无中生有的尝试,因为代码 sn-p 没有透露应用程序的细节,也没有透露底层结构的细节。)

【讨论】:

    【解决方案3】:

    哇,真是一团糟。摆弄编译器不会让你到任何地方。该代码在所有架构上都是非法的,但恰好适用于某些架构(例如 x86)。我会自己修复代码。

    遗憾的是,没有很好的方法可以做到这一点。但是,您可能会获得很长的搜索和替换列表,然后手动修复其余部分。我会从删除这些数据类型的声明开始,所以如果你编译你错过的任何代码,它就会出错。然后,用 "set_dword(pSP, " 搜索并替换诸如 "*(IEC_DWORD OS_SPTR *)pSP =" 之类的 sn-ps。制作一个内联函数 "set_dword " 做正确的事。继续使用尽可能多的易于替换的 sn-ps。仍然有大量需要手动修复。

    我能想到的唯一其他方法是编译器插件,正如你所建议的那样,并使整个编译单元中的每个指针都对齐 1。然后编译器将字节加载/存储所有内容。它可能最终会为你想要的代码做更多的事情。这可能不像听起来那么容易实现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-02
      • 2010-11-07
      • 2013-10-21
      • 2014-06-25
      • 2013-05-09
      • 1970-01-01
      • 2021-09-04
      相关资源
      最近更新 更多