c++ - 程序集和 C++ 之间的 Segfault 共享数组
问题描述
我正在编写一个在程序集和 C++ 之间具有共享状态的程序。我在程序集文件中声明了一个全局数组,并在 C++ 中的函数中访问了该数组。当我从 C++ 中调用该函数时,没有问题,但随后我从程序集中调用相同的函数,我得到了分段错误。我相信我在函数调用中保留了正确的寄存器。
奇怪的是,当我将 C++ 中的指针类型更改为 uint64_t 指针时,它会正确输出值,但在将其转换为 uint64_t 后再次出现分段错误。
在下面的代码中,不断给我错误的数组是 currentCPUState。
//CPU.cpp
extern uint64_t currentCPUState[6];
extern "C" {
void initInternalState(void* instructions, int indexSize);
void printCPUState();
}
void printCPUState() {
uint64_t b = currentCPUState[0];
printf("%d\n", b); //this line DOESNT crash ???
std::cout << b << "\n"; //this line crashes
//omitted some code for the sake of brevity
std::cout << "\n";
}
CPU::CPU() {
//set initial cpu state
currentCPUState[AF] = 0;
currentCPUState[BC] = 0;
currentCPUState[DE] = 0;
currentCPUState[HL] = 0;
currentCPUState[SP] = 0;
currentCPUState[PC] = 0;
printCPUState(); //this has no issues
initInternalState(instructions, sizeof(void*));
}
//cpu.s
.section .data
.balign 8
instructionArr:
.space 8 * 1024, 0
//stores values of registers
//used for transitioning between C and ASM
//uint64_t currentCPUState[6]
.global currentCPUState
currentCPUState:
.quad 0, 0, 0, 0, 0, 0
.section .text
.global initInternalState
initInternalState:
push %rdi
push %rsi
mov %rcx, %rdi
mov %rdx, %rsi
push %R12
push %R13
push %R14
push %R15
call initGBCpu
pop %R15
pop %R14
pop %R13
pop %R12
pop %rsi
pop %rdi
ret
//omitted unimportant code
//initGBCpu(rdi: void* instructions, rsi:int size)
//function initializes the array of opcodes
initGBCpu:
pushq %rdx
//move each instruction into the array in proper order
//also fill the instructionArr
leaq instructionArr(%rip), %rdx
addop inst0x00
addop inst0x01
addop inst0x02
addop inst0x03
addop inst0x04
call loadCPUState
call inst0x04 //inc BC
call saveCPUState
call printCPUState //CRASHES HERE
popq %rdx
ret
其他详细信息:操作系统:Windows 64 位编译器 (MinGW64-w) 架构:x64
任何见解将不胜感激
编辑: addop 是一个宏:
//adds an opcode to the array of functions
.macro addop lbl
leaq \lbl (%rip), %rcx
mov %rcx, 0(%rdi)
mov %rcx, 0(%rdx)
add %rsi, %rdi
add %rsi, %rdx
.endm
解决方案
一些x86-64 调用约定要求在调用函数之前必须将堆栈对齐到 16 字节边界。
函数被调用后,一个 8 字节的返回地址被压入堆栈,因此必须向堆栈添加另一个 8 字节的数据以满足此对齐要求。否则,某些具有对齐要求的指令(如某些 SSE 指令)可能会崩溃。
假设应用了这样的调用约定,initGBCpu
函数看起来没问题,但是initInternalState
函数必须在调用函数之前再向堆栈添加一个 8 字节的东西initInternalState
。
例如:
initInternalState:
push %rdi
push %rsi
mov %rcx, %rdi
mov %rdx, %rsi
push %R12
push %R13
push %R14
push %R15
sub $8, %rsp // adjust stack allignment
call initGBCpu
add $8, %rsp // undo the stack pointer movement
pop %R15
pop %R14
pop %R13
pop %R12
pop %rsi
pop %rdi
ret
推荐阅读
- javascript - window.document.execCommand() 不工作
- android - 自动生成不正确的 IntentService 默认构造函数
- ruby-on-rails - Rails 服务器无法运行,“Rails 服务器”命令出错
- javascript - 如何访问 JavaScript 匿名函数中的变量
- c - 在c中通过索引偶数或奇数将数组拆分为两个数组
- android - 使用 SmoothScroller 时一次可点击的单个项目
- python - 在python中编码和解码字符串
- r - 仅使用基本 R 将 `=` 转换为 `<-`
- sonos - Sonos 身份验证令牌使用只能使用一次的代码
- ubuntu - 如何从 ubuntu 终端停止在本地主机上运行的超级账本作曲家游乐场?