簡體   English   中英

為什么在啟用-O2或更高版本時此代碼會中斷?

[英]Why does this code break when -O2 or higher is enabled?

我試圖在8位PIC單片機中安裝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 使用0x454e028b, 0x8c6fa548 ,我得到0x454e028b, 0x8c6fa548 ,這是正確的結果。

步調試

實施指南包括所有中間密鑰安排其他值,所以我逐行逐步完成代碼,將結果與指南進行比較。

為“×”的預期結果是: 03020100131d0309bbd80d530d334df3 我開始步驟調試,但是當達到第4個結果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 %8lx\n", (unsigned long) out[0], 0x454e028bLU);
  printf("%8lx %8lx\n", (unsigned long) out[1], 0x8c6fa548LU);
}

產量

454e028b 454e028b
8c6fa548 8c6fa548

意外的輸出

0x8FA3FED7
0x53D8CEA8

我沒有在代碼中看到任何未定義行為的跡象,除非它在您未顯示的設置/調用點的某些方面。 因此,根據優化級別,行為應該是不可能的。 通常情況下,我不會快速責怪編譯器錯誤這樣的東西,而是FOSS編譯器的嵌入式東西,特別是在未編譯為16位int的編譯器中將int重新定義為16位的forks,以及特別是專有的問題,他們的代碼非常糟糕,甚至不想讓你看到它,很可能是一個編譯器錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM