Индексирование аргументов в виде массива в Windows x86-64 ABI

Я пытаюсь перенести функцию-оболочку с 32-битной на x86-64 asm для Windows ABI. Функция зависит от индексации своих аргументов в виде массива.

Я знаю, что MSVC не может выполнять встроенную сборку в проектах X64, но мне интересно встроить эквивалентную функцию в файл X64 .asm.

Функция устанавливает фрейм стека для вызова API.

__declspec( naked ) PVOID WINAPIV CGX86( FARPROC lpPtr, UINT lpSize, ... )
{
    __asm {
        push ebp;
        mov ebp, esp;
        lea eax, [ ebp + 0x04 ];
        mov [ ebp - 0x04 ], eax;
        mov eax, [ ebp - 0x04 ];
        mov ecx, [ ebp + 0x0C ];
        add ecx, 2;
ParseArgs:
        cmp ecx, 2;
        jz short MoveFinal;
        push dword ptr [ eax + ecx * 0x04 ];
        sub ecx, 1;
        jmp short ParseArgs;
MoveFinal:
        call [ ebp + 0x08 ];
        mov esp, ebp;
        pop ebp;
        retn;
    }
}

пример использования:

CGX86( ( FARPROC )MessageBoxA, 4, GetForegroundWindow( ), "BODY", "TITLE", MB_OK );

person RaulFernando    schedule 25.04.2016    source источник
comment
И под помощью вы подразумеваете, что хотите, чтобы мы написали это для вас?   -  person Jester    schedule 26.04.2016
comment
К сожалению, это более сложная задача, чем вы можете подумать, потому что соглашение о вызовах, используемое в X64, сильно отличается. Придется полностью переписывать с нуля.   -  person Ross Ridge    schedule 26.04.2016
comment
@MichaelPetch да, это работает, потому что eax указывает на адрес возврата, как вы сказали, и использует индексацию на основе 1 для аргументов, поэтому последним копируемым аргументом будет ecx=3, который дает eax+3*4, а это ebp+16. Однако я не понимаю цели push ecx.   -  person Jester    schedule 26.04.2016
comment
@Jester: я удалил свой комментарий вскоре после того, как вставил его. Только когда я увидел, что функция делает в теле, я понял, что все в порядке. Я собирался опубликовать продолжение о бесполезном дублировании здесь mov [ ebp - 0x04 ], eax; mov eax, [ ebp - 0x04 ];, но решил удалить все это.   -  person Michael Petch    schedule 26.04.2016
comment
push ecx был ошибкой, мне нужно было сохранить регистр, чтобы восстановить его позже. но теперь я вижу, что Росс Ридж прав. Невозможно индексировать аргументы, такие как массив на x64, так что это бессмысленно. Спасибо в любом случае, ребята. eli.thegreenplace.net/2011/09 /06/stack-frame-layout-on-x86-64   -  person RaulFernando    schedule 26.04.2016
comment
Тем не менее, вы, конечно, можете написать эквивалентный код. Кроме того, если вы знаете, что вам потребуется не более, скажем, 6 аргументов, простой C switch избавится от ассемблерного кода.   -  person Jester    schedule 26.04.2016
comment
Блестящая идея, шут, я создам 6 фиктивных аргументов, а затем положу золото на rbp, спасибо, сэр :)   -  person RaulFernando    schedule 26.04.2016
comment
@RossRidge и другие близкие избиратели: я задал вопрос конкретно по той части, в которой Раулю действительно нужна была помощь. (@ Рауль: отредактируйте еще раз, если хотите еще больше улучшить свой вопрос, чтобы помочь другим людям в будущем, или откатитесь, если вам не нравятся мои изменения.)   -  person Peter Cordes    schedule 26.04.2016
comment
@PeterCordes Я не думаю, что ваши правки сильно улучшают вопрос. Хороший ответ на вопрос по-прежнему должен содержать подробное описание полного решения с использованием соглашения о вызовах X64, включая то, как обрабатывать регистры, используемые для аргументов, правильно выравнивать стек и создавать информацию о раскрутке для SEH. Так как теперь код нужно писать на MASM или другом ассемблере, а не на встроенном ассемблере, который тоже нужно охватить. Тогда есть проблема, что эта функция кажется бесполезной и ненужной, поэтому неясно, каковы ее реальные требования.   -  person Ross Ridge    schedule 26.04.2016
comment
@RossRidge: отличный способ расслабиться. Я согласен с тем, что оболочка на чистом C++ — гораздо лучший выбор, поскольку аргументы обычно являются константами времени компиляции. Что касается обработки регистров, используемых для аргументов, я думал, что это очевидно: хранить аргументы 3 и 4 в их слотах в теневом пространстве, а затем использовать ту же индексацию (с коэффициентом масштабирования 8). ОП уже сказал, что знает, что ему нужно написать это в отдельном .asm, а не в строке.   -  person Peter Cordes    schedule 26.04.2016
comment
@PeterCordes На самом деле вам нужно перенести входящие аргументы 5 и 6 из стека в регистры. Вам не нужно ничего помещать в теневое пространство. Возможно, вы можете упростить ситуацию, скопировав MAX(2, lpSize - 2) слотов стека, начиная с третьего теневого слота, но необходимость выравнивания стека до этого означает, что, вероятно, проще просто скопировать фактически используемые слоты стека. Что делает эту функцию бесполезной, так это то, что прямой вызов функции (например, MessageBoxA(GetForegroundWindow(), "BODY", "TITLE", MB_OK)) является лучшим выбором. Итак, вопрос в том, какую проблему на самом деле решает эта функция?   -  person Ross Ridge    schedule 26.04.2016
comment
Сначала спасибо за отзывы, ребята. Эта функция упрощает реализацию функций angelscript без необходимости прототипирования всего и привязки огромного количества обратных вызовов к приложению. Она также маскирует ReturnAddress реального вызывающего API, поэтому ее немного сложнее отменить. Использование GetProcAddress или других методов для получения смещения API также затруднит понимание и отслеживание IDA.   -  person RaulFernando    schedule 26.04.2016


Ответы (1)


Предложение Jester написать его на C, вероятно, хорошее, особенно. если его можно встроить в вызовы, где некоторые аргументы являются константами времени компиляции. В вашем примере прецедент передает в основном постоянные аргументы времени компиляции, включая указатель на функцию. Любой приличный оптимизирующий компилятор встраивает это и оптимизирует косвенность в обычный вызов функции с правильными аргументами. Убедитесь, что вы поместили определение туда, где оно может быть встроено.


Однако, если вы не можете заставить компилятор сделать хороший код:

Индексация аргументов в виде массива — единственная часть функциональности, которую неочевидно реализовать в 64-битном ABI, где некоторые аргументы находятся в regs.

Соглашение о вызовах 64-разрядной версии Windows предоставляет пространство для хранения 4 аргументов регистров прямо под аргументами стека (теневое пространство), поэтому фактически вы можете создать массив аргументов, который можно индексировать, используя не более 4 инструкций ( сохраните аргументы в эти слоты). Вам не нужно указывать первые 4 аргумента в особом регистре.

Не забудьте также поместить исходящие аргументы в regs вместо стека и оставить теневое пространство для вызываемой вами функции.

Как указывает Росс Ридж, убедитесь, что вы включили директивы для создания информации о раскрутке SEH в автономном пакете asm. Это еще одна веская причина отдавать предпочтение решению на чистом C++, особенно. если количество аргументов ограничено небольшим числом.

Ссылки на соглашения о вызовах и скоро.

я m не являюсь поклонником соглашения о вызовах Windows в целом, но оно упрощает реализацию функций var-args. Я почти уверен, что именно поэтому в ABI существует «теневое пространство».

person Peter Cordes    schedule 25.04.2016
comment
спасибо за помощь, теперь понял. Я проголосовал за вас и поставил галочку, не знаю, кто проголосовал и почему вы получили минус. - person RaulFernando; 26.04.2016
comment
@RaulFernando похоже, что вы нажали не ту кнопку, вы проголосовали против, а не за (есть только один голос, и он против). - person Jester; 26.04.2016
comment
@Jester: за некоторое время до комментария Рауля за него проголосовали. И теперь отрицательный голос удален после того, как я отредактировал, чтобы уточнить, почему это является ответом. :) - person Peter Cordes; 26.04.2016
comment
Да, я был сбит с толку, потому что Рауль сказал, что проголосовал за. - person Jester; 26.04.2016
comment
Две причины, указанные для теневого пространства, упоминаются в Windows Соглашение о вызовах x64 в документации MSDN. C Переменные аргументы и непрототипированные функции C. - person Michael Petch; 26.04.2016
comment
@MichaelPetch: У вас есть каноническая ссылка на __vectorcall ABI, которую я мог бы одновременно добавить в вики тегов x86? В нем есть только хорошие ссылки на официальные документы для не-Windows ABI, потому что я никогда не тратил время на поиск хороших ссылок. В идеале также какая-нибудь страница Microsoft о 32-битных ABI. - person Peter Cordes; 26.04.2016
comment
@MichaelPetch: Спасибо, обновил stackoverflow.com/tags/x86/info с помощью этих и 32-битных __stdcall/__cdecl. - person Peter Cordes; 26.04.2016