#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
, и особенно проприетарные форки, где их код настолько плох, что они даже не хотят, чтобы вы его видели, ошибка компилятора очень, очень вероятна.