Прямой просмотр программы

Может ли счетчик программ на процессорах Intel считываться напрямую (без "трюков" ) в режиме ядра или в каком-либо другом режиме?

Ответ 1

Нет, к EIP/IP нельзя получить доступ напрямую, но в зависимом от кода коде это константа времени ссылки, поэтому вы можете использовать ближайший (или удаленный) символ как немедленный.

Чтобы получить EIP или IP в независимом от нуля коде:

call _here
_here: pop eax
; eax now holds the PC.

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

get_retaddr:
    mov  eax, [esp]
    ret                ; keeps the return-address predictor stack balanced

В режиме x86-64 RIP можно читать напрямую, используя RIP-relative lea.

default rel           ; NASM directive: use RIP-relative by default

lea  rax, [_here]     ; RIP + 0
_here:

MASM: lea rax, [rip]

Синтаксис AT & T: lea 0(%rip), %rax

Ответ 2

Если вам нужен адрес конкретной инструкции, обычно что-то вроде этого делает трюк:

thisone: 
   mov (e)ax,thisone

(Примечание. На некоторых ассемблерах это может сделать неправильное дело и прочитать слово из [thisone], но обычно есть некоторый синтаксис, чтобы заставить ассемблер поступать правильно.)

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

Ответ 3

На x86-64 вы можете сделать, например:

lea rax,[rip] (48 8d 05 00 00 00 00)

Ответ 4

Нет инструкции для прямого чтения указателя инструкции (EIP) на x86. Вы можете получить адрес текущей команды, собранной с небольшой встроенной сборкой:

// GCC inline assembler; for MSVC, syntax is different
uint32_t eip;
__asm__ __volatile__("movl $., %0", : "=r"(eip));

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

// In a C header file:
uint32_t get_eip(void);

// In a separate assembly (.S) file:
.globl _get_eip
_get_eip:
    mov 0(%esp), %eax
    ret

Это означает, что каждый раз, когда вы хотите получить указатель на инструкцию, он немного менее эффективен, так как вам нужен дополнительный вызов функции. Обратите внимание, что выполнение этого способа не удаляет стек обратного адреса (RAS). Стек обратного адреса представляет собой отдельный стек обратных адресов, используемый внутренним процессором, чтобы облегчить предсказание ветвления ветки для инструкций RET.

Каждый раз, когда у вас есть инструкция CALL, текущий EIP попадает в RAS, и каждый раз, когда у вас есть команда RET, RAS выставляется, а верхнее значение используется как предсказание целевой ветки для этой команды. Если вы испортите RAS (например, не сопоставляя каждый CALL с RET, как в решении Cody), вы получите целую кучу ненужной ветки неправильные предсказания, замедляющие вашу программу. Этот метод не удаляет RAS, так как он имеет пару команд CALL и RET.

Ответ 5

Существует независимый от архитектуры (но gcc-зависимый) способ доступа к адресу, который выполняется с использованием меток в качестве значений:

http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html

void foo()
{
  void *current_address = $$current_address_label;
  current_address_label:
      ....
}

Ответ 6

Вы также можете прочитать это из /proc/stat. Проверьте файлы proc.