Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Управление стеком, crt

16K
03 декабря 2011 года
asmforce
186 / / 05.01.2010
Вопрос комплексный, так что может неслабо доставлять.;)

Исследования начинаются с передачи структур по значению в языках высокого уровня.
Работаю с отладчиком. Наблюдаю выделение памяти на стеке.

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

Итак. Имеем некий процесс, порожденный PE.
  • При его инициализации, операционная система резервирует (reserve) некоторое количество страниц памяти под стек.
  • Не более этого объёма она связывает (commit) с физическими страницами.
  • Эту информацию она берёт из полей `SizeOfStackReserve` и `SizeOfStackCommit` заголовка PE файла.
  • Это означает, что размер стека никогда не превысит значение `SizeOfStackReserve`, если явно этого не затребовать уже в рантайме.
  • Это означает, что после выхода (попытка доступа) стека за границы `SizeOfStackCommit` будет сгенерировано исключение `PageNotPresent` и в причинное место будет спроецирована физическая страница, а в случае нехватки физической памяти произойдёт fail и процесс будет терминирован с соответствующим кодом ошибки или будет использован своп.
  • Это означает, что после выхода за `SizeOfStackCommit` процесс будет иметь известные проблемы, если не попадёт в heap, что приводит к другим известным проблемам.

Проблема в том, что во всей этой схеме я не вижу место функции _alloca (_malloca - более `secure` :facepalm:). Хотя нет, - вижу укромный уголок: превращение слова на букву `Ж` в слово на букву `И`, где второе означает исключение, что по-идее может позволить огромному корпоративному приложению со всеми копирайтами не упасть вместе с многочасовыми несохранёнными наработками, а сберечь и тихо лечь (о как срифмовал:D).

Более того, хотелось бы знать, зачем может понадобиться вызывать (самому, а не кодом, который сгенерирован компилером) _alloca из кода на С/С++ или других подобных.

Ах да, переменный размер выделяемого блока.
А теперь вопросы: как быть с текущим стеком функции?
Ведь если распределённый блок заканчивается (начинается конец :facepalm:) восемью байтами ниже (адрес возврата из _alloca + аргумент-размер выделяемого блока) текущего esp, то это просто ту-ту (сами понимаете).
Значит нужно помещать адрес начала блока в esp и оттуда уже использовать "свой" стек (который может понадобиться внутри текущей функции для других распределений на стеке или вложенных вызовов).
И более фундаментальный вопрос: на кой чёрт нужна такая защита от переполнения, если ручное распределение по-прежнему необходимо и по-прежнему потенциально падабельно?

Возникает сопутствующий вопрос:
Цитата:

[COLOR="paleturquoise"]_alloca allocates size bytes from the program stack.[/COLOR] The allocated space is automatically freed when the calling function exits (not when the allocation merely passes out of scope). Therefore, do not pass the pointer value returned by _alloca as an argument to free.


Как это возможно, если я прав по тем пунктам, что выделены bb-кодом списка?
Тут вот обсуждали подтекание inline функций, что наводит на некоторые мысли.

Тут под номером 3.14 есть вопрос, но ответ не проливает свет (второй раз уже).

P.S. Надеюсь целевую аудиторию не отпугнёт многабукав?

260
03 декабря 2011 года
Ramon
1.1K / / 16.08.2003
1. alloca не является кросс-платформенным
2. предназначен для выделения небольших блоков в автоматической памяти в пределах функции (пример: локальный массив элементов неизвестного заранее размера)
3. вспомните, что например в архитектуре IA-32 представляют из себя пролог и эпилог ф-ции и какую роль там играет ebp. В результате вы получите объяснение того, как освобождается память и почему несчастный получил в релизнутой версии срабатывание собственноручно и с любовью внесенного паттерна "детонатор".
16K
03 декабря 2011 года
asmforce
186 / / 05.01.2010
Цитата: Ramon

1. alloca не является кросс-платформенным.



Не отрицаю.
Без проблем. Другая платформа - другое решение.

Цитата: Ramon

2. Предназначен для выделения небольших блоков в автоматической памяти в пределах функции (пример: локальный массив элементов неизвестного заранее размера)



Знаем.

Цитата: Ramon

3. Вспомните, что например в архитектуре IA-32 представляют из себя пролог и эпилог ф-ции и какую роль там играет ebp. В результате вы получите объяснение того, как освобождается память и почему несчастный получил в релизнутой версии срабатывание собственноручно и с любовью внесенного паттерна "детонатор".



Знакомо, - ebp сохраняет изначальный указатель стека для его восстановления перед выходом из функции.

Разумеется, если выделение памяти на стеке выполняется простым и элегантным:

 
Код:
sub esp, 100500h

то после
 
Код:
mov esp, ebp
pop ebp

всё будет выглядеть так, как будто ничего и не было.

Итак, Вы согласны с bb-пунктами?
Некоторые вопросы, что я поднял не получили ответа, Вы согласны?
260
04 декабря 2011 года
Ramon
1.1K / / 16.08.2003
Цитата: asmforce

Итак, Вы согласны с bb-пунктами?
Некоторые вопросы, что я поднял не получили ответа, Вы согласны?



Желаете ответы - уточните интересующие вас вопросы.

16K
04 декабря 2011 года
asmforce
186 / / 05.01.2010
Цитата: Ramon
Желаете ответы - уточните интересующие вас вопросы.



Ок.

Цитата:

[COLOR="lightblue"]The _freea function deallocates a memory block (memblock) that was previously allocated by a call to _malloca. _freea checks to see if the memory was allocated on the heap or the stack.[/COLOR] If it was allocated on the stack, _freea does nothing.



Это подводит нас к выводу о том, что alloca ничего не делает, кроме как уменьшает esp на необходимое число байт?
Если да, - зачем она нужна в таком случае?


Цитата: Ramon

2. Предназначен для выделения небольших блоков в автоматической памяти в пределах функции (пример: локальный массив элементов неизвестного заранее размера)



На счет "небольшести" блоков тоже вопрос:
Я видел код, который генерирует gcc для выделения памяти под структуру:

 
Код:
struct Data
{
  bool a;
  int b,c,d,e;
  long long f,g,h,i,j,k,l;
};


и

 
Код:
struct Data
{
  bool a;
  int b,c,d,e;
  long long f,g,h,i,j,k,l;
  char m[10240];
};


В первом случае она использует банальное:
 
Код:
sub esp, 0x49


А во втором:
 
Код:
mov eax, 0x2854
  call 0x405870 <_alloca>


Должно же этому быть рациональное объяснение.
Да и зачем в crt функция, которая делает то, что быстрее и короче сделать без неё?
260
04 декабря 2011 года
Ramon
1.1K / / 16.08.2003
Цитата: asmforce

Это подводит нас к выводу о том, что alloca ничего не делает, кроме как уменьшает esp на необходимое число байт?



Истинно.

Цитата: asmforce

Если да, - зачем она нужна в таком случае?




Цитата: Ramon

...
2. предназначен для выделения небольших блоков в автоматической памяти в пределах функции (пример: локальный массив элементов неизвестного заранее размера)
...

16K
04 декабря 2011 года
asmforce
186 / / 05.01.2010
Долго редактировал, - пардоньте.
260
04 декабря 2011 года
Ramon
1.1K / / 16.08.2003
Цитата: asmforce

На счет "небольшести" блоков тоже вопрос:
Я видел код, который генерирует gcc для выделения памяти под структуру:
 
Код:
struct Data
{
  bool a;
  int b,c,d,e;
  long long f,g,h,i,j,k,l;
};


и

 
Код:
struct Data
{
  bool a;
  int b,c,d,e;
  long long f,g,h,i,j,k,l;
  char m[10240];
};


В первом случае она использует банальное:
 
Код:
sub esp, 0x49


А во втором:
 
Код:
mov eax, 0x2854
  call 0x405870 <_alloca>


Должно же этому быть рациональное объяснение.
Да и зачем в crt функция, которая делает то, что быстрее и короче сделать без неё?



Компилятор оптимизировал естественный случай, а попытку суицида отдал на откуп CRT.

PS: К тому же в данном случае размер структуры известен заранее, а это потенциальная возможность оптимизации особенно при небольшом ее размере.

16K
04 декабря 2011 года
asmforce
186 / / 05.01.2010
Цитата: Ramon
Компилятор оптимизировал естественный случай, а попытку суицида отдал на откуп CRT.



О как! Я в ауте! :)

Должен признать, даже не думал об этом в такой плоскости.
Но это же ересь, Вам не кажется?

У меня есть более-менее сносное предположение, не отрицающее, при этом, Ваш вариант.:cool:
Вот псевдокод, соответствующий коду, что я увидел в отладчике:

Код:
push eax
ecx = [esp+8]
while( eax > 0x1000 )
{
  ecx -= 0x1000;
  [ecx] |= 0;
  eax -= 0x1000;
}
ecx -= eax;
[ecx] |= 0;
eax = esp;
esp = ecx;
ecx = [eax];
eax = [eax+4];
push eax


Вы только посмотрите на этот, на первый взгляд, бред:
 
Код:
[ecx] |= 0;


Это же гениально. Вызов alloca имеет смысл: она беспокоится о том, чтобы не залезть в heap, а получить access violation вместо этого в большинстве случаев. (ровно как и в случае выхода за `SizeOfStackReserve`).
Кстати НЕ сработает она только в редком случае, - когда heap и стек сомкнутся.
260
04 декабря 2011 года
Ramon
1.1K / / 16.08.2003
Как правило в конце стека расположена сторожевая страница, которая не даст вылезти стеку за свои пределы.

PS:Касательно стека и кучи, которые якобы двигаются навстречу друг другу. Сие утверждение перестало быть однозначно истинным с момента появления многопоточности, а многопоточность появилась ой как давно...
16K
04 декабря 2011 года
asmforce
186 / / 05.01.2010
Цитата: Ramon

Как правило в конце стека расположена сторожевая страница, которая не даст вылезти стеку за свои пределы.



Вот читаю:

Цитата:

По мере разрастания дерева вызовов (одновременного обращения ко всё большему числу функций) потоку, естественно, требуется и больший объем стека. Как только поток обращается к следующей странице (а она является сторожевой), то процессор возбуждает аппаратное исключение "доступ к сторожевой странице" (это не механизм, специфичный для стека, а вообще для любой страницы с PAGE_GUARD-атрибутом). Система видит это исключение и расширяет стек - та страница, что была сторожевой, становится обычной read-write страницей, а следующая за ней (в сторону младших адресов, т.к. стек растёт в обратную сторону) становится новой сторожевой страницей. Благодаря такому механизму работы, объем памяти, занимаемой стеком, увеличивается только по мере необходимости.



Так стек расширяется за пределы `SizeOfStackReserve` или сторожевая страница находится только внутри этой области?

Цитата: Ramon

PS:Касательно стека и кучи, которые якобы двигаются навстречу друг другу. Сие утверждение перестало быть однозначно истинным с момента появления многопоточности, а многопоточность появилась ой как давно...



У каждой нити свой стек, но они-то в одном виртуальном а.п.. Значит в разных областях. А куча-то общая!
Вы об этом?

260
04 декабря 2011 года
Ramon
1.1K / / 16.08.2003
Цитата: asmforce

Так стек расширяется за пределы `SizeOfStackReserve` или сторожевая страница находится только внутри этой области?


Стек за пределы `SizeOfStackReserve` не выходит.

Цитата: asmforce

У каждой нити свой стек, но они-то в одном виртуальном а.п.. Значит в разных областях. А куча-то общая!
Вы об этом?


Как то так.

16K
04 декабря 2011 года
asmforce
186 / / 05.01.2010
Цитата: Ramon

Стек за пределы `SizeOfStackReserve` не выходит.



Вообще-то, было бы удивительно, если бы не так. Хотя бывает ещё и не такое.

P.S. Thx!

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог