计算要在x64程序集中打印的字符数

时间:2019-09-08 20:58:16

标签: assembly 64-bit nasm

我一直在研究this nasm code,这是一个玩具程序,可以将命令行参数中的两个数字相加并打印结果。

section .data
    SYS_WRITE  equ 1
    STD_OUT    equ 1
    SYS_EXIT   equ 60
    EXIT_CODE  equ 0
    NEW_LINE   db 0xa
    WRONG_ARGC db "Must be two command line argument", 0xa

section .text
    global _start


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Syscall args
;;
; As we can read in System V AMD64 ABI, the first six function arguments passed in registers. They are:

;     rdi - first argument
;     rsi - second argument
;     rdx - third argument
;     rcx - fourth argument
;     r8 - fifth argument
;     r9 - sixth

; Next arguments will be passed in stack. So if we have function like this:

; int foo(int a1, int a2, int a3, int a4, int a5, int a6, int a7)
; {
;     return (a1 + a2 - a3 - a4 + a5 - a6) * a7;
; }

; Then first six arguments will be passed in registers, but 7 argument will be passed in stack.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Commandline args
;;
; If we run application with command line arguments, all of their will be in stack after running in following order:

;     [rsp] - top of stack will contain arguments count.
;     [rsp + 8] - will contain argv[0]
;     [rsp + 16] - will contain argv[1]
;     and so on...
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

_start:
    pop rcx            ; first thing on the stack is n_args
    cmp rcx, 3         ; make sure that n_arg == 3
    jne argcError

    add rsp, 8         ; increment the stack pointer by a byte, skip argv[0], which is the program name
    pop rsi            ; get argv[1] address
    call str_to_int    ; convert argv[1] to int
    mov r10, rax       ; store the int in r10
    pop rsi            ; get argv[2] address
    call str_to_int    ; convert argv[2] to int
    mov r11, rax       ; store the int in r11
    add r10, r11       ; sum up

    mov rax, r10       ; store sum in rax
    xor r12, r12       ; set r12 to 0
    jmp int_to_str

argcError:
    ;; sys_write syscall
    mov rax, SYS_WRITE
    ;; file descritor, standard output
    mov rdi, STD_OUT
    ;; message address
    mov rsi, WRONG_ARGC
    ;; length of message
    mov rdx, 34
    ;; call write syscall
    syscall
    ;; exit from program
    jmp exit


str_to_int:
    xor rax, rax; ; set rax to 0
    mov rcx,  10 

; A loop for reading argc. For example if rsi points to ‘5’ ‘7’ ‘6’ ‘\000’ sequence, then will be following steps:
;     rax = 0
;     get first byte - 5 and put it to rbx
;     rax * 10 --> rax = 0 * 10
;     rax = rax + rbx = 0 + 5
;     Get second byte - 7 and put it to rbx
;     rax * 10 --> rax = 5 * 10 = 50
;     rax = rax + rbx = 50 + 7 = 57
;     and loop it while rsi is not \000
next:
    cmp [rsi], byte 0   ; check if argv[1] is null byte, if yes, return
    je return_str
    mov bl, [rsi]       ; copy argv[1] into register bl
    sub bl, 48          ; '0' to '9' in ascii is 48 to 57, so we need to subtract by 48
    mul rcx
    add rax, rbx
    inc rsi
    jmp next

return_str:
    ret

;;
;; Print number
;;
print:
    ;;;; calculate number length
    mov rax, 1
    mul r12 ; number of chars
    mov r12, 8 ; why? 8 bits each char?
    mul r12
    mov rdx, rax
    ;;;;

    ;;;; print sum
    mov rax, SYS_WRITE
    mov rdi, STD_OUT
    mov rsi, rsp
    ;; call sys_write
    syscall
    ;;

    ;; newline
    jmp printNewline

;;
;; Print number
;;
printNewline:
    mov rax, SYS_WRITE
    mov rdi, STD_OUT
    mov rsi, NEW_LINE
    mov rdx, 1
    syscall
    jmp exit

;;
;; Convert int to string
;;
int_to_str:
    ;; reminder from division
    mov rdx, 0
    ;; base
    mov rbx, 10
    ;; rax = rax / 10
    div rbx
    ;; add \0
    add rdx, 48
    ;; push reminder to stack
    push    rdx
    ;; increment number of chars
    inc r12
    ;; check factor with 0
    cmp rax, 0x0
    ;; loop again
    jne int_to_str
    ;; print result
    jmp print

;;
;; Exit from program
;;
exit:
    ;; syscall number
    mov rax, SYS_EXIT
    ;; exit code
    mov rdi, EXIT_CODE
    ;; call sys_exit
    syscall

大多数代码简单明了,但是我一直在努力计算要打印的字符数。在我看来,L154已经处理完了计数,为什么在L112中需要再次将其乘以8?有人可以解释一下吗?

谢谢。


0 个答案:

没有答案