Как узнать количество аргументов при реализации backtrace в C - PullRequest
2 голосов
/ 04 марта 2012

Я реализую функцию обратного отслеживания в C, которая может выводить информацию о вызывающем абоненте.вот так

ebp:0x00007b28 eip:0x00100869 args:0x00000000 0x00640000 0x00007b58 0x00100082

Но как мне узнать количество аргументов звонящего?

Ответы [ 4 ]

2 голосов
/ 04 марта 2012

Вы можете вывести количество аргументов, которые функция использует в 32-битном коде x86 при некоторых обстоятельствах.
Если код был скомпилирован для использования framepointers , тогда стековый фрейм данной функции простирается между (самый высокий адрес) EBP и (самый низкий адрес / вершина стека) ESP.Сразу над концом стека в EBP вы найдете обратный адрес , и снова выше, чем у вас, если ваш код использует соглашение о вызовах C (cdecl), последовательно, arg[0...].

Это означает: arg[0] в [EBP + 4], arg[1] в [EBP + 8 ] и т. д.

Когда вы разбираете функцию, ищите инструкцииссылаясь на [EBP + ...], и вы знаете, что они получают доступ к аргументам функции.Максимальное используемое значение смещения говорит вам, сколько их.

Это, конечно, несколько упрощено;Аргументы с размерами, отличными от 32-битных, код, который не использует cdecl, например, fastcall, код, в котором был оптимизирован указатель кадра, вызывает отключение метода, по крайней мере, частично.

Другой вариант, опять же для cdecl функции, это посмотреть на обратный адрес (расположение call в интересующем вас функционале) и разобрать там;во многих случаях вы найдете последовательность push argN; push ...; push arg0; call yourFunc и сможете определить, сколько аргументов было передано в этом случае.Фактически это единственный способ (из одного кода) проверить, сколько аргументов было передано функциям, таким как printf() в конкретном случае.Опять же, не идеально - в наши дни компиляторы часто предварительно выделяют пространство стеков, а затем используют mov для записи аргументов вместо их выдвижения (на некоторых процессорах это лучше, поскольку последовательности команд push имеют зависимости друг от друга из-за каждого измененияуказатели стека).

Поскольку все эти методы эвристические, для автоматизации требуется немало кодирования.Если сгенерированная компилятором отладочная информация доступна, используйте ее - это быстрее.

Редактировать: Есть еще одна полезная эвристика, которую можно сделать;Генерируемый компилятором код для вызова функции часто выглядит следующим образом:

...
[ code that either does "push arg" or "mov [ESP ...], arg" ]
...
call function
add  ESP, ...

Инструкция add предназначена для очистки стека, используемого для аргументов .Из размера непосредственного операнда вы знаете, сколько места заняли аргументы, которые этот код дал function, и косвенно (если предположить, что они все 32-битные, например), вы знаете, сколько их было.
Это особенно просто, учитывая, что у вас уже есть адрес указанной add инструкции, если у вас есть рабочий код возврата - инструкция на обратном адресе это add.Таким образом, вы часто можете просто попытаться разобрать (одиночную) инструкцию по адресу возврата и посмотреть, является ли она add ESP, ... (иногда это sub ESP, -...), и если это так, вычислить количество аргументов, переданных из непосредственногооперанд.Код для этого гораздо проще, чем загружать полную библиотеку дизассемблирования.

1 голос
/ 04 марта 2012

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

    f(5);
002B144E  push        5  
002B1450  call        f (2B11CCh)  
002B1455  add         esp,4  
    g(1, "foo");
002B1458  push        offset string "foo" (2B5740h)  
002B145D  push        1  
002B145F  call        g (2B11C7h)  
002B1464  add         esp,8  
    h("bar", 'd', 8);
002B1467  push        8  
002B1469  push        64h  
002B146B  push        offset string "bar" (2B573Ch)  
002B1470  call        h (2B11D1h)  
002B1475  add         esp,0Ch  

По сути, только вызываемая функция знает, сколько аргументов у нее есть.

0 голосов
/ 04 марта 2012

Glibc реализует функцию backtrace . Это раскручивание обратный ход, arg. Arg.

Вы можете увидеть, как они это сделали в sysdeps/$ARCH/backtrace.c. Остерегайтесь, что это довольно трудно читать.

0 голосов
/ 04 марта 2012

Как прокомментировал Яхья, общего пути нет.

Возможно, вам потребуется проанализировать отладочную информацию, размещенную отладчиком (при условии, что вы скомпилировали с gcc -g).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...