Распечатать текущий адрес в программе на 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 может потребоваться временное хранение в памяти из-за ее сложности (не может вместить все, что нужно сделать в регистрах).

Куда я направляюсь, так это того, что нет ответа на ваш вопрос. Это сильно зависит от процессора, компилятора и т. Д. Нет общего ответа. Я должен задаться вопросом, в чем корень вопроса, если вы надеялись проверить с помощью отладчика, тогда этот вопрос не стоит задавать.

В итоге, разберите вашу программу и посмотрите на нее, для этой компиляции в тот день с этими настройками вы сможете увидеть, где компилятор поместил промежуточные значения. Даже если компилятор назначит место в памяти для переменной, это не означает, что программа будет когда-либо хранить переменную в этом месте. Это зависит от оптимизации.

Другие вопросы по тегам