无法获得正确的 x86_64 CPU 寄存器值

问题描述 投票:0回答:0

我有一个用 C++ 开发游戏引擎的项目。我希望我的游戏引擎能够报告错误以及转储内存并获取 CPU 寄存器的值,将它们打印到屏幕上,然后将它们发送给开发人员(即我),以调查并修复错误。 .在我们进入引擎的核心之前,我想先做这个调试器实现。

想法是当引擎退出或中止时我会注册一个跟踪器。我也实现了接口函数,一切正常

但是,当我尝试获取CPU寄存器的值时,出现了一个小问题。我为此使用内联汇编。

具体来说,当我检索寄存器值时,我看到了一些奇怪的事情:当我运行程序时,

EAX
EBX
R8
寄存器的值;
RAX
RBX
;每次程序运行时,
RDX
R10
总是相同的,特别是:
ECX
RCX
总是
0x00000000000000c00
R11
总是
0x0000000000000246
在所有运行中。

这里是两次引擎运行的输出:

/mnt/Linux/CPP_Projects/CyberEngine/build$ ./CyberEngine
Engine has an error occured and an exception thrown
What cause: Test Exception
Exception Details: Test Exception

Register value: 

EAX=0x000000001e860040                EBX=0x000000001e860040            ECX=0x0000000000000c00            EDX=0x00000000d78213b0
RAX=0x0000560a1e860040                RBX=0x0000560a1e860040            RCX=0x0000000000000c00            RDX=0x00007f67d78213b0
R8=0x000000001e860040                 R9=0x0000560a2021f0a0             R10=0x00007f67d720d560            R11=0x0000000000000246


Terminating CyberEngine after 15 seconds......
Aborted (core dumped)

/mnt/Linux/CPP_Projects/CyberEngine/build$ ./CyberEngine
Engine has an error occured and an exception thrown
What cause: Test Exception
Exception Details: Test Exception

Register value: 

EAX=0x000000006f4b2040                EBX=0x000000006f4b2040            ECX=0x0000000000000c00            EDX=0x0000000090c213b0
RAX=0x000055e06f4b2040                RBX=0x000055e06f4b2040            RCX=0x0000000000000c00            RDX=0x00007f7a90c213b0
R8=0x000000006f4b2040                 R9=0x000055e0714980a0             R10=0x00007f7a9060d560            R11=0x0000000000000246


Terminating CyberEngine after 15 seconds......
Aborted (core dumped)

这是显示两次运行时引擎输出的屏幕截图:

所以这是我的问题:

  • 请解释一下为什么会发生这种奇怪的事情?寄存器的值不不同吗?除了不能直接访问的 RIP、EIP 和 EFLAGS 寄存器外,其余寄存器都可以访问以读取数据,并且应该返回正确的结果。

我推测可能是存储结果的变量的内存位置,我误读了它,但后来我立即驳回了它,因为它很可笑。要么我被自己的计算机欺骗了,要么我的代码在做一些有趣的事情。因为我尝试用

std::cout
直接输出寄存器的值得到相同的结果

  • 我正在运行 Intel 64 位 CPU(i3-8145U 是 64 位 CPU),所以我怎么还能访问

    EAX
    EBX
    ECX
    EDX
    寄存器。它们是32位CPU的通用寄存器。在 64 位上,它已重命名为
    RAX
    RBX
    RCX
    RDX
    。那么,这些
    RAX
    RBX
    RCX
    RDX
    寄存器只是
    EAX
    EBX
    ECX
    EDX
    寄存器中存在的值的副本吗?

  • 如果不是,那么什么是

    EAX
    EBX
    ECX
    EDX
    存储信息,当
    RAX
    RBX
    RCX
    RDX
    在那里存储信息应用程序和操作系统?

我推测这些寄存器只是为了向后兼容 32 位应用程序,但我不确定,即使如此,应用程序也必须使用RAX

 注册?
如果我有权读取 
RBX
RCX

    RDX
  • EAX

     寄存器,也有可能(尽管很小)我可以读取寄存器 AX、BX、CX 的值, DX(都是16位的寄存器,我懂的),据我所知,后来的CPU都是设计成8086兼容的,所以这个可能性是有的
    如果可能,请解释为什么
    EBX
    ECX

    寄存器可以通过访问RSP寄存器来提取?
  • 我知道可能问题太多,但请回答,我也是寄存器的新手所以请帮助我。
    这是我的代码,它被分成多个文件。他们的代码可能有点乱,我会尽量整理一下:

  • engine_state.hpp:

EDX

engine_state.cpp:

RIP

libs.hpp:

EFLAGS

main.hpp:

#ifndef ENGINE_STATE_HPP #define ENGINE_STATE_HPP #include "libs.hpp" struct EngineState { std::string errDetails; int exitCode; }; void engine_signal_abort(); void engine_signal_exit(); void engine_state_abort(const std::string& errDetails); void engine_state_quit(int exitCode); void engine_state_configure(); // must call this function before call 2 functions above // Interface void engine_abort_triggered(struct EngineState *state); void engine_exit_triggered(struct EngineState *state); void engine_abort(std::string errDetails); void engine_exit(int exitCode); #endif

main.cpp:

#include "engine_state.hpp" struct EngineState *state = new EngineState; void engine_state_abort(const std::string& errDetails) { std::cout << "Engine has an error occured and an exception thrown" << std::endl; std::cout << "What cause: " << errDetails << std::endl; std::cout << "==================================================================================================================\n" << std::endl; std::cout << "Register value: \n" << std::endl; // eax, ebx, ecx, edx unsigned int eax, ebx, ecx, edx; __asm__ volatile ( "movl %%eax, %0\n" "movl %%ebx, %1\n" "movl %%ecx, %2\n" "movl %%edx, %3\n" : "=rm" (eax), "=rm" (ebx), "=rm" (ecx), "=rm" (edx) ); // rax, rbx, rcx, rdx unsigned long long rax, rbx, rcx, rdx; __asm__ volatile ( "movq %%rax, %0\n" "movq %%rbx, %1\n" "movq %%rcx, %2\n" "movq %%rdx, %3\n" : "=rm" (rax), "=rm" (rbx), "=rm" (rcx), "=rm" (rdx) ); // rbp, rsp, rsi, rdi registers unsigned long long rbp, rsp, rsi, rdi; __asm__ volatile ( "mov %%rbp, %0\n" "mov %%rsp, %1\n" "mov %%rsi, %2\n" "mov %%rdi, %3\n" : "=rm" (rbp), "=rm" (rsp), "=rm" (rsi), "=rm" (rdi) ); // r8 to r15 registers unsigned long long r8, r9, r10, r11, r12, r13, r14, r15; __asm__ volatile ( "movq %%r8, %0\n" "movq %%r9, %1\n" "movq %%r10, %2\n" "movq %%r11, %3\n" "movq %%r12, %4\n" "movq %%r13, %5\n" "movq %%r14, %6\n" "movq %%r15, %7\n" : "=rm" (r8), "=rm" (r9), "=rm" (r10), "=rm" (r11), "=rm" (r12), "=rm" (r12), "=rm" (r13), "=rm" (r14), "=rm" (r15) ); // cs to ss registers unsigned long long cs, ds, es, fs, ss; __asm__ volatile ( "mov %%cs, %0\n" "mov %%ds, %1\n" "mov %%es, %2\n" "mov %%fs, %3\n" "mov %%ss, %4\n" : "=rm" (cs), "=rm" (ds), "=rm" (es), "=rm" (fs), "=rm" (ss) ); // RIP register unsigned long rip, eflags; __asm__ volatile ( "movq (%%rsp), %0\n" "pushfq\n" "popq %1\n" : "=rm" (rip), "=rm" (eflags) ); // EAX char eax_value[4096]; sprintf(eax_value, "0x%016llx", eax); char *eax_msg = (char *)malloc(strlen("EAX=") + strlen(eax_value) + 1); strcpy(eax_msg, "EAX="); strcat(eax_msg, eax_value); std::cout << eax_msg << " "; // EBX char ebx_value[4096]; sprintf(ebx_value, "0x%016llx", ebx); char *ebx_msg = (char *)malloc(strlen("EBX=") + strlen(ebx_value) + 1); strcpy(ebx_msg, "EBX="); strcat(ebx_msg, ebx_value); std::cout << ebx_msg << " "; // ECX char ecx_value[4096]; sprintf(ecx_value, "0x%016llx", ecx); char *ecx_msg = (char *)malloc(strlen("ECX=") + strlen(ecx_value) + 1); strcpy(ecx_msg, "ECX="); strcat(ecx_msg, ecx_value); std::cout << ecx_msg << " "; // EDX char edx_value[4096]; sprintf(edx_value, "0x%016llx", edx); char *edx_msg = (char *)malloc(strlen("EDX=") + strlen(edx_value) + 1); strcpy(edx_msg, "EDX="); strcat(edx_msg, edx_value); std::cout << edx_msg << std::endl; // RAX char rax_value[4096]; sprintf(rax_value, "0x%016llx", rax); char *rax_msg = (char *)malloc(strlen("RAX=") + strlen(rax_value) + 1); strcpy(rax_msg, "RAX="); strcat(rax_msg, rax_value); std::cout << rax_msg << " "; // RBX char rbx_value[4096]; sprintf(rbx_value, "0x%016llx", rbx); char *rbx_msg = (char *)malloc(strlen("RBX=") + strlen(rbx_value) + 1); strcpy(rbx_msg, "RBX="); strcat(rbx_msg, rbx_value); std::cout << rbx_msg << " "; // RCX char rcx_value[4096]; sprintf(rcx_value, "0x%016llx", rcx); char *rcx_msg = (char *)malloc(strlen("RCX=") + strlen(rcx_value) + 1); strcpy(rcx_msg, "RCX="); strcat(rcx_msg, rcx_value); std::cout << rcx_msg << " "; // RDX char rdx_value[4096]; sprintf(rdx_value, "0x%016llx", rdx); char *rdx_msg = (char *)malloc(strlen("RDX=") + strlen(rdx_value) + 1); strcpy(rdx_msg, "RDX="); strcat(rdx_msg, rdx_value); std::cout << rdx_msg << std::endl; // R8 char r8_value[4096]; sprintf(r8_value, "0x%016llx", r8); char *r8_msg = (char *)malloc(strlen("R8=") + strlen(r8_value) + 1); strcpy(r8_msg, "R8="); strcat(r8_msg, r8_value); std::cout << r8_msg << " "; // R9 char r9_value[4096]; sprintf(r9_value, "0x%016llx", r9); char *r9_msg = (char *)malloc(strlen("R9=") + strlen(r9_value) + 1); strcpy(r9_msg, "R9="); strcat(r9_msg, r9_value); std::cout << r9_msg << " "; // R10 char r10_value[4096]; sprintf(r10_value, "0x%016llx", r10); char *r10_msg = (char *)malloc(strlen("R10=") + strlen(r10_value) + 1); strcpy(r10_msg, "R10="); strcat(r10_msg, r10_value); std::cout << r10_msg << " "; // R11 char r11_value[4096]; sprintf(r11_value, "0x%016llx", r11); char *r11_msg = (char *)malloc(strlen("R11=") + strlen(r11_value) + 1); strcpy(r11_msg, "R11="); strcat(r11_msg, r11_value); std::cout << r11_msg << std::endl; // Break std::cout << "\n"; std::cout << "==================================================================================================================\n" << std::endl; // Abort engine section std::cout << "\nTerminating CyberEngine after 15 seconds......" << std::endl; // Sleep for 15 seconds sleep(15); // Abort engine std::abort(); } void engine_state_quit(int exitCode) { std::cout << "Engine exit with returned " << exitCode << std::endl; std::cout << "Performing cleanup" << std::endl; std::exit(exitCode); } void engine_signal_exit() { engine_state_quit(state->exitCode); } void engine_signal_abort() { engine_state_abort(state->errDetails); } // must be call before you can call engine_exit() and engine_abort() void engine_state_configure() { // how to use: // // For exit engine handler: use engine_exit() function and pass exitCode as parameter for this function, like this: // engine_exit(0); // 0 is exitCode // // For abort engine handler: use engine_abort() function and pass errDetails as parameter for this function, like this: // engine_abort("Exception"); // "Exception" is errDetails // // Configure handler when engine exit() std::atexit(engine_signal_exit); // exit engine handler // Configure handler when engine abort or crash std::set_terminate(engine_signal_abort); // crash or abort engine handler } // Interface void engine_abort_triggered(struct EngineState *state) { try { throw std::runtime_error(state->errDetails); } catch (const std::exception& e) { engine_state_abort(state->errDetails + "\nException Details: " + e.what()); } } void engine_exit_triggered(struct EngineState *state) { std::exit(state->exitCode); } void engine_abort(std::string errDetails) { struct EngineState *engine_abort_struct = (struct EngineState *)malloc(sizeof(struct EngineState)); // Setting up value errDetails engine_abort_struct->errDetails = errDetails; engine_abort_struct->exitCode = 3; // call engine_abort_triggered() interface engine_abort_triggered(engine_abort_struct); } void engine_exit(int exitCode) { struct EngineState *engine_exit_struct = (struct EngineState *)malloc(sizeof(struct EngineState)); // Setting up value exitCode engine_exit_struct->exitCode = exitCode; engine_exit_struct->errDetails = "Engine exited"; engine_exit_triggered(engine_exit_struct); }
我使用以下命令使用

#ifndef LIBS_HPP #define LIBS_HPP // Include standard library #include <stdio.h> #include <iostream> #include <stdlib.h> #include <string> #include <cstring> #include <unistd.h> #include <stdint.h> #include <cstdint> // Thread library #include <thread> // Include GLEW. Always include before gl.h and glfw3.h #include <GL/glew.h> // Include GLFW #include <GLFW/glfw3.h> // Include GLM #include <glm/glm.hpp> using namespace glm; // CyberEngine APIs #include "main.hpp" #include "engine_state.hpp" #endif 编译引擎:

CMakeLists.txt:

#ifndef MAIN_HPP
#define MAIN_HPP

struct Input_Data {
    int argument_count;
    char **arguments;
};

#endif

c++ gcc x86 inline-assembly cpu-registers
© www.soinside.com 2019 - 2024. All rights reserved.