2015-06-22 8 views
1

Я пишу на C++, используя MSVC++ 2012, и мой код предназначен для платформы x86. У меня есть ситуация, когда было бы полезно написать функцию, которая (среди прочего) может выделить некоторую память в стеке функции CALLING. Не мое намерение в этом посте обсуждать мудрость этого, но только для рассмотрения технической осуществимости.Выделение памяти в стеке вызывающего абонента

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

Кто-нибудь видит какие-либо отверстия в этом плане?

+1

Не совсем, нет, предполагая, что вы не испортили ассемблер, и есть достаточно пространства для стека :) Я предполагаю, что стек должен будет исправить в какой-то момент после возврата, чтобы удалить пространство распределения? –

+1

Как это делает '' alloca' '(http://man7.org/linux/man-pages/man3/alloca.3.html)? –

+0

Это, вероятно, проблема XY. 'alloca' делает то, что вы хотите, поскольку @CaptainObvlious отметил, но без какого-либо фона все решения могут быть просто бессмысленными. –

ответ

6

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

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

Единственный способ, которым я выполнял то, что вы хотите сделать, - использовать параметр по умолчанию. Вы можете сделать, например.

int f(void* p = alloca(55)) { 
} 

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

+0

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

+0

@Josh: Очевидное противодействие - это определение макроса, который эффективно передается в параметре для вас. – Puppy

0

Было много хороших моментов, сделанных несколькими плакатами. Я буду лежать здесь.

Метод, который я описал, будет работать, но ТОЛЬКО, если вызывающая функция была определена с помощью соглашения о вызове CDECL. И переключатель компоновщика, который отключает указатели на кадры стека, не включен. Вызывающая функция должна быть CDECL, а не STDCALL или THISCALL, потому что STDCALL и THISCALL неправильно восстанавливали бы указатель стека в эпилоге вызывающей функции, поскольку эти соглашения вычитают из текущего ESP, а не восстанавливают исходное значение. Они вычитали бы неправильную сумму, не учитывая добавленное пространство, и поэтому стек был бы поврежден. Выключатель компоновщика, который отключает указатель фрейма стека, не должен быть активным, потому что если он является вызывающей функцией, он будет ссылаться на параметры как смещения от ESP, а не EBP, и поэтому смещения будут неправильными.

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

С учетом ограничений, которые я решил не продолжать с этой техникой.

 Смежные вопросы

  • Нет связанных вопросов^_^