Вы можете упростить асм до одной инструкции, когда математика выполняется во время сборки. movabs rax, [0xdeadbeef + 0xa]
может использовать AL / AX / EAX / RAX-форму mov, который загружается с 64-битного абсолютного адреса (https://felixcloutier.com/x86/MOV.html). (он не подходит для 32-битного расширенного знака disp32, потому что старший бит младшего 32 установлен, в отличие от обычных статических адресов в позиционно-зависимом коде.) Обычный mov
с 32-битным переопределением размера адреса тоже будет работать, примерно в 7 байтах, потому что ваш адрес подходит в расширенном от нуля 32-разрядном целом числе.
В C вы также можете сделать все это одним утверждением . Не нужно слишком усложнять вещи: ваш адрес является указателем на указатель, поэтому вам нужно привести ваше целое число к типу x **
.
void *ptr = *(const void**)(0xdeadbeefUL + 0xa);
В asm указатели являются просто целыми числами, поэтому имеет смысл выполнять математику, используя целые числа вместо char*
. Отсутствие знака гарантирует, что он расширяется от нуля до ширины указателя, а не до знака.
(Числовые литералы в C имеют достаточно широкий тип для представления значения, поэтому 0xdeadbeef
на компиляторе x86-64 будет int64_t
(long long
). На самом деле вы не получите 0xdeadbeef
являющийся отрицательным 32-разрядным int
со знаком, расширенным до 0xffffffffdeadbeef
.)
Так как void
не имеет размера, вы не можете добавлять / вычитать целые числа к void*
. И указатель-математика на void **
будет в кусках sizeof(void*)
.
Чтобы избежать неопределенного поведения от разыменования void**
, который не выровнен по 8 = alignof(void*)
(в обоих основных x86-64 ABI), вы должны использовать memcpy
. Но я предполагаю, что ваш пример адреса является лишь поддельным примером. Основные компиляторы x86, такие как gcc, не делают ничего странного с невыровненными адресами, чтобы наказать программистов за UB, поэтому выходные данные компилятора будут содержать невыровненные нагрузки, которые отлично работают на x86. Но при автоматической векторизации вы можете столкнуться с проблемами из такого рода UB. Почему при выравнивании доступа к памяти mmap иногда возникает ошибка на AMD64?
Но если вы по какой-то причине захотели разбить вещи на несколько операторов asm, вы можете транслитерировать это на несколько операторов C, например:
uintptr_t wheres_the_beef = 0xdeadbeef; // mov eax, 0xdeadbeef
wheres_the_beef += 0xa; // add eax, 0xa
void **address = (void**)wheres_the_beef; // purely a cast, no asm instructions;
void *ptr = *address; // mov rax, [rax]
Вы можете возиться с char*
, если хотите добавить смещение байтов в указатели, но здесь нет никакого смысла.
Опять же, это все еще имеет неопределенное поведение в большинстве реализаций C, где alignof(void*)
больше 1, поэтому void **address = (void**)wheres_the_beef
создает неправильно выровненный указатель.
(Интересный факт: даже создание неправильно выровненных указателей - это UB в ISO C. Но все компиляторы x86, которые поддерживают встроенные функции Intel, должны поддерживать создание неправильно выровненных указателей для передачи их встроенным объектам, таким как _mm_loadu_ps()
, поэтому потенциальная проблема заключается только в их разыменовании. на компиляторах x86.)