Почему этот код прерывается при включении -O2 или выше?

#c #clang #pic #microchip #xc8

#c #лязг #рис #микросхема #xc8

Вопрос:

Я попытался встроить реализацию SPECK от NSA в 8-разрядный микроконтроллер PIC. Бесплатная версия их компилятора (основанная на 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» следующие: 03020100 , 131d0309 , bbd80d53 , 0d334df3 0d334df3 . Я запускаю пошаговую отладку, но при достижении 4-го результата 0d334df0 вместо этого появляется окно отладчика. К следующему раунду ожидаемое 7fa43565 значение равно 7FA43578 и с каждой итерацией становится только хуже.

Это происходит только тогда, когда включен параметр -O2 или выше. Без оптимизации или с -O1 код работает так, как ожидалось.

Комментарии:

1. TL; DR: неопределенное поведение в вашем коде. начните с защиты аргументов вашего макрокоманды круглыми скобками. при использовании i 2 не уверен, что он делает то, что вы хотите.

2. @AnttiHaapala «аргумент макроса в скобках» был бы лучшей формой, но я думаю, что OP, к счастью, избежал этой ловушки здесь. Однако отсутствие [MCVE] OP делает это сложнее, чем должно быть.

3. вам просто нужно правильно прочитать комментарии, чтобы понять, в чем проблема: не объявляйте свои данные как указатели на char . Компилятор предположит, что он может поместить их в невыровненные ячейки памяти. И, пожалуйста, будьте вежливы. Здесь нет элитарности.

4. Похоже, здесь это не требуется "speck.h" . Если в нем есть что-то интересное , дайте нам это увидеть, иначе лучше это удалить.

5. UB может возникать из другого не опубликованного кода. Мы не видим, как OP подал encrypt_block(out, in, key); и не сообщил о своих выходных данных. Будем надеяться, что отсутствующий код исправен, но он остается неизвестным.

Ответ №1:

Это была ошибка в компиляторе.

Я разместил вопрос на форуме производителя. Другие пользователи действительно воспроизвели проблему, которая возникает при компиляции для определенных частей. Другие части не затрагиваются.

В качестве обходного пути я преобразовал макросы в реальные функции и разделил операцию на две строки:

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

Это дает правильный результат.

Ответ №2:

Публикуем компилируемый тестовый код в качестве ссылки.

 #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
  

Ответ №3:

Я не вижу никаких признаков неопределенного поведения в вашем коде, если только это не относится к какому-либо аспекту настройки / точки вызова, который вы не показали. Таким образом, поведение не должно отличаться в зависимости от уровня оптимизации. Обычно я бы не спешил обвинять ошибки компилятора в чем-то подобном, но форки компиляторов FOSS для встроенных материалов, и особенно форки, которые переопределяют int 16-разрядный код в компиляторе, который не был разработан для работы с 16-разрядными int , и особенно проприетарные форки, где их код настолько плох, что они даже не хотят, чтобы вы его видели, ошибка компилятора очень, очень вероятна.