Почему в х64 АПИ бьют стек?

Тема в разделе "WASM.X64", создана пользователем Magnum, 10 дек 2011.

  1. Magnum

    Magnum New Member

    Публикаций:
    0
    Регистрация:
    29 дек 2007
    Сообщения:
    925
    В х64 соглашение __fastcall.
    Аргументы в апи передаются через регистры (rcx, rdx, r8, r9), потом через стек.
    Я беру АПИ из кернел32 в windows7.
    Загоняю аргументы в стек.

    Код (Text):
    1. ;;;;;;;; PUSH REGS ;;;;;;;;;;;;
    2. xor rcx, rcx
    3. xor rax, rax
    4. push rax
    5. push rcx
    6.  
    7. ;;;;;;;; API CALLING ;;;;;;;;;;;;
    8. mov rcx, [arg1]
    9. mov rdx, [arg2]
    10. mov r8, [arg3]
    11. mov r9, [arg4]
    12. call VirtualProtect  ; после вызова rbp и rsp те же, что и до вызова
    13.  
    14. ;;;;;;;; POP REGS ;;;;;;;;;;;;;;;
    15. pop rax
    16. pop rcx
    Объясните, почему из стека достаются уже не нулевые регистры?
    Я отладчиком прошелся по функции, там действительно бьется стек.
    На прологе функции код вида:

    Код (Text):
    1. push rbp
    2. mov rbp, rsp
    3. mov [rbp+0x18], rcx
    Насколько я понимаю, в х64 теперь по другому со стеком работать нужно.
    Расскажите, где можно прочитать по устройство стека в х64 и как теперь с ним работать, если перед вызовом АПИ нужно сохранить пару регистров?
     
  2. dermatolog

    dermatolog Member

    Публикаций:
    0
    Регистрация:
    3 фев 2005
    Сообщения:
    406
    Адрес:
    Екатеринбург
    Код (Text):
    1. sub rsp, 0x20
    2. call VirtualProtect
    3. add rsp, 0x20
    Читай про calling convention для х64
     
  3. Magnum

    Magnum New Member

    Публикаций:
    0
    Регистрация:
    29 дек 2007
    Сообщения:
    925
    dermatolog
    Я читал, но все никак не мог понять, что такое stack-backing.
    На примере вашего кода понял.

    Т.е., если АПИ принимает 3 аргумента, то перед вызовом нужно делать вот так: sub rsp, _ALIGNUP(8*3, 0x10) Или выравнивание не обязательно в данном случае?

    И наверное глупый вопрос, но для чего это сделали? Зачем фаст-кол, если все-равно стек юзается..?
     
  4. Magnum

    Magnum New Member

    Публикаций:
    0
    Регистрация:
    29 дек 2007
    Сообщения:
    925
    И еще. Я так понимаю, далеко не все компиляторы знают про stack-backing?
    stack-backing применим для всех _fastcall функций в х64, либо только для системных АПИ?
     
  5. ziral2088

    ziral2088 New Member

    Публикаций:
    0
    Регистрация:
    16 авг 2009
    Сообщения:
    283
     
  6. s_d_f

    s_d_f New Member

    Публикаций:
    0
    Регистрация:
    15 май 2008
    Сообщения:
    342
    Конвенция x64 более всего похоже на 32-битную Cишную.
    А так на практике лучше не делать.
    Код (Text):
    1. sub rsp, 0x20
    2. call VirtualProtect
    3. add rsp, 0x20
    Перед call`ом в рантайме стэк должен быть выровнен на границу 16 байт, и даже про push/pop`ы вне пролога/эпилога лучше забыть.
     
  7. Magnum

    Magnum New Member

    Публикаций:
    0
    Регистрация:
    29 дек 2007
    Сообщения:
    925
    s_d_f
    Не. _cdecl x32 - стек не бьет.
    Там тот же _stdcall х32, только стек не очищается. После вызова к esp добавляем (ArgsNum*4).
    А тут странности (для меня).

    Так я и не юзаю push/pop. Это просто как пример.
    На деле голая функция и внутри функции вызов АПИ. После вызова бьется стек. А если делать, как сказал dermatolog, то стек не бьется.

    А бага всплыла, потому что не все компиляторы знают о stack-backing. Это уже выяснил.
    А так не хочется использовать классический студийный компилятор :dntknw:
     
  8. rpy3uH

    rpy3uH New Member

    Публикаций:
    0
    Регистрация:
    14 сен 2006
    Сообщения:
    503
    при вызове х64-fastcall функции всегда подразумевается что ей передаётся 4 параметра. Даже если ей передаётся меньше четырёх, то ВСЁ РАВНО надо зарезервировать в стеке место под 4 параметра.

    чтобы функция могла сохранить параметры из rcx rdx r8 r9 в стек и вызвать другую функцию

    fastcall это только название, не более, никакой быстроты не подразумевается, хотя в некоторых случаях вызов происходит действительно быстрее чем stdcall. это сделано для унификации и стандартизации вызовов функций. к тому же резервирование места происходит обычной sub rsp, 32, а не 4хpush, изменить значение регистра в разы быстрее чем запихать в стек 4 параметра по 8 байт

    в win32 полный беспорядок по этой части, в win64 решили таким образом решить эту проблему раз и навсегда
     
  9. Magnum

    Magnum New Member

    Публикаций:
    0
    Регистрация:
    29 дек 2007
    Сообщения:
    925
    Т.е. в простейшем случае свои функции на асме нужно оформлять так:

    1. открываем фрейм
    2. сохраняем регистры в стек
    2. дополнительно отнимаем 0x20 от rsp
    3. ..... тут идет тело функции с вызовами АПИ ......
    4. прибавляем к rsp 0х20
    5. достаем регистры
    6. закрываем фрейм
    7. возвращаем управление
     
  10. rpy3uH

    rpy3uH New Member

    Публикаций:
    0
    Регистрация:
    14 сен 2006
    Сообщения:
    503
    сохранять и доставать их надо только в том случае еслио ни нужны для работы. соглашение x64-fatcall не предсматривает обязательное сохранение регистров RCX, RDX, R8-R11
     
  11. s_d_f

    s_d_f New Member

    Публикаций:
    0
    Регистрация:
    15 май 2008
    Сообщения:
    342
    А что значит бьет, не бьет?
    Сохранение rcx, rdx, r8, r9.
    Так место под них должно еще в прологе выделяться, во всяком случае так делает Cишный компилятор.
     
  12. dermatolog

    dermatolog Member

    Публикаций:
    0
    Регистрация:
    3 фев 2005
    Сообщения:
    406
    Адрес:
    Екатеринбург
    s_d_f
    Вы бы умничали поменьше что-ли. Человеку нужно для начала со стеком разобраться, а вы его начинаете грузить про раскрутку стека при исключениях.
     
  13. s_d_f

    s_d_f New Member

    Публикаций:
    0
    Регистрация:
    15 май 2008
    Сообщения:
    342
    Не понял. О чем это вы?
     
  14. dermatolog

    dermatolog Member

    Публикаций:
    0
    Регистрация:
    3 фев 2005
    Сообщения:
    406
    Адрес:
    Екатеринбург
    s_d_f
    Про "и даже про push/pop`ы вне пролога/эпилога лучше забыть.". Почему про них нужно забыть?
     
  15. s_d_f

    s_d_f New Member

    Публикаций:
    0
    Регистрация:
    15 май 2008
    Сообщения:
    342
    После нескольких push/pop`ов трудно сказать выровнен стэк на 16 байт или не выровнен, а конвенция этого требует.
    Первый-же вызов АПИ может все сохранения затереть.
     
  16. dermatolog

    dermatolog Member

    Публикаций:
    0
    Регистрация:
    3 фев 2005
    Сообщения:
    406
    Адрес:
    Екатеринбург
    s_d_f
    Перед тем как писать сюда всякую ерунду рекомендую ознакомиться с матчастью. Итак попорядку:
    Выравнивание стека на границу 16-ти байт совершенно не запрещает нам делать sub/add rsp, 0x20 вокруг вызова API (можете проверить это на практике). Все работает, не правда ли?
    Затереть где? В вершине стека? А 4 QWORD-а там как раз и сделаны для того чтобы его использовал вызываемый код (тот который мы вызываем) и логично полагать что в вызывающем коде эту область на стеке нельзя использовать под хранение каких либо данных. Вы видимо думаете как раз с точностью до наоборот.

    Ну а теперь на самое вкусное я вам подброшу еще одну тему, над которой вы можете подумать на досуге - почему в реальном коде действительно не используются push/pop кроме как в прологе а в эпилоге. Это все связано исключительно с обработкой исключений и с раскруткой стека, т.к. компилятор создается специальные структуры на уровне РЕ, которые парсятся операционкой на этапе обработки исключений и предполагается, что никто крое пролога и эпилога со стеком не балуется.

    Так что перед тем как умничать на форуме не плохобы почитать матчасть.
     
  17. s_d_f

    s_d_f New Member

    Публикаций:
    0
    Регистрация:
    15 май 2008
    Сообщения:
    342
    К каждому call`у add/sub цеплять. И это разве оптимально по скорости и по размеру кода?

    Да. Но add/sub`ами вы этого избегаете.

    Про это я не знал. Обработкой исключений не занимался, кроме как VEH`ами.
    Я и прочитал необходимый минимум.
     
  18. rpy3uH

    rpy3uH New Member

    Публикаций:
    0
    Регистрация:
    14 сен 2006
    Сообщения:
    503
    более, того стек можно вообще не выравнивать, и всё равно программа будет работать. это не троллинг, просто к слову
     
  19. s_d_f

    s_d_f New Member

    Публикаций:
    0
    Регистрация:
    15 май 2008
    Сообщения:
    342
    Да что-ж тут из себя все умников мнят.
    Без выравнивания MessageBox и то на #AC загнется.
     
  20. rpy3uH

    rpy3uH New Member

    Публикаций:
    0
    Регистрация:
    14 сен 2006
    Сообщения:
    503
    я говорю про выравнивание на 8 байт. я не говорю про то что не выравнивать на 16, а просто если оставить как есть (не делать sub rsp, 8 в точке входа) то всё равно программа будет работать. но MessageBox я не тестил поэтому ничего сказать не могу