Могу ли я программно вывести соглашение о вызовах, используемое DLL C ++? - PullRequest
5 голосов
/ 17 февраля 2010

Представьте себе, что вы хотите написать программу, которая тестирует функции в dll-файле c ++. Вы должны разрешить пользователю выбирать dll (мы предполагаем, что речь идет о c ++ dll). Он должен иметь возможность получить список всех функций, экспортируемых DLL. Затем пользователь должен иметь возможность выбрать имя функции из списка, вручную ввести список аргументов (все основные типы аргументов, такие как массивы int, double, bool или char (например, строки c-типа)) и попытаться выполнить запустить выбранную функцию с указанными аргументами. Он хотел бы знать, выполняется ли функция с указанными аргументами или они вызывают ее сбой (например, потому что они не совпадают с сигнатурой).

Основная проблема заключается в том, что C ++, будучи языком со строгой типизацией, требует, чтобы вы знали количество и тип аргументов для вызова функции во время компиляции. А в моем случае я просто не знаю, что это за аргументы До тех пор, пока пользователь не выберет их во время выполнения.

Единственное решение, которое я нашел, состояло в том, чтобы использовать ассемблер, чтобы вручную выдвигать аргументы в стеке вызовов.

Однако я пришел к выводу, что если я захочу поработать со сборкой, мне лучше убедиться, что я знаю, какое соглашение о вызовах используются функциями в dll.

Итак (наконец-то :) вот мой вопрос: могу ли я вывести соглашение о вызовах программно? Зависимость Уокер мне не поможет, и я не знаю, как вручную читать формат PE.

Ответы [ 5 ]

3 голосов
/ 17 февраля 2010

Вы не указали, говорите ли вы здесь о 32-битном или 64-битном коде, и трудности, изложенные вами и другими авторами, в основном касаются 32-битного кода. В 64-битной Windows, по сути, есть только одно соглашение о вызовах (оно также есть в статье в википедии, связанной Джоном Кноеллером), что означает, что вы действительно знаете соглашение о вызовах (конечно, за исключением тех, кто готовит до своих).

Кроме того, в соответствии с соглашением о вызовах Microsoft x64 незнание количества параметров вызываемой функции не мешает вам вызывать ее, предоставляя столько параметров, сколько вы пожелаете / пользователь пожелает. Это потому, что вы, как вызывающая сторона, откладываете пространство стека и очищаете его впоследствии. - Конечно, отсутствие правильного [количества] параметров может привести к тому, что вызываемая функция делает глупые вещи, потому что вы предоставляете неверный ввод, но это другая история.

3 голосов
/ 17 февраля 2010

Ответ возможно .

Если имена функций оформлены в C ++, вы можете определить количество и типы аргументов по оформлению имени, это ваш лучший сценарий, и вполне вероятно, если MSVC использовался для написания кода в первую очередь.

Если экспортируемые функции соответствуют соглашению о вызовах stdcall (по умолчанию для windows api), вы можете определить количество байтов, которые нужно передать, но не типы аргументов.

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

http://en.wikipedia.org/wiki/X86_calling_conventions

Имя, которое функция дается в качестве экспорта, не требуется , чтобы иметь какие-либо связи с именем, которое видит компоновщик, но в большинстве случаев с экспортированным именем и именем символа, которое компоновщик видит одинаково.

2 голосов
/ 17 февраля 2010

Скомпилированный код не просто говорит: «К сожалению, эта функция - быстрый вызов, а вот эта - stdcall».

Даже современные дизассемблеры, такие как IDA, не пытаются определить типы вызовов по умолчанию (там может быть плагин или опция где-то idk).

Обычно, если вы человек, вы можете посмотреть первые несколько инструкций и сказать 90% времени. Если они являются pop и push, его stdcall, если его передача параметров через регистры (особенно ecx), то это cdecl. Fastcall также использует регистры, но делает что-то особенное .. не знаю, с моей головы. Но вся эта информация бесполезна, потому что ваша программа, очевидно, не будет человеком.

Если вы проводите тестирование, у вас по крайней мере есть файлы заголовков ?? Это ужасно трудный способ снять шкуру с кошки ..

0 голосов
/ 20 февраля 2010

На этой странице описано, как VC ++ 6 кодирует параметр и информацию о соглашении о вызовах в имя символа: http://www.bottledlight.com/docs/mangle.html

Я подозреваю, что более поздние версии VC ++ будут совместимы, но я не подтвердил это.

Есть также некоторые инструменты, которые автоматизируют это, которые сопровождают компилятор: http://msdn.microsoft.com/en-us/library/5x49w699.aspx

Перенос имени применяется только для функций C ++; если функция 'extern "C"', это не сработает.

0 голосов
/ 17 февраля 2010

Если вы хотите знать, какое соглашение о вызовах использует функция C ++, вам лучше всего изучить

  1. Заголовок, который объявляет эту функцию, и
  2. Документация для компилятора, который скомпилировал вашу конкретную DLL.

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

...