提问人:1111 B 提问时间:12/2/2022 最后编辑:Peter Cordes1111 B 更新时间:12/2/2022 访问量:64
ASM/NASM - 在类型结构中返回 MUL 的高低
ASM/NASM - Return high low of a MUL in a type struct
问:
global mymul
mymul:
mov rax, rdi
mul rsi
ret
#include <stdio.h>
typedef struct {
unsigned long long high;
unsigned long long low;
} resmul;
void mymul(unsigned long long, unsigned long long, resmul *res);
int main() {
resmul res;
mymul(3, 6, &res);
printf("mymul(3, 6); res.high=0x%llx, res.low=0x%llx\n", res.high, res.low);
//mymul(3, 6); res.high=0x0, res.low=0x12
return 0;
}
目标是将第一个 arg 与第二个 arg 相乘,然后发送到最后一个 arg 第一个参数 = RDI / 第二个参数 = RSI 将结果高/低发送到 TypeStruct 的目标
我不明白为什么它给两个结果都给 0 RAX 和 RDX 应该返回,但我没有
答:
2赞
Nate Eldredge
12/2/2022
#1
你的函数是从 C 中声明为采用指针参数的,它应该将结果存储在内存中。但实际上,它将它们留在 rdx:rax 寄存器中,并且根本不会在内存中存储任何内容,完全忽略指针。mymul
第三个参数将在 rdx 寄存器中传递,这会使事情变得有点复杂,因为会覆盖它。所以你必须做类似的事情mul
global mymul
mymul:
mov rcx, rdx ; save argument
mov rax, rdi
mul rsi
mov [rcx], rdx
mov [rcx+8], rax
ret
评论
0赞
1111 B
12/2/2022
哦,谢谢!但是为什么我们加+而不是8,难道不应该加4个字节吗?
0赞
Nate Eldredge
12/2/2022
@1111B:是 64 位,即 8 个字节。您的函数将两个 64 位输入相乘以产生 128 位输出。unsigned long long
mymul
0赞
Peter Cordes
12/2/2022
IMO,正确的解决方法是更改原型和调用者,而不是通过引用返回来降低一切效率。(并将结构向后保留,高元素位于较低的地址。
0赞
Peter Cordes
12/2/2022
#2
您告诉编译器您的函数返回,因此调用方不会查看 RDX:RAX。void
使用调试器和/或查看调用方中的 asm;它将从堆栈中加载其未初始化的局部变量,而未写入的堆栈内存恰好为零。res
要正确声明在 RDX:RAX 中为 x86-64 SysV 调用约定返回的函数,请返回结构,或者返回未签名的 __int128
(如果您使用的是 GNU 扩展)。
#include <stdio.h>
#include <stdint.h>
typedef struct {
uint64_t low; // low first, the low half of RDX:RAX
uint64_t high;
} resmul; // x86-64 System V will return this in RDX:RAX, like __int128
resmul mymul(uint64_t, uint64_t); // your function doesn't look for a pointer in RDX
int main() {
resmul res = mymul(3, 6);
printf("mymul(3, 6); res.high=%#lx, res.low=%#lx\n", res.high, res.low);
//mymul(3, 6); res.high=0x0, res.low=0x12
return 0;
}
Godbolt,还包括编译为相同 asm 的函数的 GNU C 实现:
// compiles to the same asm
resmul mymul(uint64_t a, uint64_t b){
unsigned __int128 prod = a * (unsigned __int128)b;
return (resmul){prod, prod>>64};
}
unsigned __int128 mulv2(uint64_t a, uint64_t b){
return a * (unsigned __int128)b;
}
mymul(unsigned long, unsigned long):
mov rax, rdi
mul rsi
ret
mulv2(unsigned long, unsigned long):
mov rax, rdi
mul rsi
ret
评论