Что именно вызывает исключение нарушения доступа к памяти для чтения (аппаратное обеспечение/ОС)?

Рассмотрим пользовательскую структуру данных, которая представляет собой 3-байтовое целое число без знака. В ассемблере с прямым порядком байтов можно просто загрузить 4 байта в регистр с помощью указателя на двойное слово и, например, выполнить сложение 3-байтовых целых чисел, сохранить младшее слово по адресу памяти «x», сдвинуть результат вправо на 16 и сохранить младший байт по адресу памяти x + 2. Более медленная версия загрузки с неопределенным поведением будет состоять в том, чтобы сначала обнулить регистр «a», загрузить младшее слово в регистр «a», загрузить третий байт в регистр «b». , сдвиньте «b» влево на 16 и ИЛИ зарегистрируйте «a» и «b» в другой регистр.

Итак, теперь давайте предположим, что заданное 3-байтовое целое число находится прямо на краю программного пространства или сегмента данных; интуитивно вы получили бы незаконный доступ к памяти, разыменовав адрес целого числа как двойное слово, не так ли? Или в более общем виде: когда происходит нарушение доступа к памяти при чтении памяти; рассматривается только базовый адрес или это (baseAddress + lengthInBytes)? Я искал ответ в течение нескольких недель, но пока не нашел ответа, поэтому я спрашиваю сообщество...


person MrUnbelievable92    schedule 21.03.2021    source источник
comment
Достаточно легко проверить, и да, в этом случае это действительно ошибка. Вот почему SIMD strlen должен тщательно проверять, что он находится достаточно далеко от конца страницы, прежде чем выполнять невыровненную загрузку 16 байт или выполнять только выровненную загрузку. Вы не можете проникнуть в чтение байта с начала страницы только для супервизора или заставить HW заполнить нулями или чем-либо еще байты с неотображенной страницы (вообще нет действительного перевода), которые являются частью многобайтовая загрузка.   -  person Peter Cordes    schedule 22.03.2021
comment
Ответы, которые вы обнаружите, содержат только упоминание начального адреса для выровненных нагрузок, потому что естественно выровненный доступ не может пересекать более широкие границы. Так что в этом случае это то же самое, что требовать, чтобы все байты исходили из действительных страниц. (И если вы использовали сегментацию, я думаю, что в пределах сегмента.)   -  person Peter Cordes    schedule 22.03.2021
comment
Кстати, я написал большой ответ на Как переместить 3 байта (24 бита) из памяти в регистр? некоторое время назад.   -  person Peter Cordes    schedule 22.03.2021
comment
Эй - спасибо! фактическая причина, по которой я задал этот вопрос, связана с SIMD... А именно с суммой массива беззнаковых символов/байтов. Компиляторы, которые я пробовал, генерируют только SIMD-код, который с нуля расширяет их до 32-битных целочисленных векторов. Преобразование их в короткие добавляет вдвое больше за итерацию, но для оптимальной производительности вы должны использовать базовый указатель + число, кратное 8, в качестве ptr xmmword, который может выйти за пределы. Я просто придумал более простой пример, не задумываясь о загрузке 16 бит в AX. Я только что попробовал протестировать его на C с двумя глобальными 3-байтовыми переменными, не вызывая segfault. Я хотел бы знать, как это проверить!   -  person MrUnbelievable92    schedule 22.03.2021
comment
Чтобы проверить это, вы должны mmap загрузить страницу, а затем выполнить загрузку 4 байтов из 3 байтов до конца страницы, например *(volatile int32_t*)(page+4093). Если вы сообщите компилятору, что у вас есть 3-байтовый объект битового поля, он всегда безопасно загрузит его.   -  person Peter Cordes    schedule 22.03.2021
comment
Или в рукописном NASM с section .bss/align 4096/buf: resb 4096 и написать нагрузку типа mov eax, [buf + 4093]/mov eax, [buf+4096] и посмотреть, достигнута ли 2-я нагрузка. (Если это тоже не сбой, после этого есть еще одна страница BSS, возможно, из кода libc или CRT, если вы скомпоновали их вместо простого статического исполняемого файла; в этом случае, возможно, уменьшите до buf: resb 100, чтобы другие материалы BSS могли поместиться в та же страница)   -  person Peter Cordes    schedule 22.03.2021
comment
Re: векторизация: глупые компиляторы. Хороший способ суммировать байты без знака (без переполнения) состоит в том, чтобы psadbw против 0 расширить до двух 64-битных элементов, затем paddq или просто paddd быстрее на некоторых процессорах, и их можно использовать, если вам не нужны 64-битные суммы.   -  person Peter Cordes    schedule 22.03.2021