Почему GCC использует указатель кадра, когда я вызываю функции Win32 с аргументами?

Когда я компилирую 32-битный код C с GCC и параметром -fomit-frame-pointer, указатель кадра (ebp) не используется, если только моя функция не вызывает функции Windows API с stdcall и хотя бы одним параметром.

Например, если я использую только GetCommandLine() из Windows API, который не имеет параметров/аргументов, GCC пропустит указатель фрейма и будет использовать ebp для других целей, ускоряя код и избавляясь от бесполезного пролога.

Но в тот момент, когда я вызываю функцию stdcall Win32, которая принимает хотя бы один аргумент, GCC полностью игнорирует -fomit-frame-pointer и все равно использует указатель кадра, а код хуже при проверке, поскольку он не может использовать ebp для общих целей. вещи. Не говоря уже о том, что я нахожу указатель кадра совершенно бессмысленным. Я имею в виду, что я хочу скомпилировать для выпуска и распространения, зачем мне отладка? (если я хочу отлаживать, я просто использую отладочную сборку после воспроизведения ошибки)

Мой стек, безусловно, НЕ содержит динамического распределения, такого как alloca. Итак, стек имеет определенную структуру, но GCC выбирает тупой метод, несмотря на мои варианты? Есть ли что-то, чего мне не хватает, чтобы заставить его не использовать указатель кадра?

Моя вторая проблема заключается в том, что он отказывается использовать инструкции «push» для функций Win32. Все остальные компиляторы, которые я пробовал, использовали push-инструкции для помещения в стек, что приводило к гораздо лучшему, более компактному коду, не говоря уже о том, что это самый естественный способ поместить аргументы для stdcall. Тем не менее, GCC упрямо использует инструкции «mov» для перемещения в каждом месте вручную со смещением относительно esp, потому что ему нужно, чтобы указатель стека оставался полностью статичным. stdcall устроен таким образом, чтобы вызывающая сторона была удобна для вызывающей стороны, и все же GCC полностью упускает из виду суть stdcall, поскольку генерирует этот дрянной код при взаимодействии с ним. Что еще хуже, так как указатель стека статичен, он по-прежнему использует указатель кадра? Просто почему?

Я пробовал -mpush-args, ничего не делает.

Я также заметил, что если я сделаю свой стек достаточно большим, чтобы он превышал страницу (4096 байт), GCC добавит пролог с функцией, которая ничего не делает, кроме «побитового или» стека через каждые 4096 байт с нулем (что ничего не делает) . Я предполагаю, что это для прикосновения к стеку и автоматического выделения памяти с ошибками страницы, если стек был зарезервирован? К сожалению, это происходит, даже если я устанавливаю начальную фиксацию стека (не резерва) на достаточно высокое значение, чтобы удерживать мой стек, не говоря уже о том, что это вообще не нужно. Избыточный код в лучшем виде.

Есть ли эти ошибки в GCC? Или я что-то пропустил в опциях? Должен ли я использовать что-то еще? Пожалуйста, сообщите мне, если я пропустил некоторые параметры.

Я серьезно надеюсь, что мне не придется создавать встроенный макрос asm только для вызова функций stdcall и использования инструкций push (и, я думаю, это также позволит избежать указателя кадра). Звучит чересчур для чего-то настолько простого, что должно быть в современных компиляторах. И да, я использую GCC 4.8.1, так что это не древняя версия.

В качестве дополнительного вопроса можно ли заставить GCC не сохранять регистры в стеке в прологе функции? Я использую свою собственную прямую точку входа с аргументом -nostartfiles, потому что это чисто Windows-приложение, и оно отлично работает без стандартного запуска библиотеки. Если я использую attribute((noreturn)), он отменит эпилог, восстанавливающий регистры, но все равно поместит их в стек в прологе, я не знаю, есть ли способ заставить его не сохранять регистры для этой функции точки входа. В любом случае, это не имеет большого значения, я думаю, это было бы более полным. Спасибо!


person kktsuri    schedule 25.07.2014    source источник


Ответы (2)


См. ответ Force GCC для помещения аргументов в стек перед вызовом функции (используя инструкцию PUSH)

т.е. попробуй -mpush-args -mno-accumulate-outgoing-args. Также может потребоваться -mno-stack-arg-probe, если gcc жалуется.

person mity    schedule 27.07.2014
comment
Я обновил GCC до 4.9.0, и кажется, что это не только работает, но и указатель кадра вообще больше не используется. Возможно, это была ошибка в 4.8.1. Решено сейчас, спасибо, надеюсь, это поможет кому-то еще. - person kktsuri; 28.07.2014

Похоже, что предоставление -mpush-args -mno-accumulate-outgoing-args -mno-stack-arg-probe работает, особенно последнее. Теперь код чище и более нормальный, как и другие компиляторы, и он использует PUSH для аргументов, что даже упрощает отслеживание в OllyDbg таким образом.

К сожалению, это ЗАСТАВЛЯЕТ использовать глупый указатель кадра даже в небольших функциях, которым он совершенно не нужен. Серьезно, есть ли способ полностью заставить GCC отключить указатель кадра?!

person kktsuri    schedule 27.07.2014