Ошибка шины Solaris не возникает после копирования невыровненных данных в новое место

Итак, вкратце: моя программа получает буфер необработанных байтов (u_char), который представляет собой сетевой пакет. Я пытаюсь разобрать информацию в этом пакете, и делаю это, используя определенные системой структуры заголовков (ether_header, ip, ip6, tcphdr, udphdr). Я реализовал это как в Linux, так и в AIX, и это сработало, но по какой-то причине я продолжаю получать ошибку шины, когда делаю это в Solaris.

То, как я получаю данные, в основном просто отбрасывает каждую часть буфера как одну из структур и читает данные. Например, если у меня есть

u_char buffer[] = {...some bytes...};
struct ether_header *ethdr = (struct ether_header *)buffer;
struct ip *iphdr = (struct ip *) (buffer + sizeof(struct ether_header));
etc. etc.

Затем я могу получить необходимую мне информацию, например:

iphdr->ip_v; //to get the version
etc->etc; //to get whatever piece of data I need

Обычно в Linux и AIX это работает нормально (некоторые структуры имеют разные имена в разных системах, но это не имеет значения), но при попытке запустить это в Solaris я продолжаю получать ошибку шины, когда она достигает iphdr->ip_v; после struct ip *iphdr = (struct ip *) (buffer + sizeof(struct ether_header)); . После некоторого расследования я обнаружил, что это вызвано попыткой доступа к невыровненной памяти. Это имеет смысл, поскольку размер заголовка Ethernet составляет всего 14 байт, поэтому заголовок IP не выровнен по байтам в массиве.

Я попытался обойти это, скопировав соответствующие части в отдельный буфер, прежде чем пытаться их прочитать.

memcpy(&buffer_copy, buffer + sizeof(struct ether_header), sizeof(struct ip));
struct ip *iphdr = &buffer_copy;
iphdr->ip_v;
etc.

Это работает, но я не понимаю, почему. Почему memcpy не выдает ошибку шины, когда пытается получить доступ к тому же месту в памяти? Мне не нравится решение, которое я придумал, и я пытаюсь лучше понять ситуацию, чтобы придумать что-то другое. Может быть, я пропустил часть головоломки?


person monkeygame7    schedule 31.07.2015    source источник
comment
memcpy() копирует байты, они никогда не смещаются.   -  person Hans Passant    schedule 31.07.2015
comment
Система требует, чтобы данные были выровнены по 4 байтам. Я не пытаюсь читать из середины байта, а из позиции байта, которая не кратна 4 (именно к этому относится выравнивание памяти).   -  person monkeygame7    schedule 31.07.2015
comment
Нет, для выравнивания требуются переменные размером более байта.   -  person Hans Passant    schedule 31.07.2015
comment
Какое оборудование? Аппаратное обеспечение SPARC не является аппаратным обеспечением x86.   -  person Andrew Henle    schedule 31.07.2015
comment
Тогда может кто-нибудь объяснить, почему struct ip *iphdr = (struct ip *) (buffer + sizeof(struct ether_header)); выдает ошибку шины? Вернее, почему попытка доступа к данным внутри него выдает ошибку.   -  person monkeygame7    schedule 31.07.2015
comment
Я не знаю особенностей оборудования.   -  person monkeygame7    schedule 31.07.2015
comment
@monkeygame7 - Что выдает uname -a?   -  person Andrew Henle    schedule 31.07.2015
comment
SunOS apollo 5.11 11.0 sun4v sparc SUNW,Sun-Fire-T1000   -  person monkeygame7    schedule 31.07.2015
comment
en.wikipedia.org/wiki/Data_structure_alignment   -  person user3386109    schedule 31.07.2015


Ответы (1)


На оборудовании SPARC у вас есть два варианта:

  1. Не пишите код, который обращается к памяти способом, вызывающим ошибку шины.
  2. Скомпилируйте с помощью компиляторов Solaris Studio и используйте параметр командной строки -xmemalign=1i. Это приведет к тому, что двоичный файл будет работать медленнее, чем двоичный файл, предполагающий правильно выровненный доступ к памяти.

Также см. этот вопрос.

person Andrew Henle    schedule 31.07.2015
comment
Я просто не понимаю, почему memcpy не вызывает ошибку шины. Я передаю это точно такое же место в него. - person monkeygame7; 31.07.2015
comment
@monkeygame7 - Потому что memcpy() предназначен для перемещения произвольного фрагмента памяти из одного произвольного места в другое произвольное место и, таким образом, реализован таким образом, что не имеет ограничений по выравниванию. Но на оборудовании SPARC обычная инструкция загрузки или сохранения для таких вещей, как целые числа и значения с плавающей запятой, имеет ограничения выравнивания, которые вызывают SIGBUS, если ограничения выравнивания нарушаются. Аппаратное обеспечение буквально не подключено таким образом. - person Andrew Henle; 31.07.2015
comment
Спасибо. Я подумал, что это должно быть реализовано по-другому. Я думаю, что копирование данных в другую структуру - единственный способ получить к ним доступ. - person monkeygame7; 31.07.2015