C-启用-O2或更高时,为什么此代码断开



我试图在8位PIC MicroController中安装NSA Speck的实现。他们的编译器的免费版本(基于Clang)无法启用优化,因此我用完了记忆。我尝试了启用-O2,-O3和-OS的"试用"版本(针对大小进行优化)。使用-os,它设法将我的代码适合在2K程序内存空间中。

这是代码:

#include <stdint.h>
#include <string.h>
#define ROR(x, r) ((x >> r) | (x << (32 - r)))
#define ROL(x, r) ((x << r) | (x >> (32 - r)))
#define R(x, y, k) (x = ROR(x, 8), x += y, x ^= k, y = ROL(y, 3), y ^= x)
#define ROUNDS 27
void encrypt_block(uint32_t ct[2],
        uint32_t const pt[2],
        uint32_t const K[4]) {
    uint32_t x = pt[0], y = pt[1];
    uint32_t a = K[0], b = K[1], c = K[2], d = K[3];
    R(y, x, a);
    for (int i = 0; i < ROUNDS - 3; i += 3) {
        R(b, a, i);
        R(y, x, a);
        R(c, a, i + 1);
        R(y, x, a);
        R(d, a, i + 2);
        R(y, x, a);
    }
    R(b, a, ROUNDS - 3);
    R(y, x, a);
    R(c, a, ROUNDS - 2);
    R(y, x, a);
    ct[0] = x;
    ct[1] = y;
}

不幸的是,当按线进行调试时,将其与实施指南中的测试向量进行比较,第32页" 15 Speck64/128测试向量",结果与预期结果不同。

这是一种调用此功能的方法:

uint32_t out[2];
uint32_t in[] = { 0x7475432d, 0x3b726574 };
uint32_t key[] = { 0x3020100, 0xb0a0908, 0x13121110, 0x1b1a1918 };
encrypt_block(out, in, key);
assert(out[0] == 0x454e028b);
assert(out[1] == 0x8c6fa548);
根据指南,

" OUT"的预期值应为0x454e028b, 0x8c6fa548。我使用-O2的结果是0x8FA3FED7 0x53D8CEA8。使用-O1,我得到0x454e028b, 0x8c6fa548,这是正确的结果。

步骤调试

《植入指南》包括所有中间密钥计划其他值,因此我逐行逐步介绍了代码,将结果与指南进行了比较。

" X"的预期结果是:03020100131d0309bbd80d530d334df3。我开始进行步骤调试,但是当达到第四结果0d334df3时,调试器窗口将显示0d334df0。到下一轮,预期的7fa43565值为7FA43578,并且每次迭代都会变得更糟。

这仅在启用-O2或更大时才发生。没有优化或使用-O1,代码可以按预期工作。

这是编译器中的错误。

我在制造商论坛上发布了问题。其他人确实已经复制了这个问题,这是在某些部分编译时发生的。其他部分不受影响。

作为解决方法,我将宏更改为真实功能,然后将操作分为两行:

uint32_t ROL(uint32_t x, uint8_t r) {
    uint32_t intermedio;
    intermedio = x << r;
    intermedio |= x >> (32 - r);
    return intermedio;
}

这给出了正确的结果。

将可编译的测试代码作为参考。

#include <stdint.h>
#include <string.h>
//#include "speck.h"
#define ROR(x, r) ((x >> r) | (x << (32 - r)))
#define ROL(x, r) ((x << r) | (x >> (32 - r)))
#define R(x, y, k) (x = ROR(x, 8), x += y, x ^= k, y = ROL(y, 3), y ^= x)
#define ROUNDS 27
void encrypt_block(uint32_t ct[2], uint32_t const pt[2], uint32_t const K[4]) {
  uint32_t x = pt[0], y = pt[1];
  uint32_t a = K[0], b = K[1], c = K[2], d = K[3];
  R(y, x, a);
  // for (int i = 0; i < ROUNDS - 3; i += 3) {
  for (uint32_t i = 0; i < ROUNDS - 3; i += 3) {
    R(b, a, i);
    R(y, x, a);
    R(c, a, i + 1);
    R(y, x, a);
    R(d, a, i + 2);
    R(y, x, a);
  }
  R(b, a, ROUNDS - 3);
  R(y, x, a);
  R(c, a, ROUNDS - 2);
  R(y, x, a);
  ct[0] = x;
  ct[1] = y;
}
int main(void) {
  uint32_t out[2];
  uint32_t in[] = {0x7475432d, 0x3b726574};
  uint32_t key[] = {0x03020100, 0x0b0a0908, 0x13121110, 0x1b1a1918};
  encrypt_block(out, in, key);
  printf("%8lx %8lxn", (unsigned long) out[0], 0x454e028bLU);
  printf("%8lx %8lxn", (unsigned long) out[1], 0x8c6fa548LU);
}

输出

454e028b 454e028b
8c6fa548 8c6fa548

意外输出

0x8FA3FED7
0x53D8CEA8

我看不到代码中未定义的行为的任何指示,除非在您尚未显示的设置/呼叫点的某些方面。因此,根据优化水平,行为不可能有所不同。通常,我不会很快地将编译器错误归咎于嵌入式内容的FOSS编译器,尤其是在编译器中重新定义int至16位的叉子编译器的叉子错误,而不是为目标16位int而设计的尤其是专有叉子,它们的代码非常糟糕,甚至不想让您看到它,编译器错误很可能。

相关内容

  • 没有找到相关文章