5

新人来了,我已经有一个问题了。

我正在修改 Jeff Duntemann 汇编书中使用的示例代码,并且我想将存储在数据寄存器中的整数值打印到终端?

下面的代码所做的是它可以打印出字符串,可以在 ECX 中推送值,但是当它到达以下内容时:

                pop ecx
                mov eax,4                          
                mov ebx,1                          
                mov edx, ecx

                int 80h  

它不会在终端上显示 edx 的内容,尽管我想我是用 mov eax,4 等告诉它的。

谁能给我任何“指针”(双关语)?

参考代码(自 2012 年 6 月 17 日起修订):

SECTION .data
    submessage: db "I am subtracting 5 from 10!", 10
    msglen: equ $-submessage

    ansmsg: db "Answer is:", 10
    msglen2: equ $-ansmsg
    EOL: db 10

SECTION .bss
    msg: resd 2                                         ; reserve space for 2 dwords

SECTION .text

global _start

_start: nop

;Displays test on shell
                    mov eax,4                           ;print to terminal     
                    mov ebx,1                                      
                    mov ecx, submessage
                    mov edx, msglen
                    int 80h                             ;"I am subtracting 5 from 10!"

                    mov eax,4                           ;print to terminal   
                    mov ebx,1                                            
                    mov ecx, ansmsg
                    mov edx, msglen2
                    int 80h                             ;"Answer is..."

;Subtraction operation below:                  
                            mov edx, 10
                            sub edx, 5
                            mov [msg], edx                ; store 5 in msg


; now we need to print  msg to terminal                            


                            mov eax, 4                  ;print to terminal 
                            mov ebx, 1
                            mov dword [msg+1], 0xa      ;helps prints something out! 

                    ;Encountered problem here= prints out 'Answe' instead of integer '5'

                            push dword 2                ; store size of msg
                            push dword [msg]            ; push to stack contents of msg 
                            int 80h                    

                            add esp, 3                  ;clean stack (2 push calls *4)
                            int 80h

; I like labels :)

sys_exit:                            mov eax,1                   ;exit status
                                    mov ebx,0                        
                                    int 80h  
                                        nop

PS-如果我的行缩进很糟糕,我想知道如何改进它;恕我直言,一旦您克服了最初的学习“驼峰”,学习大会就会变得更有吸引力:)

4

2 回答 2

2

首先,感谢这个问题——它促使我学习 int 80h,这是我以前不熟悉的东西。

您的程序以当前形式做什么?它打印任何东西吗?如果我在脑海中正确执行它,我希望它打印第一条消息然后崩溃。由于缓冲,它甚至可能不会显示可疑崩溃之前的第一条消息。

int 80h/eax=4 映射到 write() 函数。运行“man 2 write”以获取完整文档。函数原型为:

ssize_t write(int fd, const void *buf, size_t count);

所以,eax = 4,ebx = fd,ecx = buf,edx = count。如果您想对代码注释更加迂腐,“mov eax,4”表示“将字符串写入文件”,而“mov ebx,1”表示“指定文件#1,映射到 STDOUT(终端)” .

我希望第一个 int 80h 调用打印一些东西。第二个 int 80h 调用是可疑的。此时 eax 和 ebx 不变。但是, edx 也没有改变,它保存第一个字符串的字符串长度。更大的问题是您将值 5 放入 ecx。ecx 保存指向要写入的字符串的指针,而不是要写入的值。因此,当您将 5 放在那里并调用中断时,int 将尝试打印从地址 0x00000005 开始的字符串。这几乎肯定会导致段错误(崩溃)。

此外,如果我正确阅读了 int 80h 文档,那么您示例中的推送/弹出堆栈操作没有任何相关性。

在 ASM 中将数字打印为字符串有点麻烦。您需要将数字转换为字符串,然后您可以使用 int 80h 打印它。如果您只想打印一个数字,可以使用以下方法作弊:

  • 在 .data 部分中,声明一个名为 int2char: 'int2char: db 0' 的新字节
  • 从 ecx 中减去 5 后,通过添加 48 将数字转换为 ASCII(这会给你 '5' 而不是 5)
  • 将 ecx 存储到 int2char
  • 将 int2char 的地址移入 ecx
  • 将 edx 设置为 1 因为您只想打印 1 个字符

一旦你得到这个工作,转换多于一位的数字将作为练习留给你。

祝你好运!

于 2012-06-09T01:02:30.783 回答
1

我认为推送消息是相关的。这是“hello world”程序的另一个示例。

section     .text
    global _start                       ;must be declared for linker (ld)

_syscall:           
    int     0x80            ;system call
    ret

_start:                         ;tell linker entry point

    push    dword len       ;message length
    push    dword msg       ;message to write
    push    dword 1         ;file descriptor (stdout)
    mov     eax,0x4         ;system call number (sys_write)
    call    _syscall        ;call kernel

                         ;the alternate way to call kernel:
                         ;push   eax
                         ;call   7:0

    add     esp,12          ;clean stack (3 arguments * 4)

    push    dword 0         ;exit code
    mov     eax,0x1         ;system call number (sys_exit)
    call    _syscall        ;call kernel

                         ;we do not return from sys_exit,
                         ;there's no need to clean stack
section .data

    msg     db      "Hello, world!",0xa     ;our dear string
    len     equ     $ - msg                 ;length of our dear string

现在,我没有编写上面的代码,它来自这里:

http://www.cin.ufpe.br/~if817/arquivos/asmtut/index.html#helloworld

所以你可以看到这个作者能够将值移动到堆栈上并调用内核例程,该例程将从堆栈中取出参数。这实际上对我来说更有意义,因为我认为这就是将参数传递给 c 函数的方式。通过特定寄存器传递参数是没有意义的。如果一个方法有 30 个参数,你将如何通过寄存器传递呢?

无论如何,我已经编译了上面的代码,链接它并在我的 macbook pro 上运行它,它运行良好。

不管怎样,和上面的例子一样好,它并没有教给你太多东西。我认为,这里是一个更好的例子,说明如何从寄存器中打印出任意值。

所以我想要的不是预定义的字符串,而是真正的编程构造;一个变量。

很简单,您可以通过执行以下操作来定义一个 32 位变量:

section .bss
    msg:     resd 2

这给了我一个名为“msg”的变量(或内存位置),我可以将内容存储到其中。通过用 resd 声明它,我定义了我想要保留的双字的数量。双字是 32 位,所以我声明我要保留 2 个双字。为什么是2?我一会儿告诉你。

到现在为止还挺好。

现在我需要做的就是将一个值移动到该内存位置并按照 hello, world 中的示例进行操作,对吗?所以我编码了这个:

section     .text
    global _start                       ;must be declared for linker (ld)

_syscall:           
    int     0x80            ;system call
    ret

_start:                     ;tell linker entry point
    mov dword [msg], 'h'    ;move the letter h into my memory location
    mov dword [msg+1], 0xa  ;this is so important, but other author's gloss over it
                            ; the line terminator is essential in order to
                            ; print something out
    push    dword 2         ;message length
    push    dword msg       ;message to write
    push    dword 1         ;file descriptor (stdout)
    mov     eax,0x4         ;system call number (sys_write)
    call    _syscall        ;call kernel

                         ;the alternate way to call kernel:
                         ;push   eax
                         ;call   7:0

    add     esp,12          ;clean stack (3 arguments * 4)

    push    dword 0         ;exit code
    mov     eax,0x1         ;system call number (sys_exit)
    call    _syscall        ;call kernel

                         ;we do not return from sys_exit,
                         ;there's no need to clean stack
section .bss
    msg:     resd 1

所以没有太大的变化。我添加了 .bss 部分,用于未初始化的数据。比静态字符串有用得多。我们正在将一个静态值(字母 h)移动到我的 msg 内存位置,但是您可以像这样轻松地移动一个寄存器

mov [msg], eax

下一行非常重要,但每个“hello world”作者都忽略了它。那 0xa 是标准输出需要的行终止符,否则它不会显示您的值。这让我发疯了很长一段时间,试图弄清楚为什么我的任何东西都不会打印出来。你需要那个行终止符。这也是为什么我们需要定义我们的变量来保存两个值而不是一个。我们需要存储该行终止符。

其余的很简单,将参数扔到堆栈上并调用内核。现在您有了一个真实示例,说明如何从实际的可变数据位置打印出一些任意数据。享受!

于 2012-06-15T23:26:46.497 回答