8

我试图通过示例或一本好书搜索有关英特尔 x64 汇编教程的信息,但我什至在英特尔网站上都没有找到。

所以,你能给我推荐一本好的教程或书吗?我在linux上使用nasm。

谢谢

4

1 回答 1

15

诚然,您更喜欢如何学习编程是个人偏见。

但特别是就汇编语言而言,我发现一种对我来说比阅读指令集参考手册和/或汇编语言书籍(如果存在的话)更有用的方法。

在我尚未使用过的操作系统平台上,我通常要弄清楚组装如何在我尚未使用的操作系统平台上为我未知的新 CPU/CPU 工作是利用开发人员工具链。像那样:

  • 为自己的目标 CPU 安装一个(交叉)编译器和反汇编器。gcc这些天来,GNU gcc/binutils 无处不在通常意味着objdump -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;
}
  • 使用编译器优化编译这些并反汇编生成的目标代码。
    代码的结构很简单,足以演示ABI的行为方式。函数调用,传递参数返回值管理寄存器空间wrt。进行函数调用时保留/易失的寄存器。它还将向您展示一些用于初始化常量数据的基本汇编代码,以及诸如堆栈访问和管理之类的“粘合”。

  • 将此扩展为简单的 C 语言结构,如循环和if/elseswitch语句。始终保留对外部未定义函数的一些调用,因为这样做会阻止编译器优化器抛出所有“测试代码”,并且当您使用if()的测试时switch(),谓词argc(或其他函数参数),因为编译器无法预测(并因此“奇怪地”优化代码的构建块)。

  • 扩展它以使用struct {}class {}包含不同原始数据类型序列的定义,以了解编译器如何在内存中安排这些,哪些汇编指令用于访问字节/字/整数/长整数/浮点数等。
    所有这些测试您可以故意改变代码(例如,使用与 不同的操作+),和/或使代码更复杂,以便更多地了解指令集和 ABI 的特定部分。

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

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

那是因为当你首先经历了上述内容时,你已经获得了足够的经验/你已经看到了足够的经验,可以从一个小的 C 程序开始,将其编译为汇编源代码,稍作修改,汇编并链接它,然后看看你的修改是否做了它应该做的事情。

在那个阶段尝试使用一些更不常见/专门的指令会容易得多,因为您已经了解了函数调用的工作原理,需要什么样的胶水代码才能将您的程序集与程序的其他部分接口,您'已经使用过工具链,因此您不再需要完全从头开始。

即,总而言之,我的建议是从上到下而不是自下而上学习组装。

边注:

为什么我建议在为这些简单示例分析编译器生成的汇编代码时使用编译器优化?
好吧,答案是因为,与某些人的直觉相反,生成的汇编代码要简单得多如果你让编译器优化一切。如果没有优化,编译器通常会创建“愚蠢”的代码,例如将所有变量放入堆栈,从那里保存和恢复它们,你看不到任何原因,寄存器保存/恢复/初始化只是为了覆盖该 reg 下一条指令和许多更多这样的事情。因此,发出的代码量要大得多。它充满了杂乱无章,更难理解。编译器优化会强制将这些杂乱无章的东西精简到最本质的部分,这是您想要了解的平台 ABI 和汇编的内容。因此,使用编译器优化。

于 2013-03-08T17:06:30.320 回答