Неравномерные указатели на x86

Может ли кто-нибудь представить пример, что литье указателя из одного типа в другой происходит из-за неправильного выравнивания?

В комментариях к этому ответу оба государства заявляют, что делают что-то вроде

char * foo = ...; int bar = *(int *)foo; 

может привести к ошибкам даже на x86, если включена проверка выравнивания.

Я попытался создать условие ошибки после установки флажка выравнивания через set $ps |= (1<<18) в GDB, но ничего не произошло.

Как выглядит рабочий (т.е. нерабочий;)) пример?


Ни один из fragmentов кода из ответов не работает в моей системе – я попробую его с другой версией компилятора и на другом ПК позже.

Кстати, мой собственный тестовый код выглядел так (теперь также используем asm для установки флага AC и неустановленного чтения и записи):

 #include  int main(void) { #ifndef NOASM __asm__( "pushf\n" "orl $(1<<18),(%esp)\n" "popf\n" ); #endif volatile unsigned char foo[] = { 1, 2, 3, 4, 5, 6 }; volatile unsigned int bar = 0; bar = *(int *)(foo + 1); assert(bar == 0x05040302); bar = *(int *)(foo + 2); assert(bar == 0x06050403); *(int *)(foo + 1) = 0xf1f2f3f4; assert(foo[1] == 0xf4 && foo[2] == 0xf3 && foo[3] == 0xf2 && foo[4] == 0xf1); return 0; } 

Утверждение проходит без проблем, даже несмотря на то, что сгенерированный код определенно содержит неприсоединившийся доступ mov -0x17(%ebp), %edx и movl $0xf1f2f3f4,-0x17(%ebp) .


Таким образом, установка AC запускает SIGBUS или нет? Я не мог заставить его работать на моем двухъядерном ноутбуке Intel под Windows XP, и ни одна из версий GCC, которые я тестировал (MinGW-3.4.5, MinGW-4.3.0, Cygwin-3.4.4), в то время как кодологические и Джонатан Леффлер упомянутые сбои на x86 …

Дополнительное условие, не упомянутое, для EFLAGS.AC фактически вступает в силу. CR0.AM должен быть установлен для предотвращения отключения INT 17h на более старых ОС, предшествующих 486, у которых нет обработчика этого исключения. К сожалению, Windows не устанавливает его по умолчанию, вам нужно написать драйвер режима ядра, чтобы установить его.

Ситуации встречаются редко, когда неравномерный доступ вызывает проблемы на x86 (за исключением того, что доступ к памяти занимает больше времени). Вот некоторые из тех, о которых я слышал:

  1. Вы можете не считать это проблемой x86, но операции SSE выигрывают от выравнивания. Выровненные данные могут использоваться в качестве операнда источника памяти для сохранения инструкций. Команды Unaligned-load, такие как movups , медленнее, чем movaps на микроархитектурах до Nehalem, но в Nehalem и более поздних версиях (и семействе Bulldozer AMD) неуравновешенные 16-байтовые нагрузки / хранилища примерно так же эффективны, как и невыложенные 8-байтовые нагрузки / хранилища; single uop и вообще никакого штрафа, если данные будут выровнены во время выполнения или не пересекаются с границей кеш-линии, в противном случае эффективная аппаратная поддержка расщепления линии кэша. Расщепления 4k очень дороги (~ 100 циклов), пока Skylake (до ~ 10 циклов, как разделение кэш-линии). См. https://agner.org/optimize/ и ссылки на производительность в wiki для x86 для получения дополнительной информации.

  2. блокированные операции (например, lock add [mem], eax ) очень медленны, если они недостаточно выровнены, особенно если они пересекают границу линии кэша, поэтому они не могут просто использовать блокировку кэша внутри ядра процессора. На старых (багги) системах SMP они могут фактически не быть атомарными (см. https://blogs.msdn.com/oldnewthing/archive/2004/08/30/222631.aspx ).

  3. и еще одна возможность, обсуждаемая Раймондом Ченом, – это иметь дело с устройствами, имеющими аппаратную банковскую память (по общему признанию, странную ситуацию) – https://blogs.msdn.com/oldnewthing/archive/2004/08/27/221486.aspx

  4. Я помню (но не имею ссылки для – так что я не уверен в этом) похожие проблемы с негласными доступами, которые ограничивают границы страниц, которые также связаны с ошибкой страницы. Я посмотрю, смогу ли я выкопать ссылку для этого.

И я изучил что-то новое, когда смотрел на этот вопрос (мне было интересно о команде « $ps |= (1<<18) » GDB, упомянутой в нескольких местах). Я не понимал, что процессоры x86 (начиная с 486, похоже,) могут вызывать исключение при выполнении несогласованного доступа.

Из «Программных приложений для Windows, 4-го Эд» Джеффри Рихтера:

Давайте подробнее рассмотрим, как процессор x86 обрабатывает выравнивание данных. CPU x86 содержит специальный бит в своем регистре EFLAGS, который называется флажком AC (выравнивание). По умолчанию этот флаг установлен на ноль, когда процессор сначала получает питание. Когда этот флаг равен нулю, CPU автоматически выполняет все, что ему нужно, чтобы успешно получить доступ к значениям несогласованных данных. Однако, если этот флаг установлен в 1, CPU выдает прерывание INT 17H всякий раз, когда есть попытка получить доступ к несогласованным данным. Версия x86 для Windows 2000 и Windows 98 никогда не изменяет бит этого процессора. Таким образом, вы никогда не увидите исключение несоосности данных в приложении, когда оно выполняется на процессоре x86.

Это было для меня новостью.

Конечно, большая проблема с несогласованными обращениями заключается в том, что когда вы в конечном итоге переходите на компиляцию кода для процессора, отличного от x86 / x64, вам приходится отслеживать и исправлять целую кучу вещей, поскольку практически все остальные 32-разрядные или более крупные процессоры чувствительны к проблемам с выравниванием.

Если вы прочитали архитектуру Core I7 (в частности, свою литературу по оптимизации), Intel фактически поставила TON аппаратного обеспечения там, чтобы сделать доступ к неправильной памяти практически бесплатной. Насколько я могу судить, только смещение, пересекающее границу линии кэша, имеет какую-либо дополнительную стоимость – и даже тогда оно минимально. Насколько мне известно, у AMD также очень мало проблем с неправильным доступом (по циклу) (хотя прошло некоторое время).

Для чего это стоит, я установил этот флаг в eflags (проверка битов AC-бит), когда я увлекался оптимизацией проекта, над которым я работал. Оказывается, что windows являются ПОЛНЫМИ неправильными доступами – так много, что я не смог найти какие-либо смещенные обращения к памяти в нашем коде, меня обстреляли так много неправильных доступов в библиотеках и оконном коде, что у меня не было времени Продолжать.

Возможно, мы сможем узнать, что, когда процессоры делают вещи бесплатными или очень дешевыми, программисты станут самодовольными и будут делать что-то лишнее. Возможно, инженеры Intel провели некоторое исследование, и обнаружили, что типичное настольное программное обеспечение x86 делает миллионы несогласованных обращений в секунду, поэтому они устанавливают невероятно быстрое несогласованное оборудование доступа в CoreI7.

НТН

char * foo, вероятно, совпадает с границами int. Попробуй это:

 int bar = *(int *)(foo + 1); 
 char *foo = "...."; foo++; int *bar = (int *)foo; 

Компилятор поместил бы foo на границу слова, а затем, когда вы увеличиваете его, оно находится в слове + 1, что является недопустимым для указателя int.

 #include  int main(int argc, char **argv) { char c[] = "a"; printf("%d\n", *(int*)(c)); } 

Это дает мне SIGBUS после установки set $ps |= (1<<18) в gdb, который, по-видимому, бросается, когда выравнивание адреса неверно (среди других причин).

EDIT: довольно легко поднять SIGBUS:

 int main(int argc, char **argv) { /* EDIT: enable AC check */ asm("pushf; " "orl $(1<<18), (%esp); " "popf;"); char c[] = "1234567"; char d[] = "12345678"; return 0; } 

Глядя на parsingку главного устройства в gdb:

 Dump of assembler code for function main: .... 0x08048406 : mov 0x8048510,%eax 0x0804840b : mov 0x8048514,%edx 0x08048411 : mov %eax,-0x10(%ebp) 0x08048414 : mov %edx,-0xc(%ebp) 0x08048417 : movl $0x34333231,-0x19(%ebp) <== BAM! SIGBUS 0x0804841e : movl $0x38373635,-0x15(%ebp) 0x08048425 : movb $0x0,-0x11(%ebp) 

Во всяком случае, Кристоф, ваша тестовая программа выходит из строя под Linux, поднимая SIGBUS так, как должна. Это, наверное, вещь Windows?


Вы можете включить бит проверки выравнивания в коде с помощью этого fragmentа:

 /* enable AC check */ asm("pushf; " "orl $(1<<18), (%esp); " "popf;"); 

Кроме того, убедитесь, что флаг действительно установлен:

 unsigned int flags; asm("pushf; " "movl (%%esp), %0; " "popf; " : "=r"(flags)); fprintf(stderr, "%d\n", flags & (1<<18)); 

Чтобы воспользоваться исключением, вызовите SetErrorMode с помощью SEM_NOALIGNMENTFAULTEXCEPT :

 int main(int argc, char* argv[]) { SetErrorMode(GetErrorMode() | SEM_NOALIGNMENTFAULTEXCEPT); ... } 

Дополнительные сведения см. В разделе « Выравнивание данных Windows» на IPF, x86 и x64 .

gcc, когда авто-векторизация предполагает, что uint16_t* выровнена с 2-байтовой границей. Если вы нарушите это предположение, вы можете получить segfault: почему неуправляемый доступ к mmap’ed-памяти иногда может возникнуть на AMD64?

Поэтому соблюдение правил выравнивания C имеет значение даже при таргетинге на x86.


Используйте это, чтобы эффективно выразить неуравновешенную нагрузку в C:

 static inline uint32_t load32(char *p) // char* is allowed to alias anything uint32_t tmp; memcpy(&tmp, p, sizeof(tmp)); return tmp; } 

На x86 он будет скомпилирован с единственным mov вы ожидаете (или автоматически-векторизовать или каким-либо другим), но на SPARC или MIPS перед MIPS64r6 или каким бы то ни было компилятором для любой последовательности команд, необходимой для невыложенной нагрузки. Это использование memcpy полностью оптимизирует цели, которые поддерживают невысокие нагрузки.

т. е. ваш компилятор знает, поддерживает ли целевая ISA неуправляемые нагрузки или нет, и будет выдавать asm, который делает их или нет по своему усмотрению.

  • Являются ли a, & a, a, a , & a и & a одинаковыми указателями?
  • Можно ли выделить функцию внутри массива и вернуть ее с помощью ссылки?
  • Что происходит, когда вы удаляете указатель дважды или больше на C ++?
  • Инициализация указателя в отдельной функции в C
  • Могу ли я взять адрес элемента «один конец прошлого» массива?
  • Передача 2D-массива функции C ++
  • Почему не указатели инициализируются с помощью NULL по умолчанию?
  • Адрес массива
  • Размещение звездочки в объявлениях указателей
  • Как передать функцию в качестве параметра в C?
  • Можно ли программно определить размер массива C ++? А если нет, то почему?
  • Interesting Posts

    Динамически добавлять компоненты в JDialog

    В чем разница между анонимными методами (C # 2.0) и lambda-выражениями (C # 3.0)?

    Как очистить текст всех текстовых полей в форме?

    В каком порядке следует устанавливать драйверы, программы и обновления после установки новой Windows?

    SQL Server 2005 – использование сгенерированных последовательностей вместо столбцов Identity?

    $ http-запрос не отправляет куки-файлы в угловой CORS

    Как я могу написать единичный тест, чтобы определить, можно ли собирать мусор?

    Как заполнить geom_polygon разными цветами выше и ниже y = 0?

    Как реализовать аутентифицированные маршруты в React Router 4?

    Несколько версий Excel

    Захват звука для анализа и визуализации частот в Android

    Включение и отключение устройства Windows 7 через командную строку

    Есть ли способ вставить команду в Планировщик задач Windows?

    Текстовая тень в Internet Explorer?

    Как сделать брекеты с клавиатурой Apple в Windows?

    Давайте будем гением компьютера.