首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将32位转换为64位,只需将所有寄存器名从eXX更改为rXX即可使阶乘返回0?

将32位转换为64位,只需将所有寄存器名从eXX更改为rXX即可使阶乘返回0?
EN

Stack Overflow用户
提问于 2017-10-16 20:37:12
回答 1查看 246关注 0票数 0

对于所有的人来说,学习计算机编程的艺术,能够访问像Stack溢出这样的社区是多么幸运啊!我决定承担起学习计算机编程的任务,我是通过一本名为“从头编程”的电子书的知识来完成这一任务的,这本书教读者如何在GNU/Linux环境下用汇编语言创建程序。

我在书中的进展已经到了用函数计算整数4的阶乘的地步,我已经做了和做了,没有任何由GCC的汇编程序引起的错误,或者运行这个程序所造成的任何错误。但是,函数在我的程序中没有返回正确的答案!4的阶乘为24,但程序返回的值为0!正确地说,我不知道这是为什么!

下面是您需要考虑的代码:

代码语言:javascript
复制
.section .data

.section .text

.globl _start

.globl factorial

_start:

push $4                    #this is the function argument
call factorial             #the function is called
add $4, %rsp               #the stack is restored to its original 
                           #state before the function was called
mov %rax, %rbx             #this instruction will move the result 
                           #computed by the function into the rbx 
                           #register and will serve as the return 
                           #value 
mov $1, %rax               #1 must be placed inside this register for 
                           #the exit system call
int $0x80                  #exit interrupt

.type factorial, @function #defines the code below as being a function

factorial:                 #function label
push %rbp                  #saves the base-pointer
mov %rsp, %rbp             #moves the stack-pointer into the base-
                           #pointer register so that data in the stack 
                           #can be referenced as indexes of the base-
                           #pointer
mov $1, %rax               #the rax register will contain the product 
                           #of the factorial
mov 8(%rbp), %rcx          #moves the function argument into %rcx
start_loop:                #the process loop begins
cmp $1, %rcx               #this is the exit condition for the loop
je loop_exit               #if the value in %rcx reaches 1, exit loop
imul %rcx, %rax            #multiply the current integer of the 
                           #factorial by the value stored in %rax
dec %rcx                   #reduce the factorial integer by 1
jmp start_loop             #unconditional jump to the start of loop
loop_exit:                 #the loop exit begins
mov %rbp, %rsp             #restore the stack-pointer
pop %rbp                   #remove the saved base-pointer from stack
ret                        #return
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-10-17 09:25:10

%rax**,TL:DR:返回地址的阶乘溢出了,留下了0,**,因为您移植错了。

将32位代码移植到64位的并不像更改所有寄存器名那样简单,可以让它组装的,但是正如您所发现的,即使这个简单的程序也有不同的行为。在x86-64中,push %regcall都推送64位值,并将rsp修改为8。如果使用调试器单步执行代码,您就会看到这一点。(请参阅x86标签维基底部以获得使用gdb进行asm的信息。)

您正在学习一本使用32位示例的书,因此您可能应该只使用 将它们构建为32位可执行文件。,而不是在您知道如何将它们移植到64位之前。

使用32位sys_exit() ABI的int 0x80仍然有效(如果在64位代码中使用32位INT0x80LinuxABI怎么办?),但是如果您试图传递64位指针,系统调用就会遇到麻烦。使用64位ABI

如果要调用任何库函数,也会遇到问题,因为标准函数调用约定也不同。请参阅为什么参数存储在寄存器中而不是x86-64程序集中的堆栈上?和64位ABI链接,以及x86标记wiki中的其他调用约定文档。

但是你没有这样做,所以你的程序的问题很简单,就是没有考虑到x86-64中翻了一倍的“堆栈宽度”。factorial 函数将返回地址作为参数读取。

这是您的代码,注释以解释它实际上做了什么

代码语言:javascript
复制
push $4                    # rsp-=8.  (rsp) = qword 4
                           # non-standard calling convention with args on the stack.
call factorial             # rsp-=8.  (rsp) = return address.  RIP=factorial
add $4, %rsp               # misalign the stack, so it's pointing to the top half of the 4 you pushed earlier.
# if this was in a function that wanted to return, you'd be screwed.

mov %rax, %rbx             # copy return value to first arg of system call
mov $1, %rax               #eax = __NR_EXIT from asm/unistd_32.h, wasting 2 bytes vs. mov $1, %eax
int $0x80                  # 32-bit ABI system call, eax=call number, ebx=first arg.  sys_exit(factorial(4))

所以调用方是好的(对于您发明的非标准64位调用约定,它传递堆栈上的所有arg)。您最好完全忽略add%rsp,因为您即将退出而不进一步接触堆栈。

代码语言:javascript
复制
.type factorial, @function #defines the code below as being a function

factorial:                 #function label
push %rbp                  #rsp-=8, (rsp) = rbp
mov %rsp, %rbp             # make a traditional stack frame

mov $1, %rax               #retval = 1.  (Wasting 2 bytes vs. the exactly equivalent mov $1, %eax)

mov 8(%rbp), %rcx          #load the return address into %rcx

... and calculate the factorial

对于静态可执行文件(以及动态链接的可执行文件没有用PIE启用ASLR的),_start通常位于0x4000c0。您的程序仍将在现代CPU上几乎瞬间运行,因为imulimul* 3c延迟时间仍然只有1250万个核心时钟周期。在4 4GHz上,这是3毫秒的CPU时间。

如果您通过在最近的发行版上与gcc foo.o链接来创建一个与位置无关的可执行文件,_start将有一个类似于0x5555555545a0的地址,您的函数在4 4GHz上运行3周期imul延迟将花费大约70368秒。

4194496!它包含许多偶数,因此它的二进制表示有许多尾随零。当您完成时,整个%rax将为零,将从0x4000c0减为1的所有数字相乘。

Linux进程的退出状态仅为传递给sys_exit()的整数的低8位(因为wstatus只是一个32位int,并且包含其他东西,比如结束进程的信号)。见wait4(2))。所以即使用小的args,也不需要太多。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46778697

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档