【问题标题】:Assembly simple I/O code. Int to string error汇编简单的 I/O 代码。 Int 到字符串错误
【发布时间】:2015-11-21 12:17:23
【问题描述】:

我已经问过一个问题,人们向我指出了一个整数到字符串转换的线程。反之亦然。我只是暂时复制了它,以便查看我的程序是否有效,稍后我将尝试编写自己的程序。但是有一个问题。我找不到我犯的错误。在最后一次输入之后,程序总是以 Segmentation Fault 退出。我尝试删除 int 到字符串的转换,只显示输入的值并且它有效。所以我必须在转换中做错了什么。这是我的第一个程序之一,如果我想进一步进步,我真的需要了解它为什么行不通。谢谢你。这是我的代码:

section .text
global _start       



_start:                     

mov edx, lenask
mov ecx, ask
mov ebx, 1
mov eax, 4
int 0x80

mov edx, 5
mov ecx, input
mov ebx, 0
mov eax, 3
int 0x80

mov edx, lenask2
mov ecx, ask2
mov ebx, 1
mov eax, 4
int 0x80

mov edx, 5
mov ecx, input2
mov ebx, 0
mov eax, 3
int 0x80

lea esi, [input]
mov ecx, 2
call string_to_int
push eax

lea esi, [input2]
mov ecx, 4
call string_to_int
mov ebx, eax
pop eax



neg eax

add ebx, eax


mov [buffer], ebx
mov eax, [buffer]
lea esi, [result]
call int_to_string


mov edx, lenanswer
mov ecx, answer
mov ebx, 1
mov eax, 4
int 0x80


mov edx, 5
mov ecx, result
mov ebx, 1
mov eax, 4
int 0x80




mov eax, 1
mov ebx, 0
int 80h





;code taken from another thread
; Input:
; EAX = integer value to convert
; ESI = pointer to buffer to store the string in (must have room for at      least 10 bytes)
; Output:
; EAX = pointer to the first character of the generated string
int_to_string:
add esi,9
mov byte [esi], 0

mov ebx,10         
.next_digit:
xor edx,edx         ; Clear edx prior to dividing edx:eax by ebx
div ebx             ; eax /= 10
add dl,'0'          ; Convert the remainder to ASCII 
dec esi             ; store characters in reverse order
mov [esi],dl
test eax,eax            
jnz .next_digit     ; Repeat until eax==0
mov eax,esi
push eax
ret



;code taken from another thread
; Input:
; ESI = pointer to the string to convert
; ECX = number of digits in the string (must be > 0)
; Output:
; EAX = integer value
string_to_int:
xor ebx,ebx    ; clear ebx
.next_digit:
movzx eax,byte[esi]
inc esi
sub al,'0'    ; convert from ASCII to number
imul ebx,10
add ebx,eax   ; ebx = ebx*10 + eax
loop .next_digit  ; while (--ecx)
mov eax,ebx
ret


section .data

ask db "What is your age?"
lenask equ $-ask
ask2 db "What is today's year?"
lenask2 equ $-ask2
answer db "The age you were born was: "
lenanswer equ $-answer

section .bss 

input resw 5
input2 resw 5
buffer resw 5
result resw 10

一个例子:

What is your age?35
What is today's year?2015
The age you were born was: Segmentation fault(core dumped)

应该做的:

What is your age?35
What is today's year?2015
The age you were born was: 1980

谢谢!

【问题讨论】:

  • 读取固定字节可能是错误的。您可能应该每个int 0x80 只读取一个字节并检查代表数据结尾的“\n”。
  • int_to_string 的结尾肯定看起来很糟糕:push eax; ret 这可能是错误的地方。你应该学会使用调试器,也不要在不懂代码的情况下盲目复制代码。
  • 尝试在代码末尾添加一个空行。一些编译器想要这样。
  • @Jester,我负部分责任。这个人昨天问了一个问题,我发表了评论并将他们定向到这个SO Answer,但我没有跟进他们,因为我很忙。原代码没有push

标签: linux assembly io x86 nasm


【解决方案1】:

既然我把你引到这个SO Answer,我会跟进并指出你写的代码中的两个问题。首先,您在原来的int_to_string 函数中添加了push eax。此行应删除以将原始功能恢复到正确的顺序:

jnz .next_digit     ; Repeat until eax==0
mov eax,esi
push eax   ; REMOVE this
ret

应该是:

jnz .next_digit     ; Repeat until eax==0
mov eax,esi
ret

阻止您显示转换为字符串的整数的主要错误是对string_to_int 代码的编写方式的误解。在 cmets 中它说:

;code taken from another thread
; Input:
; EAX = integer value to convert
; ESI = pointer to buffer to store the string in (must have room for at least 10 bytes)
; Output:
; EAX = pointer to the first character of the generated string

您正确设置了输入,但您可能没有注意到 EAX 返回指向字符串第一个字符的指针(作为输出)。将EAX中的返回地址写到控制台时,需要将其作为指向字符串的指针。

在您调用string_to_int 之后,您应该暂时将值压入堆栈(或将其保存在临时内存位置),然后在准备写出时将该指针放入 ECX转换为字符串的整数。如果您使用push eax,那么您可以将您的代码修改为如下所示:

lea esi, [result]
call int_to_string
push eax          ; Save the pointer to the beginning of the string containing the number

mov edx, lenanswer
mov ecx, answer
mov ebx, 1
mov eax, 4
int 0x80

pop ecx           ; Restore the pointer to beginning of number to display
mov edx, 5
mov ebx, 1
mov eax, 4
int 0x80

对您的代码进行这些更改应该可以解决您的问题。我强烈建议学习使用调试器。 GNU 调试器 (gdb) 的 ddd 图形前端很可能在您的平台上可用。如果您是 GDB 新手,这是一个合理的调试器。最大限度地利用汇编语言编程的最佳方法是使用调试信息编译您的汇编代码。我建议这样做:

nasm -f elf32 -g -F dwarf filename.asm -o filename.o
ld -melf_i386 filename.o -o filename

这将使用 dwarf 调试信息组装您的汇编程序(dwarf 格式可以使 GDB/DDD 中的汇编程序更容易单步执行)。我使用 LD 将其链接到最终的可执行文件(使用通常用于链接的任何方式)。将生成一个名为filename 的可执行文件。您可以将可执行文件名称更改为您想要的任何名称,我只是以filename 为例。

如果您有 DDD,您可以使用如下命令在调试器中启动您的程序:

ddd ./filename

DDD手册可以在here找到

【讨论】:

  • 再次感谢您。我强烈反对盲目使用代码并复制粘贴它,但我这样做只是为了看看它是否有效。我一定会明白的。现在,如果函数将完整字符串存储在结果缓冲区中,为什么我必须使用 eax 字符串指针?为什么我不能只 mov ecx, result ,不一样吗?
  • @user3348294 这就是代码的编写方式。您应该研究整数到字符串的一般转换。如果您使用除以 10 的方法来访问所有要转换为字符的数字,您最终会以相反的顺序处理字符。 1970 将被处理为 0791。此代码通过从缓冲区末尾向后移动的数组中构建数字来解释反转的数字。他返回 EAX 中数字开头的位置,因为它可能不在您传递的缓冲区的开头(除非您正在处理一个大数字)
  • @user3348294 您是否能够根据建议的更改使您的程序正常运行?
  • 是的,我知道了。我现在明白了。我编译后它工作了。但是windows呢,我尝试用-f win32编译它,但执行exe后它停止工作。带来不便敬请谅解。我读过一些地方,Windows 上不存在 start 和 main 功能,需要用 winmain 替换,是这样吗?
  • @user3348294 您编写的代码无法在 Windows 上运行。 int 0x80 系统调用是特定于 Linux 的,在 Windows 上可能会崩溃或不做任何有用的事情。在 Windows 上从输入和输出设备读取和写入是非常不同的。你不是在 Linux 上运行你的程序吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多