Распечатать текущий адрес в программе на C - PullRequest
11 голосов
/ 15 мая 2009

Представьте, что у меня есть следующая простая программа на 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 ]

23 голосов
/ 15 мая 2009

В 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 ++ нет однозначного сопоставления адресов и строк, вы все равно можете сказать «дайте мне указатель на этот код».

6 голосов
/ 15 мая 2009

Visual C ++ имеет встроенную функцию _ReturnAddress, которую можно использовать для получения некоторой информации здесь.

Например:

__declspec(noinline) void PrintCurrentAddress()
{
    printf("%p", __ReturnAddress);
}

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

2 голосов
/ 15 мая 2009

Протестировано в Visual Studio 2008:

int addr;
__asm
{
    call _here
    _here: pop eax
    ; eax now holds the PC.
    mov [addr], eax
}

printf("%x\n", addr);

Кредит на этот вопрос .

1 голос
/ 15 мая 2009

Для 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;
}
1 голос
/ 15 мая 2009

Вот эскиз альтернативного подхода:

Предположим, что вы не удалили символы отладки, и, в частности, у вас есть номер строки для таблицы адресов, которая нужна символическому отладчику на уровне источника для реализации таких вещей, как пошаговая строка исходного кода, установите точку останова на исходная строка и пр.

Большинство цепочек инструментов используют достаточно хорошо документированные форматы отладочных данных, и часто есть вспомогательные библиотеки, которые реализуют большинство деталей.

Учитывая это и некоторую помощь от макроса препроцессора __LINE__, который оценивает текущий номер строки, должна быть возможность написать функцию, которая ищет адрес любой строки источника.

Преимущества в том, что сборка не требуется, переносимость может быть достигнута путем вызова библиотек отладочной информации для конкретной платформы, и нет необходимости напрямую манипулировать стеком или использовать приемы, которые нарушают конвейер ЦП.

Большим недостатком является то, что он будет медленнее любого подхода, основанного на непосредственном считывании счетчика программы.

0 голосов
/ 24 мая 2009

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

Если бы вы:

с = а + б; Е ( "% и \ ​​п", с);

Тогда хороший компилятор также никогда не сохранит это значение C в памяти, он останется в регистрах, хотя это также зависит от процессора. Если, например, компиляторы для этого процессора используют стек для передачи переменных в функции, тогда значение для c будет вычисляться с использованием регистров (хороший компилятор увидит, что C всегда равно 11, и просто назначит его), а значение будет помещено в стек при отправке в функцию printf. Естественно, функции printf может потребоваться временное хранение в памяти из-за ее сложности (не может вместить все, что нужно сделать в регистрах).

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

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

0 голосов
/ 15 мая 2009

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

0 голосов
/ 15 мая 2009

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

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