Kir*_*rov 4 c gcc c99 compiler-options
长话短说——我使用 Ted Krovetz 的实现来计算UMAC和UMAC AE加密 ( http://www.fastcrypto.org/ )。
当我用 编译我的代码(和/或中的测试umac.c)时-std=c99,计算出的结果UMAC与预期完全不同(并且是错误的)。当我删除这个选项时,一切都像魅力一样。
有什么想法可能导致这种情况吗?我可以做什么来检查发生了什么以及什么产生了不同的结果?
$ gcc --version
gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
$ uname -a
xxx 3.13.0-43-generic #72-Ubuntu SMP .. x86_64 x86_64 x86_64 GNU/Linux
Run Code Online (Sandbox Code Playgroud)
我不使用任何其他选项 - 只是使用和不使用-std=c99.
再说几句话:
我会尝试联系 Ted Krovetz 并向他询问这个问题(可能是一些错误或其他问题),但这不是重点。这个问题有点笼统,这个具体问题可以看作是一个例子。
我跑了valgrind——没什么特别的。添加-Wall并且-Wextra-什么也没有了。听起来像UB,但valgrind没有抱怨任何事情。
这种情况非常有趣,花了我很多天和头痛才明白,问题不在我的代码中(我使用这个实现来实现复杂的协议),而是在算法中,尤其是在这个选项中。所以我决定征求意见。
在 C 和 C++ 中都有效的代码在用每种语言编译时会产生不同的行为吗?根本不相关,因为我们在这里谈论的是同一种语言。
这种没有“-std=c99”的巨大 fprintf 速度差异很接近,但还不够。
编辑
这是我的测试结果和我所做的(源/标题刚刚下载,我没有更改任何内容):
$ ll
total 176K
-rw-r----- 1 kk kk 63K Jan 20 11:00 rijndael-alg-fst.c
-rw-r----- 1 kk kk 2.0K Jan 20 11:00 rijndael-alg-fst.h
-rw-r----- 1 kk kk 3.4K Jan 20 11:00 umac_ae.h
-rw-r----- 1 kk kk 76K Jan 20 11:00 umac.c
-rw-r----- 1 kk kk 4.2K Jan 20 11:00 umac.h
$ gcc -c *.c
$ gcc *.o
$ ./a.out
AES Test :::
Digest is : 3AD78E726C1EC02B7EBFE92B23D9EC34
Digest should be: 3AD78E726C1EC02B7EBFE92B23D9EC34
UMAC Test :::
Msg Should be Is
--- --------- --
'a' * 0 : 4D61E4F5AAB959C8 4D61E4F5AAB959C8
'a' * 3 : 67C1700CA30B532D 67C1700CA30B532D
'a' * 1024 : 05CB9405EC38D9F0 05CB9405EC38D9F0
'a' * 32768 : 048C543CB72443A4 048C543CB72443A4
Verifying consistancy of single- and multiple-call interfaces.
Done.
Authenticating 44 byte messages: 6.45 cpb.
Authenticating 64 byte messages: 4.18 cpb.
Authenticating 256 byte messages: 1.63 cpb.
Authenticating 512 byte messages: 1.20 cpb.
Authenticating 552 byte messages: 1.22 cpb.
Authenticating 1024 byte messages: 1.00 cpb.
Authenticating 1500 byte messages: 1.04 cpb.
Authenticating 8192 byte messages: 0.90 cpb.
Authenticating 262144 byte messages: 0.89 cpb.
UMAC-AE Tests :::
0 bytes ('abc' * 0):
Encrypt/decrypt match, tags match
Should be: 0000000000000000
Is : E8D1DAC3EA21E56D
3 bytes ('abc' * 1):
Encrypt/decrypt match, tags match
Should be: 0000000000000000
Is : 6BEDBA31E074E2A4
48 bytes ('abc' * 16):
Encrypt/decrypt match, tags match
Should be: 0000000000000000
Is : A3F6069B913969DA
300 bytes ('abc' * 100):
Encrypt/decrypt match, tags match
Should be: 0000000000000000
Is : C5B7F3822179FC36
3000000 bytes ('abc' * 1000000):
Encrypt/decrypt match, tags match
Should be: 0000000000000000
Is : EE7F50FDDA60AA04
16 bytes, 38.12 cpb
32 bytes, 25.04 cpb
64 bytes, 19.39 cpb
128 bytes, 16.41 cpb
256 bytes, 14.79 cpb
512 bytes, 13.96 cpb
1024 bytes, 13.79 cpb
2048 bytes, 13.46 cpb
4096 bytes, 13.47 cpb
$ ll
total 176K
-rw-r----- 1 kk kk 63K Jan 20 11:00 rijndael-alg-fst.c
-rw-r----- 1 kk kk 2.0K Jan 20 11:00 rijndael-alg-fst.h
-rw-r----- 1 kk kk 3.4K Jan 20 11:00 umac_ae.h
-rw-r----- 1 kk kk 76K Jan 20 11:00 umac.c
-rw-r----- 1 kk kk 4.2K Jan 20 11:00 umac.h
$ gcc -std=c99 -c *.c
$ gcc -std=c99 *.o
$ ./a.out
AES Test :::
Digest is : 3AD78E726C1EC02B7EBFE92B23D9EC34
Digest should be: 3AD78E726C1EC02B7EBFE92B23D9EC34
UMAC Test :::
Msg Should be Is
--- --------- --
'a' * 0 : 4D61E4F5AAB959C8 9492DE86794C9F2B
'a' * 3 : 67C1700CA30B532D CF9505F52928360E
'a' * 1024 : 05CB9405EC38D9F0 9C48C0D4EFAFAA37
'a' * 32768 : 048C543CB72443A4 7F63C29BB54BB141
Verifying consistancy of single- and multiple-call interfaces.
Done.
Authenticating 44 byte messages: 7.91 cpb.
Authenticating 64 byte messages: 5.20 cpb.
Authenticating 256 byte messages: 3.03 cpb.
Authenticating 512 byte messages: 2.60 cpb.
Authenticating 552 byte messages: 2.71 cpb.
Authenticating 1024 byte messages: 2.41 cpb.
Authenticating 1500 byte messages: 2.43 cpb.
Authenticating 8192 byte messages: 2.27 cpb.
Authenticating 262144 byte messages: 2.23 cpb.
UMAC-AE Tests :::
0 bytes ('abc' * 0):
Encrypt/decrypt match, tags match
Should be: 0000000000000000
Is : 899C50FD244BBA83
3 bytes ('abc' * 1):
Encrypt/decrypt match, tags match
Should be: 0000000000000000
Is : 892D14F581A3A4DD
48 bytes ('abc' * 16):
Encrypt/decrypt match, tags match
Should be: 0000000000000000
Is : 621AB4A63383F3C5
300 bytes ('abc' * 100):
Encrypt/decrypt match, tags match
Should be: 0000000000000000
Is : 324BEF6489F57787
3000000 bytes ('abc' * 1000000):
Encrypt/decrypt match, tags match
Should be: 0000000000000000
Is : 1A25FE3714C9345A
16 bytes, 40.80 cpb
32 bytes, 25.87 cpb
64 bytes, 20.50 cpb
128 bytes, 17.72 cpb
256 bytes, 15.93 cpb
512 bytes, 15.33 cpb
1024 bytes, 14.88 cpb
2048 bytes, 14.71 cpb
4096 bytes, 14.48 cpb
Run Code Online (Sandbox Code Playgroud)
我刚刚在另一台机器上测试过,和我的一样。
好吧,我想通了。仍然不确定如何将其修复为完美/便携,但我会继续挖掘。
长话短说 - 它似乎是特定于平台的,这就是为什么大多数人都没有这个问题。
问题在于确定字节顺序。
细节:
比较汇编输出后,发现了一些显着的差异,这(几乎自动)排除了一些大常量解释和诸如此类的小事情的问题。
然后我尝试了更高级别的预处理器输出。
最后,一切都导致了这段代码umac.c:
/* Message "words" are read from memory in an endian-specific manner. */
/* For this implementation to behave correctly, __LITTLE_ENDIAN__ must */
/* be set true if the host computer is little-endian. */
#ifndef __LITTLE_ENDIAN__
#if __i386__ || __alpha__ || _M_IX86 || __LITTLE_ENDIAN
#define __LITTLE_ENDIAN__ 1
#else
#define __LITTLE_ENDIAN__ 0
#endif
#endif
Run Code Online (Sandbox Code Playgroud)
在我的平台上,__i386__、__alpha__和_M_IX86未定义。关键在__LITTLE_ENDIAN.
编译时:
-std=c99:__LITTLE_ENDIAN未定义= > #define __LITTLE_ENDIAN__ 0。 -std=c99:__LITTLE_ENDIAN 被定义 => #define __LITTLE_ENDIAN__ 1。 硬编码#define __LITTLE_ENDIAN__ 1,无论有没有硬编码,一切都开始完美工作-std=c99。
结论:__LITTLE_ENDIAN是一个gcc特定的宏,这里用于确定字节序;看来,这-std=c99会影响这个宏(如果使用该选项则未定义),这会导致不同(错误)的结果。
编辑
我当前的(“临时”)解决方案是更新有问题的预处理器 if 语句。我知道这远不是解决这个问题的最佳方法,但检测字节顺序似乎并不那么容易,而且远非微不足道。
运行时检查似乎更可靠,但这会导致代码发生更多变化,这是我想避免的。看起来,最“无害”的“解决方案”就是更新和“修复”当前的解决方案。
因此,由于我(目前)只需要它与 GCC 一起使用,因此我进行了以下修改:
#ifndef __LITTLE_ENDIAN__
#if __GNUC__
#include <endian.h>
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define __LITTLE_ENDIAN__ 1
#elif __BYTE_ORDER == __BIG_ENDIAN
#define __LITTLE_ENDIAN__ 0
#else
#error "Cannot determine endianness! Please update this macro!"
#endif
#elif __i386__ || __alpha__ || _M_IX86
#define __LITTLE_ENDIAN__ 1
#else
#warning "Endianness cannot be determined for this platform; using big endian by default! Please be aware and update this macro!"
#define __LITTLE_ENDIAN__ 0
#endif
#endif
Run Code Online (Sandbox Code Playgroud)