【问题标题】:Modify C array in external assembler routine在外部汇编程序中修改 C 数组
【发布时间】:2018-11-26 03:20:54
【问题描述】:

总结:我们在 C 中有一个 int 变量和 4 个双精度数组,其中 2 个保存输入数据,其中 2 个我们要写入输出数据。我们将变量和数组传递给外部 .asm 文件中的函数,其中输入数据用于确定输出数据,并将输出数据写入输出数组。

我们的问题是,在汇编例程完成其工作后,输出数组似乎保持不变。我们甚至不知道例程是否读取了正确的输入数据。我们哪里做错了?

我们使用以下命令编译

nasm -f elf32 -o calculation.o calculation.asm
gcc -m32 -o programm main.c calculation.o

如果您需要更多信息,请随时询问。

C 代码:

// before int main()
extern void calculate(int32_t counter, double radius[], double distance[], double paper[], double china[]) asm("calculate");

// in int main()
double radius[counter];
double distance[counter];

// [..] Write Input Data to radius & distance [...]

double paper[counter];
double china[counter];

for(int i = 0; i < counter; i++) {
    paper[i] = 0;
    china[i] = 0;
}

calculate(counter, radius, distance, paper, china);
// here we expect paper[] and china[] to contain output data

我们的汇编代码目前只接受值,将它们放入 FPU,然后将它们放入输出数组。

x86 汇编(英特尔语法)(我知道,这段代码看起来很糟糕,但我们是初学者,所以请多多包涵;另外,我无法让语法突出显示正常工作):

BITS 32
GLOBAL calculate

calculate:
SECTION .BSS
; declare all variables
pRadius: DD 0
pDistance: DD 0
pPaper: DD 0
pChina: DD 0
numItems: DD 0
counter: DD 0

; populate them
POP DWORD [numItems]
POP DWORD [pRadius]
POP DWORD [pDistance]
POP DWORD [pPaper]
POP DWORD [pChina]

SECTION .TEXT
PUSH EBX ; because of cdecl calling convention
JMP calcLoopCond

calcLoop:
; get input array element
MOV EBX, [counter]
MOV EAX, [pDistance]
; move it into fpu, then move it to output
FLD QWORD [EAX + EBX * 8]
MOV EAX, [pPaper]
FSTP QWORD [EAX + EBX * 8]
; same for the second one
MOV EAX, [pRadius]
FLD QWORD [EAX + EBX * 8]
MOV EAX, [pChina]
FSTP QWORD [EAX + EBX * 8]

INC EBX
MOV [counter], EBX

calcLoopCond:
MOV EAX, [counter]
MOV EBX, [numItems]
CMP EAX, EBX
JNZ calcLoop

POP EBX
RET

【问题讨论】:

  • 不会帮助您将代码拆分到 BSS 和文本部分。使用 POP 从传递给函数的堆栈中获取参数是非常混乱的。你也没有考虑到当一个函数被调用时,栈顶的东西就是返回地址。
  • @MichaelPetch 感谢您的意见。正如我所说,我们对实际的汇编编程是全新的,所以会发生这类重大错误。现在有什么新的意见吗?
  • 此时@Jester 会建议使用调试器(如gdb)来单步执行指令并查看内存/寄存器和执行的指令以查看失败的地方。
  • 如果您想发布代码的工作版本,请将其发布为答案。修复问题中的错误会使答案无效。我回滚了你的编辑。

标签: c arrays gcc assembly nasm


【解决方案1】:

汇编程序中有几个问题。 POP 指令被发送到.bss 部分,因此它们永远不会被执行。在POPs 的序列中,返回地址(由调用者推送)不考虑在内。根据 ABI,无论如何您都必须将参数留在堆栈上。因为POPs 永远不会执行,所以循环退出条件总是恰好为真。

而且你真的不应该以这种方式使用全局变量。相反,创建一个堆栈帧并使用它。

【讨论】:

  • 感谢您的意见。正如我所说,我们对实际的汇编编程是全新的,所以会发生这类重大错误。现在有什么新的意见吗?
  • 你有理由使用汇编吗?只需用 C 语言编写所有内容,快乐且富有成效。当谈到获得最佳性能时,人们可能也想摆脱参数传递开销。组装很乱。通常,您可以找到有用的汇编指令,可从 C 中作为编译器内部函数调用。
【解决方案2】:

感谢您的所有回答和 cmets 以及一些深入的研究,我们最终设法生成了功能代码,现在可以正确使用堆栈帧并满足 cdecl 调用约定:

BITS 32
GLOBAL calculate

SECTION .DATA
electricFieldConstant DQ 8.85e-12
permittivityPaper DQ 3.7
permittivityChina DQ 7.0

SECTION .TEXT
calculate:
PUSH EBP
MOV EBP, ESP
PUSH EBX
PUSH ESI
PUSH EDI

MOV ECX, 0 ; counter for loop
JMP calcLoopCond

calcLoop:
MOV EAX, [EBP +  12]
FLD QWORD [EAX + ECX * 8]
MOV EAX, [EBP + 20]
FSTP QWORD [EAX + ECX * 8]
MOV EAX, [EBP + 16]
FLD QWORD [EAX + ECX * 8]
MOV EAX, [EBP + 24]
FSTP QWORD [EAX + ECX * 8]

ADD ECX, 1 ; increase loop counter

calcLoopCond:
MOV EDX, [EBP +  8]
CMP ECX, EDX
JNZ calcLoop

POP EDI
POP ESI
POP EBX
MOV ESP, EBP
POP EBP
RET

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-18
    • 2020-08-11
    • 1970-01-01
    • 2021-11-11
    • 1970-01-01
    • 2011-10-07
    • 2013-07-02
    • 1970-01-01
    相关资源
    最近更新 更多