繁体   English   中英

为什么这个简单的汇编程序不返回数字?

[英]Why doesn't this simple assembly program return a number?

为什么这个简单的汇编程序不返回数字?

我正在尝试使它打印12进制,但其打印乱码。

global _start

section .text

_start:

    mov eax, 0x4
    mov ebx, 0x1
    mov ecx, var1
    mov edx, 2
    int 0x80

    ;exit the program gracefully
    mov eax, 0x1
    mov ebx, 0x5
    int 0x80

section .data

    var1:   db 0x12

您遇到的主要绊脚石试图将0x12 (ASCII 18)的写入stdout 快速检查ASCII图表将发现此值不可打印。 在汇编中,您只能将字符写入stdout 这就是说,当面对一个数字值时 ,您必须将该值分成数字,然后将数字转换为ASCII表示形式(通过将'0'0x3048位十进制加)。

这与以C或任何其他语言将数字手动转换为字符串没有什么不同。 您基本上可以将原始数字重复除以10 ,每次将剩余的数字作为数字保存到缓冲区中,然后以相反的顺序写出缓冲区,以将数字的ASCII表示形式写到stdout (SO上有很多示例)。

在看一个将所有数字分隔到缓冲区并将它们写入stdout的示例之前,让我们仅介绍示例中的一些基础知识并使其打印任何内容。 您需要牢记在脑中的汇编的第一个概念是,汇编中的所有标签(变量)都指向一个内存地址,而不是数字值。 在装配中分配标签时,例如:

var1  db 0x12

var1指向的存储位置存储一个单字节数据,其值为12-hex var1是指向该内存位置的指针。 在nasm中,如果要在该位置上操作/或引用该 ,则必须通过将指针括在[ ]来将指针解除引用到地址(就像在C中使用*var1解除引用指针一样)。

巩固的下一个概念是sys_write (syscall 4)期望在ecx存储的起始内存地址而不是值。 然后,它将edx数据字节写入ebx (1- stdout )中存储的文件描述符中。 mov荷兰国际集团的地址var1edx将使我们能够在解引用该地址的值通过对操作[edx] 要将单个数字转换为ASCII值,请在其上添加'0' (或0x30),但是如果仅在[edx]添加'0' ,结果将是什么? (当前值0x12 (18)+ 0x30 (48)= 0x42 (66)-恰好是ASCII'B 'B'

(我们还将作弊,并在var1的末尾添加一个newline0xa ),并将其0xa 2个字节,这样我们就不必再为单独的调用弄乱了。)

将其与您的示例放在一起将导致:

section .text

    global _start

_start:

        mov     eax, 4              ; linux sys_write
        mov     ebx, 1              ; stdout
        mov     ecx, var1           ; mov address of var1 to ecx
        add     byte [ecx], 0x30    ; add '0' to first byte of var1
        mov     edx, 2              ; number of chars to print
        int     0x80                ; syscall

        mov     eax, 0x1            ; __NR_exit 1
        xor     ebx, ebx            ; exit code of 0
        int     0x80

section .data

    var1  db 0x12, 0xa  ; ASCII 18 (non-printable) with newline

编译并运行该程序将导致:

$ ./convert
B

现在我们来看一个完整的示例,它将存储在var1处的值的每个数字分开,然后将0x12 (十进制18)的每个数字打印到stdout然后newline (输出转换为十六进制表示形式的工作就交给您了-您可以在网上搜索几个示例, 然后就会想到int 80h.org 。)

nasm中的一个简单示例是:

section .text

    global _start

_start:

        mov     edi, result     ; address of buffer in destination index
        xor     eax, eax        ; zero out eax

        mov     al, [var1]      ; put the value of var1 in al to divide
        mov     bl, 10          ; base 10 divisor to find remainder

        ; separate remainder digits into result buffer
 remloop:
        div     bl              ; divide current value by 10
        mov     [edi], ah       ; move the remainder to result
        cmp     al, 0           ; is the quotient zero?
        je      printchar       ; if it is we are done
        xor     ah, ah
        inc     edi             ; move offset in result string (note digits
        jmp     remloop         ; of answer are stored in reverse order)

    printchar:
        add    byte [edi], 0x30 ; add ascii '0' to digit to get printable char
        mov     eax, 4          ; linux sys_write
        mov     ebx, 1          ; stdout
        mov     ecx, edi        ; point to current digit
        mov     edx, 1          ; number of chars to print
        int     0x80            ; syscall
        dec     edi             ; point to next digit
        cmp     edi, result     ; are we past the final digit?
        jge     printchar       ; if not, keep printing to stdout

        ; print newline
        mov     eax, 4          ; linux sys_write
        mov     ebx, 1          ; stdout
        mov     ecx, newline    ; address of newline
        mov     edx, 1          ; number of chars to print
        int     0x80            ; syscall

        ; exit the program gracefully
        mov eax, 0x1            ; __NR_exit 1
        mov ebx, 0x5            ; exit code of 5
        int 0x80

section .data

    var1  db 0x12       ; ASCII 18 (non-printable)
    result times 8 db 0 ; 8 byte buffer for result
    newline db 0xa      ; newline character

如果您构建代码:

nasm -f elf -o convert.o convert.asm
ld -m elf_i386 -o convert convert.o

然后,您可以显示0x1218 ASCII的ASCII输出:

$ ./convert
18

有关汇编的出色Web参考,请参见汇编语言编程的艺术 阅读。 所有的。 就是那样 尽管它主要是为8086编写的,但所有原理都可100%适用于当前的汇编编程。 唯一的区别是x86_64的寄存器大小,调用约定和系统调用号。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM