6

Написал библиотеку с нативными функциями под андроид. Собственно, вот эти функции: Описание протокола websockets на русском языке. На компьютере эти функции работают отлично. Наконец дошли руки до запуска на андроиде и в EncodeData() программа вылетает с ошибкой.

Вылетает на этой строке, на первой итерации:

*(unsigned int*)l ^= mask;

Если убрать маску (mask = 0), то функция работает без ошибок.

В чем проблема в этом коде? Чем заменить, чтобы работало?

mikelsv
  • 3,046
  • 1
  • 20
  • 48
  • 2
    Нужно больше кода. Что за "l", там может быть не выравненный на "4" адрес? – fghj May 19 '15 at 18:07
  • Похоже на то, что если маска статически равна нулю, то всю строчку выбрасывает оптимизатор как ненужную. А что у вас в переменной l? – VladD May 19 '15 at 18:07
  • Жуть какая-то. l - это, похоже указатель. Рискну предположить, что там доступ по невыровненному адресу. Накладывайте маску побайтово. – lsillarionov May 19 '15 at 18:15
  • Действительно, была ошибка в невыровненном адресе. В чем разница с архитектурой x86? – mikelsv May 19 '15 at 18:23
  • Некоторые инструкции на некоторых архитектурах хотят работать с выровненными данными. x86 - не исключение. – Владимир Мартьянов May 19 '15 at 19:44
  • @ВладимирМартьянов, x86 не требует aligned access в подавляющем количестве случаев, встретить обратное довольно сложно. – ixSci May 20 '15 at 05:37
  • Я говорил про некоторые. SSE тому пример. – Владимир Мартьянов May 20 '15 at 07:02
  • Кстати, а с порядком байт у Вас все нормально? Не нужно длины и проч. int крутить в/из сетевого при обмене (все же Х86 и некорые ARM little-endian, а другие (и сама сеть) big-endian) – avp May 20 '15 at 14:00

2 Answers2

8

Я посмотрел Ваш код по ссылке.

Действительно, это проблема с выравниванием. Для некоторых архитектур (в частности ARM) необходимо, чтобы обращения к памяти имели естественное выравнивание, т.е. адрес памяти должен быть кратен размеру данных (2 для short, 4 для int, 8 для uint64_t и double).

У Вас это происходит из-за величины lpos (даже в предположении, что ret.data выровнена на границу слова (или двойного слова, как обычнно возвращает malloc())).

Похоже, что проще всего будет перед основным циклом

    for(l; l < ft; l += 4)
        *(unsigned int*)l ^= mask;

добавить несколько строчек

   for(; l < t && l & 0x3; l++) { // цикл выполнится 0, 1, 2 или 3 раза
      *l ^= *m;
      mask = (mask << 8) | ((mask >> 24) & 0xff); // циклический сдвиг влево на 1 байт
   }

которые (по идее, на компе не проверял) побайтово меняют данные, пока не достигнуто выравниваание подходящее для int и крутят маску так, чтобы в следующем цикле для int она осталась правильной.

avp
  • 46,098
  • 6
  • 48
  • 116
4

Это называется "неопределенное поведение", также известное как "UB".

Если l имеет тип unsigned char*, то каст (unsigned int*) l работает как reinterpret_cast<unsigned int*>(l).

В требованиях к reinterpret_cast написано что для каста T1* в T2* выравнивание T1 должно быть не меньше выравнивания T2, иначе значение результата каста не определено.

Сам по себе каст безобиден, но вот разыменование указателя имеющего такое неопределенное значение приводит к UB.

Abyx
  • 31,143