我正在尝试将最初为x86架构编写的代码移植到arm.
最初它是为msvc/win32开发的,代码中有很多错误,所以可能有UB.
当使用gcc编译时,代码正在使用x86,但是它有不同的行为.它似乎丢失了数组中的一些数据,武器开关不起作用.它影响了服务器和客户端库.
应用程序是单线程的,因此不是同步问题.
默认情况下,char在arm上是无符号的,所以我添加-fsigned-char
到编译器标志,但它没有解决问题.arm和x86 c代码之间还有什么区别?
我尝试使用gcc和clang在arm上编译代码并且没有区别,因此它不是编译器错误.
PS我使用gcc-4.9(而不是4.8)编译了x86的代码,并得到了相同的行为.之后,我结合了两个编译器,发现问题出在net_encode.c中.
到那时,塞巴斯蒂安骑士发现了这一点
iValue /= pField->multiplier;
iValue *= pField->multiplier;
当iValue为整数且pField-> multiplier == 1.0f时,有时会更改整数值.
可以通过在乘法之前添加检查pField-> multiplier!= 1.0f来修复它.
几乎不可能在没有看到你的代码的情况下说,但会尽力回答.
x86和ARM之间的一个巨大差异是大多数x86指令在内部都是原子的方式,而ARM没有这样的东西 - 你必须明确地说"只执行这组指令".因此,如果您有多个线程更新的数据,这可能会打击您.
个别指令的行为方式也存在差异.在不知道你的代码做什么的情况下,这很难说是否会影响你的代码,但有一点可以解决的是"未对齐访问",这在x86中是完全有效的(尽管不是最优的),但是无效(在大多数情况下) ARM处理器上的模型).因此指针必须与它访问的项目的大小对齐.
当然,编译器中的代码生成也不同,它们可能会根据输入代码做出不同的决策,从而最终得到的代码在各种方式下表现不同.一次不止一次打击我的是"参数函数调用的执行顺序":
func(func1(), func2());
Run Code Online (Sandbox Code Playgroud)
注意func1()
或者 func2()
可以先执行.如果您依赖此类订购,则需要:
t1 = func1();
t2 = func2();
func(t1, t2);
Run Code Online (Sandbox Code Playgroud)
提示:
如果您还没有,请尽可能多地启用警告(-Wall
至少).并修复任何警告
检查#pragma pack
或类似的"包数据结构",并从同样蒙上char *
到int *
等,因为这些可能导致对齐访问的问题.
编辑:当然,不同的编译器会有不同的错误,这些错误可能会或可能不会在特定的编译器,处理器和代码组合中产生影响.虽然铿锵不太可能也给出了同样的问题.
我并不是这方面的专家,但我可以指出一些一般性的事情。
首先,根据 ARM 的版本和设置,某些基元类型的大小、符号和字节序可能与 x86(尤其是 x86_64)的不同。在这方面,编写良好的、可移植的代码不应该对这些方面中的任何一个做出假设。如果您需要特定的大小和符号,可以使用<cstdint>
/ <stdint.h>
header 中的类型。字节序也是您应该注意的事情。
其次,正如 Mats 所提到的,ARM 和 x86 之间最臭名昭著且改变行为的差异之一是内存模型的严格性。简而言之,x86 往往非常保守(因此本质上是“安全”的,或者至少不足为奇),而 ARM 则要弱得多(因此可能更快)。这对于并发代码尤其重要。默认情况下,x86 上的许多基本操作都是原子的,并且有许多隐式的完整内存栅栏可以保证总体一致性。默认情况下,这些保护机制在 ARM 上并不存在,这可能会导致许多奇怪的行为。
例如,许多简单的多线程代码会错误地使用volatile
变量作为在线程之间共享信息或信号的方式。在许多情况下,由于 x86 保守的内存模型,这在 x86 上可以正常工作。但在 ARM 架构上,该代码可能已损坏。
最后,一般来说,不同的架构在某些操作上的行为会略有不同。所有这些差异通常属于标准的“未定义行为”或“实现定义”类别。这些是最难发现的错误,因为未定义的行为通常意味着在一种体系结构(和编译器)上会发生一件事,这可能没问题,但在另一种体系结构上,会发生其他事情,这可能不太好。
有一些工具可以帮助您解决所有这些问题。主要工具是所谓的“消毒剂”。这些工具会对您的运行时代码进行大量检查,以检查与所有这些相关的各种问题(即,您可以使用工作的 x86 代码运行它们,它们会向您指出在 ARM 上可能表现不同的“危险”事物)或其他地方)。您可以尝试UBSan(未定义行为清理程序,用于 UB 操作)、TSan(线程清理程序,用于数据争用和危险的跨线程内存访问)和ASan(地址清理程序,用于一般内存调试),它们都可以在 Clang 下使用和海湾合作委员会。我不知道这些工具是否有任何可以为 ARM 提供帮助的特殊功能,或者它们是否针对 ARM,但至少,修复它们在 x86 上指出的任何问题在将其移植到 ARM 时应该会有很大帮助。