assembly - 如何将 64 位函数转换为 32 位到 16 位到 8 位?
问题描述
我是汇编新手,我不知道如何将 64 位函数转换为 32 位到 16 位到 8 位
以下函数的目的是打印数字并返回其中的位数。
64位:
global print_uint64
section .text
print_uint64:
mov rax,rdi
mov rdi,10
mov rsi,rsp
while:
xor rdx ,rdx
div rdi
add rdx ,48
dec rsi
mov [rsi],dl
cmp rax ,0
jne while
mov rax,1
mov rdi,1
lea rdx,[rsp]
sub rdx,rsi
syscall
lea rax,[rsp]
sub rax,rsi
ret
this works fine
32位:
global print_uint32
section .text
print_uint32:
mov eax,edi
mov edi,10
mov rsi,rsp
while:
xor edx ,edx
div edi
add edx ,48
dec rsi
mov [rsi],dl
cmp eax ,0
jne while
mov eax,1
mov edi,1
lea edx,[rsp]
sub edx,esi
syscall
lea eax,[rsp]
sub eax,esi
ret
这很好用
16 位:
global print_uint16
section .text
print_uint16:
mov ax,di
mov di,10
mov rsi,rsp
while:
xor dx ,dx
div di
add dx ,48
dec rsi
mov [rsi],dl
cmp ax ,0
jne while
mov ax,1
mov di,1
lea dx,[rsp]
sub dx,si
syscall
lea ax,[rsp]
sub ax,si
ret
但这没有用
我研究了关于这个问题的堆栈溢出的一些问题,我的理解是我不能将 rsp 更改为 esp,因为 esp 将高 32 位设置为零,所以当我们在访问时使用 [] 时,没有分配给该程序的内存所以它引发段错误。
我的问题是:
1)64位到32位到16位到8位转换的基本规则是什么。
解决方案
基本规则是,无论数据宽度如何,指针仍然是 64 位的。就像在 C 中一样,sizeof(int*)
并且sizeof(char*)
是相同的(在普通系统上)。
这就是为什么您的所有版本都必须使用dec rsi
and mov [rsi],dl
:RSP 拥有一个 64 位指针。将其截断为 32 位不会产生有效的指针。
此外,系统调用编号和fd
仍然是相同的大小;mov ax,1
并将mov di,1
垃圾留在寄存器的高字节中。用于strace ./my_program
解码您实际传递给syscall
.
窄版本可以将其输入零扩展为 32 位并跳转到 32 位版本。
但除此之外,基本规则是尽可能使用 32 位操作数大小;这是 x86-64 的自然大小(在 x86-64 中使用 32 位寄存器/指令的优点)。例如,RDX/EDX/DX 始终为零,带有xor edx,edx
.
写入 32 位寄存器零扩展为 64 位,与 8/16 不同,它只是合并到旧值中。
为什么 32 位寄存器上的 x86-64 指令会将完整 64 位寄存器的上部归零?使用 16 位mov reg,imm16
并留下大量垃圾可能是您的系统调用不起作用的原因。
为什么 GCC 不使用部分寄存器?
值得注意的是,lea dx,[rsp]
/sub dx,si
可能会在 RDX 的高位(即write
系统调用的 arg)中留下垃圾。
这是一个指针减法,计算char
缓冲区中的元素数。根据输入数字的大小为此选择操作数大小是没有意义的。只要确保结果为零扩展到 RDX,实际上就可以进行窄减,因为在这种情况下,您知道位数最多为 19(对于 64 位版本),因为那是多长时间2^64-1 以 10 为底。
所以mov edx, esp
/sub edx, esi
是你在所有版本中应该做的。由于完整的 RSP 和 RSI 就在附近,因此它们的差异很小。在减法之前截断输入而不是在减法之后截断结果不会改变结果;进位从低位传播到高位。如果只需要结果的低部分,请参阅哪些 2 的补码整数运算可以在不将输入中的高位归零的情况下使用?
使用 LEA 复制寄存器效率不高; lea dx, [rsp]
架构上相同,mov dx, sp
但速度较慢。
推荐阅读
- firebase - 世博会推送通知在本机反应中使用firebase云功能
- laravel - 急切加载错误:'调用成员函数
- html - 如何在 MVC 视图中从一个模块创建多个表?
- spring - MyBatis 在转换 #{} 元素时,出现多余的 '?
- php - 如何将数组转换为 json 对象?
- html - 更改网格视图的方向
- makefile - 使用 msys2 构建 pjsip 2.10
- python - 使用 scipy 的牛顿函数求解方程
- powershell - PowerShell 获取 SharePoint 日历项目
- java - Android studio-当互联网不可用时禁用下载按钮/活动