【问题标题】:Assembly problem with simple program简单程序的装配问题
【发布时间】:2011-05-06 00:45:58
【问题描述】:

我正在尝试从头开始学习组装。我已经阅读了很多,但即使是我在参考书中找到的以下简单程序也让我感到困惑:

section .data
msg db "Hello!", 0xa
len equ $ - msg
section .text

     global _start

_start:


move edx, len
move ecx, msg
move ebx, 1
move eax, 4
int  0x80
move ebx, 0
move eax, 1
int 0x80

现在显然这应该打印“Hello”。 但我什至不知道在任何阶段发生了什么。 前两个阶段将消息长度和messgae 放在两个寄存器中,不再使用。我不明白为什么。

我不知道为什么需要四个不同的寄存器。

【问题讨论】:

  • 这本书适用于什么处理器?你用的是什么处理器?
  • 我很确定它适用于 x86 处理器。

标签: linux assembly x86 system-calls cpu-registers


【解决方案1】:

int 0x80 是一些(a)类 UNIX 操作系统中用于进行系统调用的机制。

对于这些调用,寄存器用于特定值。来自syscalls 文件:

0 STD NOHIDE { int nosys(void); } syscall nosys_args int
1 STD NOHIDE { void exit(int rval); } exit rexit_args void
2 STD POSIX  { int fork(void); }
3 STD POSIX  { ssize_t read(int fd, void *buf, size_t nbyte); }
4 STD POSIX  { ssize_t write(int fd, const void *buf, size_t nbyte); }

您可以看到数字 4 是 write 调用,需要三个其他参数。数字 1 是exit,只需要返回码。

在进行调用时,eax 是您正在进行的系统调用,而 ebxecxedx 是三个参数(假设它们都是必需的 - 例如,exit 只需要一)。

因此,您可以将代码注释如下:

move edx, len   ; length of message (nbyte).
move ecx, msg   ; message to print (buf).
move ebx, 1     ; file descriptor 1 (stdout).
move eax, 4     ; write syscall.
int  0x80       ; do it.

move ebx, 0     ; exit code (rval).
move eax, 1     ; exit syscall.
int 0x80        ; do it.

(a) 后来的 Linux 版本引入了一个新的接口,它可以使用不同的方法来提供最佳的速度。例如,如果您使用sysenter 而不是int 0x80,某些英特尔芯片会更快。

【讨论】:

  • 那么当我执行 int 80 时,任何寄存器中的任何命令都会被执行?
  • 不,eax 决定 什么 它做了什么,其他寄存器是 eax 指定的特定系统调用可能需要的额外信息。
  • 所以 eax、ebx、ecx 和 edx 不像我认为的那样“通用”,它们有不同的用途吗?我无法移动 eax,味精?
  • 没错,eax是系统调用号本身,ebx是first参数(exit的返回值,读写的文件描述符,fork不使用),ecx是参数(不用于退出,用于读写的缓冲区地址)等等。
  • 作为一个附带问题。我的程序中没有任何内容被“推动”。这是否意味着根本没有使用堆栈?
【解决方案2】:

IIRC int 0x80 指令用于通过使用中断向量来调用系统调用。在您的示例中,ebxeax 中的值用于指定您要调用的系统调用(可能是标准输出上的打印操作)。

系统调用按照惯例知道edxecx 应该包含要打印的内容。

【讨论】:

    【解决方案3】:

    在许多系统上,int 80hsystem call 门。系统调用号位于eaxebxecxedx 包含附加参数:

    move edx, len
    move ecx, msg
    move ebx, 1    ; fd 1 is stdout
    move eax, 4    ; syscall 4 is write
    int  0x80      ; write(1, msg, len)
    move ebx, 0
    move eax, 1    ; syscall 1 is exit
    int 0x80       ; exit(0)
    

    【讨论】:

    • 感谢您的回复。那么 move ebx, 0 和 move eax, 1 有什么作用呢?还有一个问题:为什么我需要指定消息的长度?
    • ebx是传递给exit()的参数,即0(成功)。您需要指定消息的长度,因为write() 适用于任意二进制数据,而不是以空字符结尾的字符串。
    • 我的印象是 eax、ebx、ecx 和 edx 是通用寄存器。使用它们有什么区别?
    • 这只是一个calling convention。寄存器本身并不特殊。
    【解决方案4】:

    当您调用系统调用时,'int' 助记符会产生系统中断。它有点“跳转”到一个系统函数,在这种情况下,它会打印输出(取决于 eax)。

    此中断使用所有这些寄存器来知道要做什么。中断读取 eax,检查您想要的功能并使用其他寄存器来执行此操作。

    eax 是函数号,4 表示 sys_write,将字符串写入流/文件描述符。

    现在它知道你想写一些东西到某个地方,然后它使用其他寄存器来记录这些信息。

    对于 eax = 4 和 int 0x80,这是其他寄存器的含义:

    ebx = 输出 (1 = 标准输出)
    ecx = 字符串的地址
    edx = 字符串的长度

    您可以阅读:

    http://www.intel.com/Assets/ja_JP/PDF/manual/253665.pdf

    第 6.4 节 它有一些关于中断和异常的东西。

    你可以开始编写Intel 80x86汇编代码了,更简单,更容易理解,这里有一些链接:

    助记符/代码表备忘单: http://www.jegerlehner.ch/intel/

    一些介绍网站: http://mysite.du.edu/~etuttle/math/8086.htm http://www.malware.org/teaching/assembly.htm

    【讨论】:

      猜你喜欢
      • 2012-08-10
      • 1970-01-01
      • 1970-01-01
      • 2022-01-01
      • 2014-05-12
      • 2010-11-09
      • 1970-01-01
      • 2022-01-17
      • 2021-10-28
      相关资源
      最近更新 更多