Как заставить malloc возвращать 32-битный указатель в 64-битной системе? - PullRequest
0 голосов
/ 22 января 2019

У меня есть ошибка в программе, так что она работает нормально на 32 битах, но работает только случайным образом на 64 битах из-за усечения 32-битного указателя где-то в программе.

Причина в том, что указатель превращается в NULL, если он malloc возвращает адрес памяти с битом, установленным в старших 32 битах при выделении указателя.

Итак, я нашел указатель, который запускает segfault. Но это не программа, в которой я участвовал (я пользователь, а не разработчик), и нет никаких предупреждений компилятора.

Итак, вместо того, чтобы тратить время, которого у меня нет, как просто убедиться, что malloc возвращает значение, которое можно использовать в 32-битном режиме?

Ответы [ 2 ]

0 голосов
/ 25 января 2019

Ядро должно поддерживать это, используя различные индивидуальные флаги : ADDR_LIMIT_32BIT, ADDR_LIMIT_3GB, PER_LINUX32, PER_LINUX32_3GB, PER_LINUX_32BIT. Команда setarch linux32 -B вызывает personality(PER_LINUX32|ADDR_LIMIT_32BIT), но ядро ​​игнорирует этот запрос на x86-64:

$ setarch x86_64 -B grep stack /proc/self/maps  
7fff38461000-7fff38482000 rw-p 00000000 00:00 0                          [stack]

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

0 голосов
/ 22 января 2019

Лучший вариант: попросите автора этой программы с ошибками исправить свой код, чтобы он был 64-битным, или используйте Linux x32 для 64-битных регистров, но 32-битных указателей.


AFAIK, нет простого способа сделать именно то, что вы просите, с помощью обычного glibc malloc . Замена malloc / free на mmap(MAP_32BIT), вероятно, будет работать, но будет ужасно для небольших распределений, потому что она может выделяться только в 4k чанках.
mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_32BIT, -1, 0) / munmap. Это не заменяющая вставка, потому что некоторый код, вероятно, нуждается в указателях, которые совместимы с free.

Или, если вы можете найти пользовательский malloc, который использует MAP_32BIT в качестве обходного пути для программного обеспечения с ошибками, которое не является 64-разрядным, вы можете использовать его в качестве замены. Или изменить пользовательскую библиотеку malloc, например tcmalloc, чтобы добавить MAP_32BIT, может быть намного проще, чем создать весь пользовательский gcc.

@ Basile Starynkevitch предлагает, возможно, использовать mmap(MAP_FIXED|MAP_NORESERVE) для отображения огромного диапазона, так что все, что осталось, это младшие 32 бита. (Тогда никогда не трогай это отображение). Диапазон адресов виртуальной памяти в пользовательском пространстве Но это сработает только в том случае, если это будет сделано до загрузки каких-либо библиотек по старшим адресам ( Как ограничить адресное пространство 64-битного процесса менее 4G? предлагает возможный способ предварительной ссылки для загрузки библиотек по низким адресам).

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


Если у вас есть код, который не является 64-битным чистым для указателей, но вы все еще хотите использовать преимущества 64-битных регистров для эффективного [u]int64_t, Linux xI ABI может быть именно тем, что вам нужно.

x32 - это ABI ILP32 для длинного режима x86-64, поэтому указатели size_t и long являются 32-разрядными типами, но 64-разрядными целочисленными типами, такими как long long и uint64_t могут использовать 64-битные регистры . Процессор работает в длинном 64-битном режиме, и ABI использует то же эффективное соглашение о вызовах регистра-аргументов, что и обычный x86-64 System V ABI.

Не не путать x32 с устаревшим 32-битным кодом i386 ABI . Они абсолютно не связаны. x32 - это небольшая модификация обычного x86-64 ABI.

Обычная причина использования x32 - меньший объем кэша с указательными структурами данных, увеличивающими попадания в кэш и экономящими пропускную способность памяти.

У меня нет времени выполнения Intel SVML для x32

Компилятор Intel и библиотеки, включая SVML, поддерживают x32 начиная с версии 16.0, см. Страница поддержки Intel x32 psABI . Если у вас версия старше 16.0, это может быть хорошей причиной для обновления.

(Похоже, на этой странице написано, что OpenMP может не поддерживаться на x32, по крайней мере, в версии 16.0. Это было бы проблемой, если я правильно понял. Текущий 19.01, возможно, он работает сейчас.)

Обратите внимание, что вывод asm для функции, которая добавляет два аргумента uint64_t, идентичен для icc -O3 -mx32 и icc -O3 -m64, оба из которых используют add rdi, rsi / mov rax,rdi. (Хотя ICC хорош в автоматическом векторизации и автоматическом распараллеливании, очевидно, что он плохо определяет lea rax, [rdi+rsi] как оптимизацию глазка, а при использовании mov не следует собственному совету по оптимизации, сначала скопируйте, а затем уничтожьте копия для более эффективного mov-elission .)

Но в любом случае, текущие версии самого компилятора Intel определенно поддерживают x32; вывод asm из C ++ показывает, что uint64_t равно unsigned long long вместо unsigned long.


Получение GCC для использования SVML:

GCC имеет опцию -mveclibabi=svml, которая позволяет автоматически векторизовать с использованием функций SVML. Поэтому, если у x32 ICC есть проблема с использованием OpenMP для автоматического распараллеливания, вы можете попробовать GCC.

gcc -fopenmp -O3 -ffast-math -march=native -mveclibabi=svml должно быть хорошо. (-ffast-math аналогично тому, что ICC включает по умолчанию.)


Получение обычных 64-битных malloc для возврата 32-битных указателей

В связи с этим: версия вопроса для OS X: Как выполнить 'malloc' в пределах первых 4 ГБ на x86_64 (там тоже нет простого пути).

Я не думаю, что glibc mallocесть опция для этого.

Можно было бы создать свой собственный glibc с небольшим изменением, если бы вы могли найти вызов mmap, который malloc использует для получения новых страниц из ОС, и добавитьфлаг MAP_32BIT.

Поместите сопоставление в первые 2 гигабайта адресного пространства процесса.Этот флаг поддерживается только на x86-64, для 64-битных программ.Он был добавлен для того, чтобы стеки потоков могли быть выделены где-то в первых 2 ГБ памяти, чтобы улучшить производительность переключения контекста на некоторых ранних 64-битных процессорах

Если вы компилируете не PIEвыполнимый, разрыв должен быть уже ниже 32, поэтому вам не нужно останавливать glibc от использования brk() для небольших выделений.

https://www.gnu.org/software/libc/manual/html_node/Malloc-Tunable-Parameters.html перечисляет вещи, которые вы можете установить с помощьюmallopt вызов или переменные окружения, например M_MMAP_THRESHOLD.Если установить 4k, glibc всегда будет использовать mmap для распределений такого размера или больше.Но нет 32-битной опции.

...