最基本的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
我对这个程序有几个问题:
- 这或多或少是一个不错的程序,还是我误用了任何操作等?
- 程序集文件是否总是必须具有一个
globl
函数,例如 main
,或者是否可以删除 main
/ .globl main
部分只是“运行 逐行代码”?
- 最后,查找操作代码的最佳资源是什么?我倾向于 Google 这些和它 return 不同的结果:如果有一个像
Python docs
这样的标准资源会很好,我可以只在一页上添加书签并查看那里的所有内容。
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
标记。
一个基本的例子:
- 用
-g
编译
gcc -g file.s -o file
- 以
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 reference。 x86
标签 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 main
,crt0.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,如果是,是哪个,等等就这样。
我写了下面的基本程序来添加两个数,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
我对这个程序有几个问题:
- 这或多或少是一个不错的程序,还是我误用了任何操作等?
- 程序集文件是否总是必须具有一个
globl
函数,例如main
,或者是否可以删除main
/.globl main
部分只是“运行 逐行代码”? - 最后,查找操作代码的最佳资源是什么?我倾向于 Google 这些和它 return 不同的结果:如果有一个像
Python docs
这样的标准资源会很好,我可以只在一页上添加书签并查看那里的所有内容。
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
标记。
一个基本的例子:
- 用
-g
编译
gcc -g file.s -o file
- 以
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 reference。 x86
标签 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 main
,crt0.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,如果是,是哪个,等等就这样。