我有一个用 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)
这是显示两次运行时引擎输出的屏幕截图:
所以这是我的问题:
我推测可能是存储结果的变量的内存位置,我误读了它,但后来我立即驳回了它,因为它很可笑。要么我被自己的计算机欺骗了,要么我的代码在做一些有趣的事情。因为我尝试用
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寄存器来提取?
我知道可能问题太多,但请回答,我也是寄存器的新手所以请帮助我。
这是我的代码,它被分成多个文件。他们的代码可能有点乱,我会尽量整理一下:
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