2013-03-08 43 views
7

我试图寻找关于用例子或一本好书英特尔的x64汇编教程,但我甚至没有在英特尔网站上找到。英特尔X86-64组装教程或书

所以,你可以建议我一个很好的教程或书吗? 我在linux上使用nasm。

感谢

+2

我不了解有关nasm教程,但可以获取[Intel Software Developer Manuals](http://www.intel.com/content/www/us/en/processors/architectures-software-developer- manuals.html),你应该发现很有帮助。 – 2013-03-08 15:48:14

+0

intel.com上的教程?这听起来像个可笑的玩笑。从[Paul Carter的教程](http://www.drpaulcarter.com/pcasm/)开始。令人惊讶的是,它基于NASM。当您掌握32位程序集时,请切换到64. – 2013-03-08 16:27:52

+0

我推荐Ray Seyfarth介绍适用于Linux的64位英特尔汇编语言编程。本书使用YASM而不是NASM,但YASM接受AFAIK几乎所有的NASM代码,并且还支持DWARF2调试数据格式等。本书也有一些SSE和AVX代码,并且它只假定一些编程背景并解释二进制和十六进制数字等。我已经在DOS时间学习了x86程序集,但是本书可以作为Linux x86-64程序集中有用的参考。 – nrz 2013-03-08 21:39:05

回答

15

诚然这是个人的偏见,你怎么喜欢学习编程。

但相对于在特定的汇编语言,我发现了一个办法这对我来说已经不是阅读汇编语言指令集参考手册和/或书籍(如有的话)更为有用。

我通常做找出如何组装一个新的CPU工作/一个CPU不知道的我一个操作系统平台,我不还制作是利用开发工具链。像这样:

  • 为自己安装目标CPU的(交叉)编译器和反汇编程序。现在,GNU gcc的/ binutils无处不在通常意味着这是gccobjdump -d

  • 创建一批的小程序/小片的源代码,如:

extern int funcA(int arg); 
extern int funcB(int arg1, int arg2); 
extern int funcC(int arg1, int arg2, int arg3); 
extern int funcD(int arg1, int arg2, int arg3, int arg4); 
extern int funcE(int arg1, int arg2, int arg3, int arg4); 
extern int funcF(int arg1, int arg2, int arg3, int arg4, int arg5); 
extern int funcG(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6); 
extern int funcH(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, 
       int arg7); 

int main(int argc, char **argv) 
{ 
    printf("sum of all funcs: %d\n", 
     funcA(1) + funcB(2, 3) + funcC(4, 5, 6) + funcD(7, 8, 9, 10) + 
     funcE(11, 12, 13, 14, 15) + funcF(16, 17, 18, 19, 20, 21) + 
     funcG(22, 23, 24, 25, 26, 27, 28) + funcH(29, 30, 31, 32, 33, 34, 35)); 
    return 12345; 
}
  • 编译这些与编译器优化和拆卸生成目标代码。
    代码的结构非常简单,足以说明如何运行。到函数调用,传递参数返回值,管理寄存器空间 wrt。在进行函数调用时寄存器被保留/不稳定。它还会显示一些用于初始化常量数据的基本汇编代码,以及像堆栈访问和管理那样的“粘合”。

  • 将此扩展为简单的C语言结构,如循环和if/elseswitch语句。始终保持对外部未定义功能几个电话,因为这样做会防止编译器优化,从扔你所有的“测试代码”出来的,而当你使用if()测试switch(),对argc(或其他功能参数)谓词,因为编译器无法预测即(并且因此优化代码的“构建块”)。

  • 延伸此使用含有不同的基本数据类型的序列struct {}class {}定义,以便找出编译器在存储器中,其中汇编指令用于访问字节/字/整型/多头/浮标如何安排这些等
    所有这些作品的测试代码,你可以故意改变(例如,使用不同的操作比+),和/或进行更复杂,以了解更多有关指令集和ABI的某些片段。

你做了之后,看着在输出中,找到平台ABI的副本(电子或没有)。其中包含规则手册,说明如何完成上述工作/为什么完成上述工作,它将帮助您了解这些规则适用于特定平台的原因。了解上述内容非常重要,因为在编写自己的汇编代码时,必须将其与其他非汇编代码(除非用于纯演示)进行交互。这就是你需要遵守规则的地方,所以即使你不了解他们,至少知道规则手册在哪里。

只有在那之后,我才建议你实际追踪特定平台的指令集参考。

这是因为,当你通过上述第一消失了,那么你已经有足够的经验/你已经看到已经足以启动一个小的C程序,编译它到汇编源,修改了一下,组装并链接它,看看你的修改是否做它应该做的。

试图在这个阶段使用一些更少见的/专门的指令会容易得多,因为你已经看到了函数调用是如何工作的,需要什么样的粘合代码来将你的程序集与界面的其他部分程序,你已经使用了工具链,所以你不需要从头开始完全。

即,要总结这一切,我的建议是从学习汇编自上而下,而不是从下往上

旁注:

为什么我建议使用编译器优化这种简单的例子分析编译器生成的汇编代码是什么时候?
那么,答案是因为,违反直觉的一些,生成的汇编代码是简单得多如果你让编译器优化地狱的东西。没有优化,编译器通常会创建“愚蠢的”代码,例如将所有变量放入堆栈,从那里保存并恢复它们,无论您看到什么原因,注册保存/恢复/初始化只是为了覆盖下一条指令以及更多这样的内容。因此,发射的代码量要大得多。它夹杂着粗糙,而且很难理解。编译器优化可以将这个问题修剪到最基本的部分,这就是你想要了解平台ABI以及可以理解的东西。因此,使用编译器优化。

+0

这是一个非常好的idead/answer。非常感谢。 – gideon 2014-02-18 07:03:44

+0

这是我见过的最好的答案之一。 – 2016-05-07 07:55:13