Соглашения о вызовах x86_64 и фреймы стека

Я пытаюсь понять смысл исполняемого кода, который GCC (4.4.3) генерирует для машины x86_64, работающей под Ubuntu Linux. В частности, я не понимаю, как код отслеживает фреймы стека. Раньше в 32-битном коде я привык видеть этот «пролог» практически в каждой функции:

push %ebp
movl %esp, %ebp

Затем, в конце функции, следовало «эпилог», либо

sub $xx, %esp   # Where xx is a number based on GCC's accounting.
pop %ebp
ret

или просто

leave
ret

, который выполняет то же самое:

  • Установите указатель стека в верхнюю часть текущего кадра, чуть ниже адрес возврата
  • Восстановить старое значение указателя кадра.

В 64-битном коде, как я вижу это через разборку objdump, многие функции не следуют этому соглашению - они не помещают% rbp, а затем сохраняют% rsp в% rbp. Как отладчик, такой как GDB, создает backtrace?

Моя настоящая цель здесь - попытаться найти разумный адрес, который будет рассматриваться как верхний (самый высокий адрес) пользовательского стека, когда выполнение достигает начала произвольной функции дальше в программе, где, возможно, стек Указатель переместился вниз. Например, для "вершины" исходный адрес argv был бы идеальным, но у меня нет доступа к нему из произвольной функции, которую вызывает main. Сначала я подумал, что могу использовать старый метод обратной трассировки: поиск сохраненных значений указателя кадра до тех пор, пока сохраненное значение не станет 0 - тогда следующее после этого может считаться наивысшим практическим значением. (Это не то же самое, что получение адреса argv, но сойдет - скажем, чтобы узнать значение указателя стека при _start или в любом другом вызове _start [например, __libc_start_main].) Теперь я не знаю, как получить эквивалентный адрес в 64-битном коде.

Спасибо.

13
задан Amittai Aviram 24 December 2011 в 15:28
поделиться