我尝试使用示例或一本好书来搜索intel x64汇编教程,但我甚至没有在intel站点中找到它。
所以,你能给我一个很好的教程或书吗? 我在linux上使用nasm。
由于
答案 0 :(得分:15)
不可否认,你喜欢学习编程是个人偏见。
但是特别是对于汇编语言,我发现一种方法比阅读指令集参考手册和/或汇编语言书籍(它们存在的地方)更有用。
我通常要做的是弄清楚如何在我尚未使用的操作系统平台上为我所知的新CPU / CPU工作的方法是利用开发人员工具链。像那样:
为目标CPU安装(交叉)编译器和反汇编程序。现在,GNU gcc / binutils无处不在通常意味着gcc
和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。在进行函数调用时保留/ volatile的寄存器。它还会向您展示一些用于初始化常量数据的基本汇编代码,以及像堆栈访问和管理一样的“粘合”。
将此扩展为简单的C语言结构,如循环和if
/ else
或switch
语句。始终保留对外部未定义函数的一些调用,因为这样做会阻止编译器优化器抛出所有“测试代码”,当您使用if()
的{{1}}测试时,{{1}上的谓词(或其他函数参数),因为编译器也无法预测(因此优化代码的构建块“怪异”)。
对此进行扩展以使用包含不同原始数据类型序列的switch()
和argc
定义,以便了解编译器如何在内存中排列这些内容,使用哪些汇编指令进行访问bytes / words / ints / longs / floats等
所有这些测试代码都可以有意改变(例如,使用与struct {}
不同的操作),和/或使其更复杂,以便更多地了解指令集和ABI的特定部分。
完成后,查看输出,找到 Platform ABI 的副本(电子版或非电子版)。其中包含有关如何完成上述操作的原则/为何以这种方式完成的规则手册,它将帮助您了解这些规则适用于特定平台的原因。了解上述内容至关重要,因为当您编写自己的汇编代码时,您必须将其与其他非汇编代码接口(除非是纯演示)。这就是你需要遵守规则的地方,所以即使你不了解它们,至少要知道规则手册的位置。
只有在那之后,我才会建议您实际跟踪特定平台的指令集参考。
那是因为当你完成上面的第一次,那么你已经获得了足够的经验/你已经看到足够的开始使用一个小的C程序,将其编译成汇编源,稍微修改一下,汇编和链接它,看看你的修改是否符合预期。
在那个阶段尝试使用一些更不常见/专业的指令会更容易,因为你已经看到函数调用是如何工作的,需要什么样的粘合代码来将程序集与程序的其他部分连接起来,你已经使用过工具链了,所以你不需要再从头开始了。
即,总结一下,我的建议是从自上而下而不是自下而上学习汇编。
旁注:
为什么我建议在分析编译器生成的汇编代码时使用编译器优化来获得这些简单示例?
好吧,答案就是因为,与某些人相反,如果让编译器优化地狱的话,生成的汇编代码更简单。如果没有优化,编译器通常会创建“愚蠢”的代码,例如将所有变量放入堆栈,保存并从那里恢复它们无缘无故地看到,注册保存/恢复/初始化只是为了覆盖下一条指令以及更多这样的事情。因此,发出的代码量要大得多。它充满了残酷,更难理解。编译器优化强制将这个问题修改为必要的,这是你想要了解的平台ABI和装配。因此,请使用编译器优化。