Intereting Posts
Как динамически выделять двумерный массив Поиск циклического вектора одной транспозиции в C Можно ли загрузить стороннюю .dll в R? CUDA + count int element встречается с использованием C Получите ошибку сегмента при чтении файла C или C ++ для использования LAME API для преобразования M4A (аудио MPEG-4) в MP3 Невозможное ограничение в ошибке «asm» и косвенный вызов без предупреждения «*» Об использовании знаковых целых чисел в семействе языков C Программа, которая читает файл и отправляет его в родительский процесс с помощью канала Оценка следующего выражения Как установить переменную среды пути в linux в коде языка C Buildroot: создайте только один пакет как общий, так и статический lib, все остальные доступны только MPI – печать в заказе Как преобразовать шестнадцатеричное значение, содержащееся в символе (байт), в целое число? hscurses или ncurses, какой из них использовать?

соглашения о вызовах x86_64 и кадры стека

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

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 создает обратную линию?

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

Благодарю.

Я считаю, что разница в том, что опускание указателя на фрейм просто более приветствуется в amd64. Сноска на странице 16 аби говорит

Обычное использование% rbp в качестве указателя кадра для фрейма стека можно избежать, используя% rsp (указатель стека) для индексации в стек стека. Этот метод сохраняет две инструкции в prologе и эпилоге и делает доступным один дополнительный регистр общего назначения (% rbp).

Я не знаю, что делает GDB. Я предполагаю, что при компиляции с -g объекты имеют волшебную отладочную информацию, которая позволяет GDB восстанавливать то, что ему нужно. Я не думаю, что пробовал GDB на 64-битной машине без отладки информации.

GDB использует DWARF CFI для размотки. Для нерасширенных двоичных файлов, скомпилированных с помощью -g, это будет в разделе .debug_info. Для разделенных бинарных файлов x86-64 в разделе .eh_frame есть информация об отключении. Это определено в аббревиатуре x86-64 ABI , раздел 3.7, стр. 56. Обработка этой информации сама по себе довольно сложна, так как parsing DWARF очень связан, но я считаю, что libunwind поддерживает его.

Если адрес argv – это то, что вы хотите, почему бы просто не сохранить указатель на него в основном?
Попытка раскрутить стопку была бы крайне неспокойной, даже если вы ее заработаете.
Даже если вам удастся вернуться через стек, не очевидно, что указатель фрейма первой функции будет NULL. Первая функция в стеке не возвращается, но вызывает системный вызов для выхода, поэтому его указатель кадра никогда не используется. Нет никакой веской причины, почему он будет инициализирован NULL.

Предполагая, что я связываюсь с glibc (что я делаю), похоже, что я могу решить эту проблему для практических целей с помощью глобального символа glibc __libc_stack_end:

 extern void * __libc_stack_end; void myfunction(void) { /* ... */ off_t stack_hi = (off_t)__libc_stack_end; /* ... */ }