linux - 如何在 GAS 汇编中使用 "repne scasb"?

标签 linux assembly x86-64 gnu-assembler

所以我写了一个基于NASM的程序,接收用户输入(准确的说是两个数),然后实现两个数的加减,然后打印回来。该程序在 NASM 中运行良好,但我在使用 GAS 时遇到了问题。由于段错误,使用 repne scasb 指令计算字符串长度的子例程让我很头疼。

我已经检查了代码中是否存在段错误,我已将错误定位在 repne scasb 行。

我基本上做的是将我制作的 NASM 代码翻译成相应的 GAS 代码。但是,正如我之前所说,它给了我一个段错误。在我从用户那里得到第一个号码后,更具体地说。

.section .data
    msg:                .ascii "Insert a number: "
    msgLen              = .-msg

    msg2:               .ascii "Insert another number: "
    msg2Len             = .-msg2

    errorMsg:           .ascii "Error: invalid character!\n"
    errorMsgLen         = .-errorMsg

    displaySuma:        .ascii "The result of the addition is: "
    displaySumaLen      = .-displaySuma

    displayResta:       .ascii "The result of the difference is: "
    displayRestaLen     = .-displayResta

    enterChar:          .ascii "\n"

    terminator:         .byte 0

.section .bss
    .lcomm num1, 8

    .lcomm num2, 8

    .lcomm buffer, 10
    .lcomm buffer2, 10

.section .text
.global _start

_start:
    call _clear                 #clear registers. Probably an useless routine
    call _msg1                  #Display msg1
    call _num1                  #Read num1

    movl num1, %edi

    call _lenString             #ECX now has num1 length

    lea (num1), %esi
    call _stringToInt           #EAX now has num1 in integer

    movl %eax, %r15d            #R15D now has the integer

    call _msg2                  #Display msg2
    call _num2                  #Read num2

    xor %edi, %edi              #Clear EDI
    movl num2, %edi         #Moving num2 to EDI register to call _lenstring
    xor %ecx, %ecx              #Clear ECX

    call _lenString             #ECX has num2 length

    xor %esi, %esi              #clear ESI
    lea (num2), %esi
    call _stringToInt           #EAX now has integer value of num2

    mov %eax,%r14d              ###R14D has num2 now

    #Addition
    #r8d = num1 + num2
    mov %r15d, %r8d
    add %r14d, %r8d             #R8D has num1 + num2

    #Difference
    #If num1 > num2    =======> r9d = num1 - num2
    #If num1 < num2    =======> r9d = num2 - num1
    cmp %r14d, %r15d
    jg .greater

    mov %r14d, %r9d
    sub %r15d, %r9d             #R9D has num1 - num2
    jmp .next

.greater:
    mov %r15d, %r9d
    sub %r14d, %r9d             #R9D has num2 - num1
    jmp .next

.next:
    mov %r8d, %eax              #Sum is now at EAX to convert it to ascii characters

    lea (buffer), %esi
    call _intToString
    #EAX ascii of the sum

    mov %eax, %r10d             #Using R10D to store the new string

    mov %r9d, %eax              #Difference result is now at EAX

    lea (buffer2), %esi
    call _intToString
    #EAX has the pointer to the difference result.

    mov %eax, %r11d             #Storing string in R10D

    xor %edi, %edi
    xor %r15d, %r15d
    xor %r14d, %r14d

    mov %r10d, %edi
    call _lenString             #ECX length of sum string
    mov %ecx, %r15d             #R15D now has that value

    call _suma                              #This prints the sum result

    xor %edi, %edi              #Clear EDI
    mov %r11d, %edi     
    call _lenString             #ECX has length of dif. string
    mov %ecx, %r14d             #R14D has that value

    call _resta                             #Print dif. result

    movl $1, %eax               #End of the program
    movl $0, %ebx
    int $0x80

_stringToInt:
    xor %ebx, %ebx

.next_digit:
    movzxb (%esi), %eax

    cmp $0x30, %eax                         #These 4 lines check for invalid characters
    jb _errorMsg
    cmp $0x39, %eax
    ja _errorMsg

    inc %esi
    sub $0x30, %eax             ###Sub 48 (converts to integer)
    imul $10, %ebx              
    add %eax, %ebx              #ebx = ebx*10 + eax
    loop .next_digit            #loop [ECX] times
    mov %ebx, %eax
    ret

_intToString:
    add $10, %esi
    mov (terminator),%esi

    mov $10, %ebx

.next_digit1:
    xor %edx, %edx
    div %ebx
    add $0x30, %edx             ##
    dec %esi
    mov %dl, (%esi)
    test %eax, %eax
    jnz .next_digit1
    mov %esi, %eax
    ret


#######################################################################################################


_msg1:
    movl $4, %eax                       #msg1 routine
    movl $1, %ebx
    movl $msg, %ecx
    movl $msgLen, %edx
    int $0x80
    ret

_num1:
    movl $3, %eax                       #Reads first number
    movl $0, %ebx
    movl $num1, %ecx
    movl $8, %edx
    int $0x80
    ret

_msg2:
    movl $4, %eax                       #msg2 display
    movl $1, %ebx
    movl $msg2, %ecx
    movl $msg2Len, %edx
    int $0x80
    ret

_num2:
    movl $3, %eax                       #Reads the next number
    movl $0, %ebx
    movl $num2, %ecx
    movl $8, %edx
    int $0x80
    ret

_salir:
    movl $1, %eax                       #Exit
    movl $0, %ebx
    int $0x80

_errorMsg:
    movl $4, %eax                       #Error msg
    movl $1, %ebx
    movl $errorMsg, %ecx
    movl $errorMsgLen, %edx
    int $0x80

    jmp _salir

_lenString:
    xor %ecx, %ecx
    not %ecx
    xor %al, %al
    mov $0xA, %al
    cld
    repne scasb                     #Segmentation fault is caused by this line
    not %ecx
    dec %ecx
    ret

_suma:
    movl $4, %eax
    movl $1, %ebx
    movl $displaySuma, %ecx
    movl $displayRestaLen, %edx
    int $0x80

    movl $4, %eax
    movl $1, %ebx
    mov %r10d, %ecx
    mov %r15d, %edx
    int $0x80

    movl $4, %eax
    movl $1, %ebx
    movl $enterChar, %ecx
    movl $1, %edx
    int $0x80
    ret

_resta:
    movl $4, %eax
    movl $1, %ebx
    movl $displayResta, %ecx
    movl $displayRestaLen, %edx
    int $0x80

    movl $4, %eax
    movl $1, %ebx
    mov %r11d, %ecx
    mov %r14d, %edx
    int $0x80

    movl $4, %eax
    movl $1, %ebx
    movl $enterChar, %ecx
    movl $1, %edx
    int $0x80
    ret

_clear:
    xor %eax, %eax
    xor %ebx, %ebx
    xor %ecx, %ecx
    xor %edx, %edx
    xor %esi, %esi
    xor %edi, %edi
    xor %r8d, %r8d
    xor %r9d, %r9d
    xor %r10d, %r10d
    xor %r11d, %r11d
    xor %r14d, %r14d
    xor %r15d, %r15d
    ret

我正在使用这个 makefile 来创建 .o 和 .exe 文件(由我的教授提供):

#*************************************************
# Executable name : hola
# Version : 2.0
# Created date : February 12, 2019
# Authors : 
#   Eduardo A. Canessa M
# Description : simple makefile for GAS
# Important Notice: To be used for GAS only
#*************************************************
#change the name "ejemplo" for the name of your source file
name=addSubInteger
#program to use as the assembler (you could use NASM or YASM for this makefile)
ASM=as
#flags for the assember
#ASM_F= #*** place here flags if needed ***

#program to use as linker
LINKER=ld

#link executable
$(name): $(name).o
    $(LINKER) -o $(name) $(name).o

#assemble source code
$(name).o: $(name).s
    $(ASM) $(ASM_F) -o $(name).o $(name).s

程序读取第一个用户输入后出现段错误。

这是 the NASM code of my program (希望您不介意其中的西类牙注释,但它与 GAS 上编写的程序本质上是相同的程序)。

我知道我已经编写了一些更高级的意大利面条代码,但这是我得出的解决方案。

最佳答案

movl num1, %edi
call _lenString             #ECX now has num1 length
lea (num1), %esi
call _stringToInt           #EAX now has num1 in integer

第一条指令不加载 %edi 中的地址。您可以像接下来调用 _stringToInt 一样使用 lea。或者,如果您关心更短的编码,请编写 mov $num1, %edi

lea  (num1), %edi
call _lenString             #ECX now has num1 length

第二个数字存在同样的问题:

movl num2, %edi      SAME PROBLEM
xor %ecx, %ecx
call _lenString

_intToString 子例程有 2 个问题!

  1. 您通过在其中写入一个随机值来销毁 %esi 中的地址。
  2. 您(尝试)在内存中写入超出通过 .lcomm buffer, 10 保留的缓冲区。这将破坏 buffer2 中的第一个字节。

由于转换 32 位整数可以产生(最多)10 个字符,因此您需要将缓冲区扩大到 11 个字节,以便可以安全地存储字节大小的终止符。

.lcomm buffer, 11
.lcomm buffer2, 11

然后使用这段代码:

_intToString:
  mov  $10, %ebx
  add  %ebx, %esi      #Instead of 'ADD $10, %ESI' now that EBX==10
  mov  (terminator), %dl
  mov  %dl, (%esi)
.next_digit1:
  xor  %edx, %edx
  div  %ebx
  add  $0x30, %edx             ##
  dec  %esi
  mov  %dl, (%esi)
  test %eax, %eax
  jnz  .next_digit1
  mov  %esi, %eax
  ret

原始 NASM 来源使用

STRING_TERMINATOR   equ 10

equ 在运行时不消耗内存。您的 terminator: .byte 0 确实使用了运行时内存! equ 的一个好的翻译是

.set terminator,0

现在你可以写了

_intToString:
  mov  $10, %ebx
  add  %ebx, %esi      #Instead of 'ADD $10, %ESI' now that EBX==10
  movb $terminator, (%esi)

关于linux - 如何在 GAS 汇编中使用 "repne scasb"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57774233/

相关文章:

php - 使用 pdftotext 将 PDF 文件转换为 PHP 中的文本不会产生干净的输出

c++ - usr/bin/ld : cannot find -l<nameOfTheLibrary>

c - 使用 nop 程序集精确延迟 Arduino?

assembly - 如何在 nasm os 中填满屏幕

c++ - 32 位应用程序中的 64 位功能?

linux - 从 bash 启动 apachectl

python:numpy 运行脚本两次

Linux汇编; bss段内存初始化为0?

assembly - 为什么 x86-64 GCC 函数序言分配的堆栈比局部变量少?

c++ - unsigned int 到 unsigned long long 定义明确吗?