2009-03-01 5 views

ответ

27

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

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

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

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

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

В режиме x86-64, RIP можно считывать непосредственно с помощью RIP-относительная 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

+0

Ах, я люблю этот трюк. В ARM возникают проблемы с конвейерной обработкой при чтении ПК. Эта проблема присутствует и в процессорах Intel? – strager

+0

Нет, его нет на x86. (Btw - проблема конвейерной обработки ARM сумасшедшая) –

+0

В x86 у вас есть стоимость ветки, что намного хуже для конвейера, чем просто чтение ПК. PIC на ARM намного дешевле, чем x86, даже с странностями трубопровода. –

26

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

thisone: 
    mov (e)ax,thisone 

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

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

+0

Спасибо за информацию, действительно хорошая идея:) –

+0

Я пробовал это на x86-64 с встроенной сборкой gcc, но получил «ошибку в backend: 32-разрядная абсолютная адресация не поддерживается в 64-битном режиме» с использованием llvm 6.1.0. Является ли эта проблема LLVM или не возможна в 64-битном режиме? – csl

+1

@csl: либо вы находитесь на OS X (где нет обходного пути, кроме использования 'lea rax, [thisone]'), или вы используете Linux для создания общего объекта, и, следовательно, это тоже не сработает. ('mov'-instant требует постоянный адрес времени ссылки, поэтому он работает только в зависимом от положения коду.Но если вы создаете исполняемый файл Linux, [ваш компилятор может по умолчанию сделать независимый от позиции исполняемый файл, а '-no-pie -fno-pie' делает исполняемый файл, зависящий от положения, где вы можете использовать абсолютную адресацию] (https: //stackoverflow.com/questions/43367427/32-bit-absolute-addresses-no-longer-allowed-in-x86-64-linux). –

7

Нет инструкции для прямого считывания указателя инструкции (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). Столбец обратного адреса представляет собой отдельный стек обратных адресов, используемый внутренним процессором для облегчения branch target prediction для инструкций RET.

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

+0

Спасибо, вы, ребята, рок :) –

+0

Большое спасибо за информацию, я не знал, что было два стека .. :) –

+1

RAS - это внутренний стек, используемый процессором; он никак не доступен для кода. Он используется только для предсказания целевой ветви. Без него код все равно будет функционировать правильно, только медленнее. –

15

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

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

Спасибо! В чем смысл чисел? –

+0

thats кодировка команды - theres неявное 32-битное смещение, которое равно 0, я не уверен, есть ли более короткая кодировка – matja

+1

'lea rax, [rip]' не работает в NASM 2.10. Кажется, что RIP может использоваться косвенно только с 'rel', как в' lea rax, [rel _start] '? –

0

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

+0

Можете ли вы указать, где? Я не мог найти это легко. –

+0

В/proc/stat вы можете найти указатель на инструкцию (EIP) –

+0

Я думаю, вы имеете в виду '/ proc/self/stat', было бы здорово цитировать manpage. –

3

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

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

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

Это не совсем сборка :-) – hirschhornsalz

+0

это должно быть '&& current_address_label', а не' $$ ' –

+0

Разве это не относительный адрес (а не IP)? –

Смежные вопросы