Использование групповых таблиц для объединения вызовов процедур
Я читал некоторые статьи в сети, касающиеся Vable thunks, и где-то читал, что thunk можно использовать для перехвата / цепочки вызовов процедур.
Это достижимо?
Кто-нибудь знает, как это работает, также я не могу найти хороший ресурс, объясняющий гром. Какие-нибудь предложения для этого?
2 ответа
Реализация необработанного thunk в стиле v-table thunks - это последнее средство. Все, что вам нужно сделать, скорее всего, может быть достигнуто с помощью функции-обертки, и это будет гораздо менее болезненным.
В общем, Thunk делает следующее:
- Исправьте входные параметры (например, конвертируйте в другой формат)
- Назовите реальную реализацию
- Очистить шаг 1 / исправить выходные параметры
Чтобы увидеть пример того, как это работает, давайте обратимся к нашему хорошему другу Рэймонду Чену и его обсуждению корректоров:
http://blogs.msdn.com/oldnewthing/archive/2004/02/06/68695.aspx
Thunk он использовал следующим образом:
[thunk]:CSample::QueryInterface`adjustor{4}':
sub DWORD PTR [esp+4], 4 ; this -= sizeof(lpVtbl)
jmp CSample::QueryInterface
Как он описывает, у вас есть класс, который реализует одни и те же методы через несколько интерфейсов, поэтому он имеет несколько v-таблиц. (Если вы не знаете COM, все, что вам нужно знать, это то, что он работает с v-таблицами напрямую, поэтому указатель на конкретный интерфейс должен содержать указатели функций на все методы этого интерфейса по порядку.)
Если вы реализуете два интерфейса с разными методами в определенном слоте, вам нужно несколько v-таблиц. Но вы пишете перекрывающиеся методы только один раз, так что метод должен уметь работать с обоими указателями "this". Для этого компилятор генерирует метод, который выполняет исправление, и вызывает исходную реализацию.
Итак, этот блок выполняет следующие действия:
- Исправьте входные параметры, а именно скрытый указатель "this", в первой строке.
- Назовите реальную реализацию во второй строке.
- Очистка: не требуется (см. Ниже)
Это где jmp
инструкция приходит. Обычно, если вы должны были вызвать функцию, используя call
, он вернется к вам, и вам придется ret
обратно к вашему абоненту. Поскольку нет необходимости выполнять очистку, компилятор выполняет оптимизацию, где он перемещает выполнение прямо в реальную реализацию, и пусть оператор возврата реальной реализации возвращает вашему вызывающему объекту. Это всего лишь оптимизация, а не фундаментальная часть thunking. Например, 16/32-разрядные преобразователи преобразуют параметры ввода / вывода между 16 и 32 битами по мере необходимости, поэтому он не может пропустить этап очистки; он должен call
не jmp
,
Мораль этой истории такова: если вам нужно что-то сделать, например, jmp
Оптимизация, которую вы не можете написать непосредственно на C++ или другом языке высокого уровня, который вы выбрали, продолжайте и пишите на ассемблере. В противном случае просто напишите обертку и покончите с этим.
Честно говоря, звучит так, будто вы просите об оптимизации производительности, и большую часть времени (1) компилятор лучше оптимизирует, чем мы думаем, и (2) он не принесет вам столь значительного улучшения, как вы думаете.
Ну, вы читали, что thunks - это решение, а теперь вы ищете проблему для решения?
Thunks, как правило, представляют собой короткие функции "пересылки", которые обеспечивают незначительные, обычно жестко заданные настройки.
Куски VTable в настоящее время очень хорошо объясняются в википедии. Они используют общий шаблон: генерируют небольшую функцию, чтобы избежать вычислений / дополнительной работы во время выполнения.
Другие места, которые я видел / использовал thunks:
Связывание дескриптора окна с объектом окна: для каждого подкласса окна на лету генерируется небольшой фрагмент, который вызывает оконную процедуру со ссылкой на объект, а затем используется в качестве оконной процедуры.
Задержка загрузки DLL: Thunk гарантирует, что DLL загружается при первом вызове любой функции.
Перехват вызовов интерфейса COM: модули предоставляют точку ввода для диагностики и переходят к реальному методу.