Мне нужно получить значения в регистрах с помощью GCC.
Что-то похожее на это:
EAX=00000002 EBX=00000001 ECX=00000005 EDX=BFFC94C0 ESI=8184C544 EDI=00000000 EBP=0063FF78 ESP=0063FE3C CF=0 SF=0 ZF=0 OF=0
Получить 32-битные регистры достаточно просто, но я не уверен, какой самый простой способ получить флаги.
В примерах к этой книге: http://kipirvine.com/asm/
Они делают это, получая весь регистр EFLAGS и сдвигая соответствующий бит. Я также думал сделать это с помощью Jcc и CMOVcc.
Есть ли другие предложения о том, как это сделать? Также были бы полезны некоторые тестовые примеры для проверки.





Я думаю, что использовать Jcc было бы дольше и не так ясно, используя встроенную сборку.
Вот что у меня сейчас есть, используя CMOVcc:
void dump_regs()
{
int eax = 0;
int ebx = 0;
int ecx = 0;
int edx = 0;
int esi = 0;
int edi = 0;
int ebp = 0;
int esp = 0;
int cf = 0;
int sf = 0;
int zf = 0;
int of = 0;
int set = 1; // -52(%ebp)
asm(
"movl %eax, -4(%ebp)\n\t"
"movl %ebx, -8(%ebp)\n\t"
"movl %ecx, -12(%ebp)\n\t"
"movl %edx, -16(%ebp)\n\t"
"movl %esi, -20(%ebp)\n\t"
"movl %edi, -24(%ebp)\n\t"
"movl %ebp, -28(%ebp)\n\t"
"movl %esp, -32(%ebp)\n\t"
"movl $0, %eax\n\t"
"cmovb -52(%ebp),%eax\n\t" // mov if CF = 1
"movl %eax, -36(%ebp) \n\t" // cf
"movl $0, %eax\n\t"
"cmovs -52(%ebp),%eax\n\t" // mov if SF = 1
"movl %eax, -40(%ebp)\n\t" // sf
"movl $0, %eax\n\t"
"cmove -52(%ebp),%eax\n\t" // mov if ZF = 1
"movl %eax, -44(%ebp)\n\t" // zf
"movl $0, %eax\n\t"
"cmovo -52(%ebp),%eax\n\t" // mov if OF = 1
"movl %eax, -48(%ebp)\n\t" // of
"movl -4(%ebp), %eax\n\t" // restore EAX
);
printf("EAX = %#08x\tEBX = %#08x\tECX = %#08x\tEDX = %#08x\n",eax,ebx,ecx,edx);
printf("ESI = %#08x\tEDI = %#08x\tEBP = %#08x\tESP = %#08x\n",esi,edi,ebp,esp);
printf("CF = %d\tSF = %d\tZF = %d\tOF = %d\n",cf,sf,zf,of);
}
Одна важная вещь, которую я еще не разработал, - это побочные эффекты, я хочу иметь возможность вызывать это, не нарушая состояние, любые советы в этом направлении приветствуются.
Из макушки головы и поправьте меня, если я ошибаюсь, но вы могли бы просто выделить некоторую память, получить выделенный адрес и просто записать туда содержимое регистра с помощью скобок asm ... Или вы можете просто засунуть его в стек и как-нибудь прочитать вручную ... Я предполагаю, что потребуется хороший asm-код, и это, вероятно, не идеальный способ сделать что-то подобное, но это сработает.
Нет необходимости использовать ассемблер только для получения регистров.
Вы можете просто использовать setjmp. Это запишет все регистры в структуру типа jmp_buf. Это даже своего рода кросс-платформа Works, за исключением того факта, что сам jmp_buf отличается для каждой архитектуры.
Однако вызов setjmp (а также вызов вашего кода ассемблера) изменит некоторые регистры, поэтому вы не можете им доверять.
Есть способ получить настоящий снимок, но это немного сложнее и сильно зависит от ОС:
установить обработчик исключений для незаконного расширения недопустимого кода операции. Обработчик может быть реальным прерыванием, обработчиком сигнала или обработчиком исключений ОС (блоки try / except из C++ работать не будут).
Введите недопустимый код операции в свой код.
Хитрость здесь в том, что недопустимый код операции не имеет побочных эффектов регистров. Обработчик исключений может копировать регистры либо из стека, либо из структуры информации об исключении.
Тот же трюк может работать с прерываниями от точки останова, принудительными переполнениями, ловушками и т. д. Обычно существует несколько способов вызвать прерывание из фрагмента кода.
Что касается EFLAGS: вы можете получить их с помощью операции стека:
PUSHFD
POP EAX
, eax now contains the EFLAG data
Я подумал о том, чтобы получить весь регистр EFLAGS, но тогда весь SH * для получения правильного бита сделает менее понятным, что происходит. Подобно тому, что я собирался сделать с Jcc's.
ИМХО, использовать gdb лучше, чем gcc.
http://www.unknownroad.com/rtfm/gdbtut/gdbadvanced.html
HTH
Это было полезно! Это помогло мне убедиться, что то, что у меня было, было правильным. Спасибо!
Следующее было протестировано на 64-битной машине. Если у вас 32-битная машина, удалите 64-битную шестеренку и измените flag64 -> flag32 (и используйте pushfd вместо pushfq). На практике я считаю, что мне нужно только проверять CY (перенос) и OV (переполнение) из регистра флагов (и я обычно делаю это с jc, jnc, jo и jno).
#include <stdio.h>
#include <stdint.h>
#define HIGH32(x) ((uint32_t)(((uint64_t)x)>>32))
#define LOW32(x) ((uint32_t)(((uint64_t)x)& 0xFFFFFFFF))
int main(int argc, char** argv)
{
uint32_t eax32, ebx32, ecx32, edx32;
uint64_t rax64, rbx64, rcx64, rdx64;
asm (
"movl %%eax, %[a1] ;"
"movl %%ebx, %[b1] ;"
"movl %%ecx, %[c1] ;"
"movl %%edx, %[d1] ;"
"movq %%rax, %[a2] ;"
"movq %%rbx, %[b2] ;"
"movq %%rcx, %[c2] ;"
"movq %%rdx, %[d2] ;"
:
[a1] "=m" (eax32), [b1] "=m" (ebx32), [c1] "=m" (ecx32), [d1] "=m" (edx32),
[a2] "=m" (rax64), [b2] "=m" (rbx64), [c2] "=m" (rcx64), [d2] "=m" (rdx64)
);
printf("eax=%08x\n", eax32);
printf("ebx=%08x\n", ebx32);
printf("ecx=%08x\n", ecx32);
printf("edx=%08x\n", edx32);
printf("rax=%08x%08x\n", HIGH32(rax64), LOW32(rax64));
printf("bax=%08x%08x\n", HIGH32(rbx64), LOW32(rbx64));
printf("cax=%08x%08x\n", HIGH32(rcx64), LOW32(rcx64));
printf("dax=%08x%08x\n", HIGH32(rdx64), LOW32(rdx64));
uint64_t flags;
asm (
"pushfq ;"
"pop %[f1] ;"
:
[f1] "=m" (flags)
);
printf("flags=%08x%08x", HIGH32(flags), LOW32(flags));
if (flags & (1 << 0)) // Carry
printf(" (C1");
else
printf(" (C0");
if (flags & (1 << 2)) // Parity
printf(" P1");
else
printf(" P0");
if (flags & (1 << 4)) // Adjust
printf(" A1");
else
printf(" A0");
if (flags & (1 << 6)) // Zero
printf(" Z1");
else
printf(" Z0");
if (flags & (1 << 7)) // Sign
printf(" S1");
else
printf(" S0");
if (flags & (1 << 11)) // Overflow
printf(" O1)\n");
else
printf(" O0)\n");
return 0;
}
Не очень понятно, как восстановить регистры из структуры jmp_buf. Я нашел источник здесь: ccrma.stanford.edu/courses/250a/docs/avrgcc/… Есть идеи по созданию кода, который не изменяет регистры? Некоторые PUSH могут помочь ...