arm和x86架构中c / c ++语言的差异

时间:2015-04-13 07:25:50

标签: c++ c x86 arm

我正在尝试将最初为x86架构编写的代码移植到arm。

https://github.com/SDLash3D

最初它是为msvc / win32开发的,代码中有很多错误,所以可能有UB。

代码在使用gcc编译时正在使用x86,但是在arm上它具有不同的行为。它似乎丢失了数组中的一些数据,武器开关不起作用。 它影响了服务器和客户端库。

应用程序是单线程的,因此不是同步问题。

默认情况下,char在arm上是无符号的,所以我将-fsigned-char添加到编译器标志,但它没有解决问题。 arm和x86 c代码之间还有什么区别?

我尝试使用gcc和clang在arm上编译代码并且没有区别,因此它不是编译器错误。

P.S 我使用gcc-4.9(而不是4.8)编译了x86的代码并得到了相同的行为。 之后,我结合了两个编译器,发现问题出在net_encode.c。

当时,sebastien chevalier发现了

iValue /= pField->multiplier; iValue *= pField->multiplier;

当iValue为整数且pField->乘数== 1.0f时,有时会更改整数值。

可以通过在乘法之前添加检查pField->乘数!= 1.0f来修复。

2 个答案:

答案 0 :(得分:5)

如果没有看到你的代码几乎不可能说,但会尽力回答。

x86和ARM之间的一个巨大差异是大多数x86指令在内部都是原子的方式,而ARM没有这样的东西 - 你必须明确地说“只执行这组指令”。因此,如果您有多个线程更新的数据,这可能会打击您。

个别指令的行为方式也存在差异。在不知道你的代码做什么的情况下,这很难说是否会影响你的代码,但有一点可以解决的是“未对齐访问”,这在x86中是完全有效的(尽管不是最优的),但是无效(在大多数情况下) ARM处理器上的模型)。所以指针必须与它访问的项目的大小对齐。

当然,编译器中的代码生成也不同,可能会根据输入代码做出不同的决策,从而最终得到的代码在各种方式下表现不同。一次不止一次打击我的是“参数函数调用的执行顺序”:

func(func1(), func2()); 

请注意,func1() func2()可能会先执行。如果您依赖此类订购,则需要:

t1 = func1();
t2 = func2();
func(t1, t2); 

提示:

如果您还没有,请尽可能多地启用警告(至少-Wall)。并修正任何警告

检查#pragma pack或类似的“数据包数据结构”,并类似地从char *转换为int *等,因为这些可能导致未对齐的访问问题。

编辑:当然,不同的编译器会有不同的错误,这些错误可能会或可能不会对特定的编译器,处理器和代码组合产生影响。虽然铿锵声不太可能也会产生相同的问题。

答案 1 :(得分:4)

我不是这方面的专家,但我可以指出一些一般性的事情。

首先,根据ARM的版本和设置,某些基本类型的大小,符号和字节顺序可能与x86(特别是x86_64)的不同。在这方面,编写良好的可移植代码不应该对任何这些方面做出假设。如果您需要特定尺寸和签名,请使用<cstdint> / <stdint.h>标题中的类型。而endianness也是你应该照顾的东西。

其次,正如Mats所提到的,ARM和x86之间最臭名昭着和改变行为的差异之一就是内存模型的严格性。简而言之,x86往往非常保守(因此,本质上“安全”或至少不足为奇),而ARM则更弱(因此可能更快)。这对于并发代码尤其重要。默认情况下,x86上的许多基本操作都是原子操作,并且有许多隐含的完整内存栅栏可以保证一般的一致性。那些保护机制在ARM上默认不存在,这可能导致许多奇怪的行为。

例如,许多天真的多线程代码会错误地使用volatile变量作为在线程之间共享信息或信号的方法。在许多情况下,由于其保守的内存模型,这将在x86上正常工作。但是在ARM体系结构中,该代码很可能会被破坏。

最后,总的来说,不同的架构在某些操作上的行为略有不同。所有这些差异通常属于标准的“未定义行为”或“实现定义”类别。这些是最难找到的错误,因为未定义的行为通常意味着在一个架构(和编译器)上会发生一件事情,这可能是正常的,但在另一个架构上,会发生其他事情,这可能没关系。

有一些工具可以帮助您解决所有这些问题。主要工具是所谓的“消毒剂”。这些工具可以检测运行时代码,检查与所有这些相关的各种问题(例如,您可以使用工作的x86代码运行它们,并且它们指向可能在ARM上表现不同的“危险”事物或其他地方)。您可以尝试UBSan(未定义的行为清理程序,用于UB操作),TSan(线程清理程序,用于数据争用和危险的跨线程内存访问)和ASan(地址清理程序,用于内存)一般调试),这些都在Clang和GCC下都可用。我不知道这些工具是否有任何可以帮助ARM的特殊工具,或者它们是否甚至针对ARM,但至少,解决他们在x86上指出的任何问题都应该在将其移植到ARM时有很大帮助。