Распечатать текущий адрес в программе на C
Представьте, что у меня есть следующая простая программа на C:
int main() {
int a=5, b= 6, c;
c = a +b;
return 0;
}
Теперь я хотел бы знать адрес выражения c=a+b, то есть адрес программы, где выполняется это добавление. Есть ли возможность, что я мог бы использовать printf? Что-то в этом роде:
int main() {
int a=5, b= 6, c;
printf("Address of printf instruction in memory: %x", current_address_pointer_or_something)
c = a +b;
return 0;
}
Я знаю, как найти адрес, используя gdb, а затем информационную строку file.c: line. Тем не менее, я должен знать, могу ли я сделать это напрямую с printf.
8 ответов
В gcc вы можете взять адрес метки с помощью оператора &&. Так что вы можете сделать это:
int main()
{
int a=5, b= 6, c;
sum:
c = a+b;
printf("Address of sum label in memory: %p", &&sum);
return 0;
}
Результатом && sum является цель команды перехода, которая будет отправлена, если вы выполнили команду goto sum
, Таким образом, хотя в C/C++ нет однозначного сопоставления адресов и строк, вы все равно можете сказать "дайте мне указатель на этот код".
Visual C++ имеет встроенную функцию _ReturnAddress, которую можно использовать для получения некоторой информации здесь.
Например:
__declspec(noinline) void PrintCurrentAddress()
{
printf("%p", __ReturnAddress);
}
Который даст вам адрес, близкий к выражению, на которое вы смотрите. В случае некоторых оптимизаций, таких как складывание хвоста, это не будет надежным.
Протестировано в Visual Studio 2008:
int addr;
__asm
{
call _here
_here: pop eax
; eax now holds the PC.
mov [addr], eax
}
printf("%x\n", addr);
Отдайте этому вопросу.
Для x86:
int test()
{
__asm {
mov eax, [esp]
}
}
__declspec(noinline) int main() // or whatever noinline feature your compiler has
{
int a = 5;
int aftertest;
aftertest = test()+3; // aftertest = disasms to 89 45 F8 mov dword ptr [a],eax.
printf("%i", a+9);
printf("%x", test());
return 0;
}
Вот набросок альтернативного подхода:
Предположим, что вы не удалили символы отладки, и, в частности, у вас есть номер строки для таблицы адресов, которая необходима символическому отладчику на уровне источника для реализации таких вещей, как пошаговая строка исходного кода, установка точки останова на исходной строке, и так далее.
Большинство цепочек инструментов используют достаточно хорошо документированные форматы отладочных данных, и часто есть вспомогательные библиотеки, которые реализуют большинство деталей.
Учитывая это и некоторую помощь от макроса препроцессора __LINE__
который оценивает текущий номер строки, должно быть возможно написать функцию, которая ищет адрес любой строки источника.
Преимущества в том, что сборка не требуется, переносимость может быть достигнута путем вызова библиотек отладочной информации для конкретной платформы, и нет необходимости напрямую манипулировать стеком или использовать приемы, которые нарушают конвейер ЦП.
Большим недостатком является то, что он будет медленнее любого подхода, основанного на непосредственном считывании счетчика программы.
Я не знаю деталей, но должен быть способ сделать вызов функции, которая может затем просканировать возвращаемый стек по адресу вызывающего, а затем скопировать и распечатать его.
Используя gcc на i386 или x86-64:
#include <stdio.h>
#define ADDRESS_HERE() ({ void *p; __asm__("1: mov 1b, %0" : "=r" (p)); p; })
int main(void) {
printf("%p\n", ADDRESS_HERE());
return 0;
}
Обратите внимание, что из-за наличия оптимизации компилятора кажущаяся позиция выражения может не соответствовать его позиции в исходном источнике.
Преимущество использования этого метода перед методом &&foo label заключается в том, что он не меняет график потока управления функции. Он также не нарушает модуль предиктора возврата, как подходы, использующие вызов:) С другой стороны, он очень сильно зависит от архитектуры... и поскольку он не нарушает CFG, нет гарантии, что переход к рассматриваемому адресу будет иметь какой-либо смысл вообще.
Если компилятор хорош, это добавление происходит в регистрах и никогда не сохраняется в памяти, по крайней мере, не так, как вы думаете. На самом деле хороший компилятор увидит, что ваша программа ничего не делает, манипулируя значениями внутри функции, но никогда не отправляя эти значения где-либо за пределами функции, это может привести к отсутствию кода.
Если бы вы были:
с = а + б; Е ("% и \ п", с);
Тогда хороший компилятор также никогда не сохранит это значение C в памяти, оно останется в регистрах, хотя это также зависит от процессора. Если, например, компиляторы для этого процессора используют стек для передачи переменных в функции, тогда значение для c будет вычисляться с использованием регистров (хороший компилятор увидит, что C всегда равно 11, и просто назначит его), а значение будет помещено в стек при отправке в функцию printf. Естественно, функции printf может потребоваться временное хранение в памяти из-за ее сложности (не может вместить все, что нужно сделать в регистрах).
Куда я направляюсь, так это того, что нет ответа на ваш вопрос. Это сильно зависит от процессора, компилятора и т. Д. Нет общего ответа. Я должен задаться вопросом, в чем корень вопроса, если вы надеялись проверить с помощью отладчика, тогда этот вопрос не стоит задавать.
В итоге, разберите вашу программу и посмотрите на нее, для этой компиляции в тот день с этими настройками вы сможете увидеть, где компилятор поместил промежуточные значения. Даже если компилятор назначит место в памяти для переменной, это не означает, что программа будет когда-либо хранить переменную в этом месте. Это зависит от оптимизации.