Грешка на Solaris Bus не се случва след копиране на неподравнени данни на ново място

И така, накратко: програмата ми получава необработен байт (u_char) буфер, който представлява мрежов пакет. Опитвам се да анализирам информацията в този пакет и го правя, като използвам дефинираните от системата структури на заглавките (ether_header, ip, ip6, tcphdr, udphdr). Приложих това както на Linux, така и на AIX и работи, но по някаква причина продължавам да получавам Bus Error, когато правя това на 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, продължавам да получавам Bus Error, когато стигне до 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 не извежда Bus Error, когато се опитва да получи достъп до същото място в паметта? Не ми харесва толкова много решението, което измислих, и се опитвам да разбера по-добре ситуацията, за да мога да измисля нещо друго. Може би пропускам част от пъзела?


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)); извежда Bus Error? Или по-скоро защо опитът за достъп до данни вътре в него извежда грешката.   -  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