Подтвердить что ты не робот

Печать текущего адреса в программе 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, а затем info line file.c: line. Тем не менее, я должен знать, могу ли я сделать это непосредственно с printf.

4b9b3361

Ответ 1

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

Ответ 2

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

Например:

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

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

Ответ 3

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

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

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

Подтвердите этот вопрос.

Ответ 4

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

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

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

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

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

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

Ответ 5

Для 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;
}

Ответ 6

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

Ответ 7

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

Ответ 8

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

Если вам нужно:

c = a + b; Е ( "% и\​​п", с);

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

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

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