最基本的x86加法程序

Most basic x86 addition program

我写了下面的基本程序来添加两个数,1+2,如下:

.globl main

main:

    # put 1 (1 byte int/char) into accumulator register
    mov     ,     %eax

    # add 2 (1 byte int/char), storing result in accumulator
    add     ,     %eax

    # move the result of the accumulator into Data register (input/output)
    mov     %eax,   %edx

    ret

编译时,这会 return 预期的输出:

$ gcc d.s -o d2.out && ./d2.out; echo $?
3

我对这个程序有几个问题:

Is this more-or-less an OK program, or am I misusing any of the operations, etc.?

首先,是的。

但是,汇编就是为了效率,所以最后一条语句是不必要的:

mov     %eax,   %edx

Does an assembly file always have to have one globl function such as main

不一定。例如,它可以是您可以从 C/C++ 代码中调用的其他函数。但是如果你想用它制作一个可执行文件,你将需要 main_start 如果你使用 ld 作为你的链接器。

"run the code line-by-line"?

为此您需要一个调试器。如果您想学习汇编,这将是 最重要的事情。你会想要查看寄存器,看看值是如何变化的,标志发生了什么等等。我给出了一个 它解释了一些关于如何设置调试器和单步执行代码的信息。当使用 gcc 来调试你的代码时,你将需要 -g 标记。

一个基本的例子:

  1. -g
  2. 编译
gcc -g file.s -o file
  1. tui 模式启动 gdb。
> gdb --tui ./file
> start           # this will automatically start the program and break at main:
> layout regs     # show registers at the top (you will need this a lot)
> n               # next instruction
> si              # step into, when you use functions, si into function

在gdb中按回车键会自动再次执行上一条命令。这将使您免于一遍又一遍地输入 n。更多命令:

> b 2      # break at line 2
> b func   # break at label func
> b main   # break at main

> print/x  $eax  # print value in eax in hex form, there are other /format specifiers, print/d (decimal), print/s string, print/t (binary)
> x/s $eax    # print string pointed to by eax

> info frame   # look at the current stack frame

这些是您需要的最常见的说明。您可以键入 help command_name 以获取有关命令的更多信息。并且有各种备忘单等可以帮助您。

如果你愿意,你也可以得到一个图形用户界面,我个人不太喜欢它们。检查 Nemiver,这非常好。 gdbgui 可以使用 pip 设置,但它对调试 asm 并不是很好,因为观察寄存器很痛苦。我最喜欢ddd,但它的gui是70年代的,所以...

Finally, what is the best resource for looking up the ops codes?

最好的资源是英特尔手册,但是如果您刚开始阅读它们可能有点太难了。我会推荐 Felix Cloutier's x86 asm referencex86 标签 wiki 中有很多信息和参考资料。

您可能还想阅读 Calling Conventions for Linux and lookup Linux Syscalls which you will be needing quite a lot. If you are going to program or just want to learn more about computers, I would highly recommend reading the Programming from the Ground Up 一本书,该书可免费获取并使用 AT&T 风格的程序集。但是它有点过时,因此您将不得不 google 东西。它有一个包含常见 x86 指令的附录,这将非常有帮助。

mov 到 EDX 是没有意义的,return 值寄存器是 AL / AX / EAX / RAX / RDX:RAX 对于 x86 上从 1 字节到 16 字节的宽度64. EDX 或 RDX 仅涉及宽 return 值,太宽而无法放入 RAX。 (或者在 32 位模式下,64 位值在 EDX:EAX 寄存器对中被 returned,因为没有 RAX。)

这适用于所有标准 x86 32 位和 x86-64 调用约定,包括 GNU/Linux.

上使用的 i386 和 x86-64 System V ABI

如果你正在写一个main,或者任何你想从另一个文件调用的函数,它需要是一个.globl符号。 (除非你 .include "foo.s" 而不是单独构建 + 链接。)这就是它在符号 table 中可见的原因,以便链接器解析对它的引用。例如来自 _start 的已编译代码中的 call maincrt0.o 或其他内容,如果你 运行 gcc -v foo.S,你可以看到 gcc 链接。 (这是一种过度简化;glibc 的 _start 实际上将 main 的地址作为参数传递给 __libc_start_main,它在 libc.so.6 中,所以有一些来自 libc 的代码 运行s 在 main 之前。参见 Linux x86 程序启动 或者 - 我们到底是怎么到达 main() 的?)

如果你正在制作一个没有 CRT 的静态 executable(定义 _start 而不是 main 并制作你自己的 exit_group 系统调用),你 可以 将指令放入文件中,让链接器(ld)选择 .text 部分的顶部作为 ELF如果找不到 _start 符号,则为入口点。 (使用 readelf -a a.out 可以看到类似的信息。)

如果您只打算 运行 GDB 下的程序单步执行您感兴趣的几条指令,您甚至可以省去 exit-cleanly 部分。 (为此,在第一个 user-space 指令之前使用 GDB 的 starti 命令到 运行 带有临时断点,因此您不必通过绝对地址手动设置断点(因为没有符号)。)

$ cat > foo.S
mov  + 2, %edi     # do the math at assemble time
mov 1, %eax         # _NR_exit_group
syscall

$ gcc -static -no-pie -nostdlib foo.S      # like as + ld manually
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000

$ ./a.out ; echo $?
3

$ strace ./a.out
execve("./a.out", ["./a.out"], 0x7ffe0706a3c0 /* 54 vars */) = 0
exit_group(3)                           = ?
+++ exited with 3 +++

如果您的系统是 32 位的,那么 as 默认为 32 位模式,使用 32 位 int [=32=]x80 和不同的寄存器。

Finally, what is the best resource for looking up the ops codes?

我通常会在浏览器选项卡上打开 https://www.felixcloutier.com/x86/, which is an HTML scrape of Intel's vol.2 manual. The original PDF 有一些关于如何阅读条目的介绍章节,所以如果您发现任何符号令人困惑,请检查一下。 Intel 手册中有一些较旧的内容遗漏了 SIMD 指令,所以这对我来说没用,但也许是初学者想要的。

其他资源链接自 x86 tag wiki, including http://ref.x86asm.net/coder64.html,该 x86 tag wiki, including http://ref.x86asm.net/coder64.html 按操作码组织,而不是按助记符组织,并具有快速参考列以提醒您指令是否读取或修改 FLAGS,如果是,是哪个,等等就这样。