32-битное и 64-битное встроенное портирование сборки - PullRequest
2 голосов
/ 25 марта 2010

У меня есть фрагмент кода C ++ (скомпилированный с g ++ в среде GNU / Linux), который загружает указатель функции (как это не имеет значения), помещает некоторые аргументы в стек с помощью встроенной сборки и затем вызывает эта функция, код выглядит так:

unsigned long stack[] = { 1, 23, 33, 43 };

/* save all the registers and the stack pointer */
unsigned long esp;
asm __volatile__ ( "pusha" );
asm __volatile__ ( "mov %%esp, %0" :"=m" (esp));

for( i = 0; i < sizeof(stack); i++ ){
    unsigned long val = stack[i];
    asm __volatile__ ( "push %0" :: "m"(val) );
}

unsigned long ret = function_pointer();

/* restore registers and stack pointer */
asm __volatile__ ( "mov %0, %%esp" :: "m" (esp) );
asm __volatile__ ( "popa" );

Я бы хотел добавить что-то вроде

#ifdef _LP64
   // 64bit inline assembly
#else
   // 32bit version as above example
#endif

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

Спасибо

Ответы [ 3 ]

4 голосов
/ 25 марта 2010

Хотя вызов указателя функции с соответствующими аргументами во встроенной сборке не должен вызывать особых проблем, я не думаю, что наивный перекодирование в x64 поможет вам, потому что очень вероятно использовать используемые соглашения о вызовахразные (по умолчанию для 32-битной и 64-битной Linux определенно разные).Посмотрите здесь для деталей.Поэтому, я думаю, если в этом случае вам удастся обойтись без встроенной сборки (см. Другой ответ), будет проще портировать.

Edit : ОК, я вижу, вы можетедолжны использовать сборку.Вот несколько указателей.

Согласно документу Агнера Фога, в Linux x64 для передачи параметров используются RDI, RSI, RDX, RCX, R8, R9 и XMM0-XMM7.Это означает, что для достижения того, что вы хотите (без учета использования чисел с плавающей запятой), ваша функция должна будет:

(1) сохранить все регистры, которые должны быть сохранены (RBX, RBP, R12-R15):Выделите место в стеке и переместите туда эти регистры.Это будет что-то вроде (синтаксис Intel):

sub rsp, 0xSomeNumber1
mov [rsp+i*8], r# ; insert appropriate i for each register r# to be moved

(2) Оцените количество аргументов, которые вам придется передавать по стеку целевой функции.Используйте это, чтобы выделить необходимое пространство в стеке (sub rsp, 0xSomeNumber2), принимая во внимание 0xSomeNumber1, так что в конце стека будет выровнено по 16 байт, т.е. rsp должно быть кратно 16. Don 't изменяйте rsp после этого до тех пор, пока ваша вызванная функция не вернется.

(3) Загрузите аргументы вашей функции в стек (при необходимости) и в регистры, используемые для передачи параметров.На мой взгляд, проще всего, если вы начнете с параметров стека и загрузите параметры регистра последними.

;loop over stack parameters - something like this
mov rax, qword ptr [AddrOfFirstStackParam + 8*NumberOfStackParam]
mov [rsp + OffsetToFirstStackParam + 8*NumberOfStackParam], rax

В зависимости от того, как вы настроили свою подпрограмму, смещение к первому параметру стека и т. Д. Может быть непредвиденным.Затем установите количество переданных регистром аргументов (пропуская те, которые вам не нужны):

mov r9, Param6
mov r8, Param5
mov rcx, Param4
mov rdx, Param3
mov rsi, Param2
mov rdi, Param1

(4) Вызовите целевую функцию, используя регистр, отличный от указанного выше:

call qword ptr [r#] ; assuming register r# contains the address of the target function

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

Примечание : вышеприведенный эскиз не учитывает значения с плавающей запятой, которые должны быть переданы в регистрах XMM, но применяются те же принципы. Отказ от ответственности : Я сделал что-то похожее на Win64, но никогда на Linux, поэтому, возможно, некоторые детали я пропускаю.Хорошо читайте, тщательно пишите код и хорошо тестируйте.

2 голосов
/ 25 марта 2010

Не совсем отвечаю на ваш вопрос, но я думаю, что вы могли бы достичь этого независимо от платформы, используя setcontext (или makecontext).

1 голос
/ 25 марта 2010

Основные проблемы:

  • на x64 отсутствует pushad / popad, вам нужно нажать отдельные регистры, которые вы хотите сохранить
  • вам нужно сохранить ваш rsp (указатель стека 64 бит) в подходящем 64-битном регистре (rax ?, r8? Etc)
  • соглашение о вызовах почти наверняка изменилось с 32-разрядного на 64-разрядное

Сводка изменений с x86 на x64:

  • Регистры, начинающиеся с E, теперь имеют 64-битные эквиваленты, начиная с R. RAX, RBX, RCX, RDX, RDI, RSI, RIP, RSP, RBP.
  • Новые регистры: R8 ... R15
  • Нет pushad, Нет popad

Я портировал некоторый встроенный код x86 на x64 в Windows. Вам определенно нужно некоторое время, чтобы прочитать набор инструкций x64, а также прочитать соглашение о вызовах для вашей операционной системы. Изменения в Windows были радикальными, а новое соглашение о вызовах намного проще. Я подозреваю, что изменения в GNU / Linux также будут другими, я бы точно не предположил, что это то же самое.

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

...