x86-64位汇编Linux输入

时间:2014-07-31 01:05:58

标签: linux assembly 64-bit x86-64

我试图输入我的程序......所有这一切都是通过并打印出来的' 0'到屏幕。我很确定PRINTDECI功能是有效的,我之前做过它并且它有效。我只需要循环输入代码,只有在输入某个值时才退出吗?我不确定如何做到这一点......除非它的ACSII价值可能很糟糕......不管怎样,这里是我的代码(Yasm(nasm clone),Intel Syntax) :

GLOBAL _start
SECTION .text

PRINTDECI:
 LEA R9,[NUMBER + 18]       ; last character of buffer
 MOV R10,R9                 ; copy the last character address
 MOV RBX,10                 ; base10 divisor

 DIV_BY_10:

 XOR RDX,RDX                ; zero rdx for div
 DIV RBX                    ; rax:rdx = rax / rbx
 ADD RDX,0x30               ; convert binary digit to ascii
 TEST RAX,RAX               ; if rax == 0 exit DIV_BY_10
 JZ CHECK_BUFFER
 MOV byte [R9],DL           ; save remainder
 SUB R9,1                   ; decrement the buffer address
 JMP DIV_BY_10

 CHECK_BUFFER:

 MOV byte [R9],DL
 SUB R9,1

 CMP R9,R10                 ; if the buffer has data print it
 JNE PRINT_BUFFER 
 MOV byte [R9],'0'          ; place the default zero into the empty buffer
 SUB R9,1

 PRINT_BUFFER:

 ADD R9,1                   ; address of last digit saved to buffer
 SUB R10,R9                 ; end address minus start address
 ADD R10,1                  ; R10 = length of number
 MOV RAX,1                  ; NR_write
 MOV RDI,1                  ; stdout
 MOV RSI,R9                 ; number buffer address
 MOV RDX,R10                ; string length
 SYSCALL
RET

_start:
 MOV RCX, SCORE     ;Input into Score
 MOV RDX, SCORELEN
 MOV RAX, 3
 MOV RBX, 0
 SYSCALL

 MOV RAX, [SCORE]
 PUSH RAX           ;Print Score
  CALL PRINTDECI
 POP RAX

 MOV RAX,60         ;Kill the Code
 MOV RDI,0
 SYSCALL

SECTION .bss
 SCORE:         RESQ 1
 SCORELEN EQU $-SCORE

感谢您的帮助!   - 凯尔

作为旁注,根据DDD,RCX中的指针变成了一个非常大的数字...所以我想我必须让它暂停并等待我输入,但我不知道怎么做......

1 个答案:

答案 0 :(得分:1)

'设置'在x86_64系统上调用syscall 0(READ)是:

@xenon:~$ syscalls_lookup read
read:
        rax = 0  (0x0)
        rdi = unsigned int fd
        rsi = char *buf
        rdx = size_t count

因此,您的_start代码应该类似于:

_start:
    mov  rax, 0         ; READ
    mov  rdi, 0         ; stdin
    mov  rsi, SCORE     ; buffer
    mov  rdx, SCORELEN  ; length
    syscall

x86_64的寄存器约定和系统调用次数完全与i386不同。

您似乎遇到的一些概念性问题:

  • READ不会对您键入的内容做任何解释,您似乎期望它允许您键入数字(例如,57)并让它返回值57.不。它将返回' 5'' 7','输入','垃圾' ...你的SCORELEN可能是8( resq 1)的长度,所以你读取最多8个字节。或者角色,如果你想给他们打电话。除非您键入EOF字符(^ D),否则在READ调用返回到您的代码之前,您需要键入这8个字符。

  • 您必须将收到的字符转换为值...您可以通过简单的方式进行操作并与C库中的ATOI()链接,或者编写自己的解析器通过加法和乘法将字符转换为值(它并不难,请参阅下面的代码)。

以下用作参考:

@xenon:~$ syscalls_lookup write
write:
        rax = 1  (0x1)
        rdi = unsigned int fd
        rsi = const char *buf
        rdx = size_t count

呃......这么多......我只是重写一下:

    global _start
    section .text

PRINTDECI:
; input is in RAX
    lea  r9, [NUMBER + NUMBERLEN - 1 ]  ; + space for \n
    mov  r10, r9            ; save end position for later
    mov  [r9], '\n'         ; store \n at end
    dec  r9
    mov  rbx, 10            ; base10 divisor

DIV_BY_10:
    xor  rdx, rdx       ; zero rdx for div
    div  rbx            : rax = rdx:rax / rbx, rdx = remainder
    or   dl, 0x30       ; make REMAINDER a digit
    mov  [r9], dl
    dec  r9
    or   rax, rax
    jnz  DIV_BY_10

PRINT_BUFFER:
    sub  r10, r9        ; get length (r10 - r9)
    inc  r9             ; make r9 point to initial character
    mov  rax, 1         ; WRITE (1)
    mov  rdi, 1         ; stdout
    mov  rsi, r9        ; first character in buffer
    mov  rdx, r10       ; length
    syscall
    ret

MAKEVALUE:
; RAX points to buffer
    mov  r9, rax        ; save pointer
    xor  rcx, rcx       ; zero value storage

MAKELOOP:
    mov  al, [r9]       ; get a character
    or   al, al         ; set flags
    jz   MAKEDONE       ; zero byte? we're done!
    and  rax, 0x0f      ; strip off high nybble and zero rest of RAX (we're lazy!)
    add  rcx, rcx       ; value = value * 2
    mov  rdx, rcx       ; save it
    add  rcx, rcx       ; value = value * 4
    add  rcx, rcx       ; value = value * 8
    add  rcx, rdx       ; value = value * 8 + value * 2 (== value * 10)
    add  rcx, rax       ; add new digit
    jmp  MAKELOOP       ; do it again

MAKEDONE:
    mov  rax, rcx       ; put value in RAX to return
    ret

_start:
    mov  rax, 0         ; READ (0)
    mov  rdi, 0         ; stdin
    mov  rsi, SCORE     ; buffer
    mov  rdx, SCORELEN  ; length
    syscall

; RAX contains HOW MANY CHARS we read!
; -OR-, -1 to indicate error, really
; should check for that, but that's for
; you to do later... right? (if RAX==-1,
; you'll get a segfault, just so you know!)

    add  rax, SCORE     ; get position of last byte
    movb [rax], 0       ; force a terminator at end

    mov  rax, SCORE     ; point to beginning of buffer
    call MAKEVALUE      ; convert from ASCII to a value

; RAX now should have the VALUE of the string of characters
; we input above. (well, hopefully, right?)

    mov  [VALUE], rax   ; store it, because we can!

; it's stored... pretend it's later... we need value of VALUE!

    mov  rax, [VALUE]   ; get the VALUE
    call PRINTDECI      ; convert and display value

; all done!
    mov  rax, 60        ; EXIT (60/0x3C)
    mov  rdi, 0         ; exit code = 0
    syscall

    section .bss
SCORE:  resb 11   ; 10 chars + zero terminator
SCORELEN equ $-SCORE
NUMBER: resb 19   ; 18 chars + CR terminator
NUMBERLEN equ $-NUMBER

我要说这个应该第一次工作,它对我来说是袖手旁观,还没有经过测试,但它应该是好。我们读取最多10个字符,用零结束它,转换为值,然后转换为ascii并将其写出来。

更合适的是,你应该将寄存器保存到每个子程序的堆栈中,好吧,某些子程序,真的,只有当你要与图书馆接口时...你自己做事让你拥有所有的你想要使用寄存器自由,你只需要记住你放在哪里!

是的,有人会说"为什么你只是乘以10而不是奇怪的添加?" ...呃...因为它在寄存器上更容易,我不必在rdx:rax中设置它。此外,它具有可读性和易懂性,特别是对于评论。随它滚!这不是一场比赛,而是学习!

机器代码很有趣!虽然没有来自编译器的帮助,但还是要把你脑子里的所有鸡蛋都玩弄了!

从技术上讲,你应该检查系统调用的返回结果(RAX)是否为READ和WRITE,适当地处理错误,yadda yadda yadda ....学习使用你的调试器(gdb或其他)。

希望这有帮助。