我希望学习x86的汇编语言.我在Mac上,我假设大多数x86教程/书籍都使用适用于Windows的代码.
运行代码的操作系统如何影响代码的作用,或者确定代码是否有效?我可以遵循基于Windows的教程,并修改一些命令,使其适用于Mac相对容易吗?更一般地说,Mac程序集程序员,特别是应该知道的是否有什么棘手的问题?谢谢!
(当然,以下所有适用只到86和x86-64汇编语言,对IA-32和AMD64的处理器和操作系统.)
目前可见的其他答案都是正确的,但在我看来,错过了重点.AT&T与Intel的语法完全没有问题; 任何体面的工具都可以使用这两种语法,或者有一个对应的或替代的.无论如何他们组装相同.(Protip:你真的想要使用Intel语法.所有官方处理器文档都有.AT&T语法只是一个巨大的问题.)是的,找到传递给汇编器和链接器的正确标志可能很棘手,但你会知道什么时候你已经得到了它,你只需要每个操作系统执行一次(如果你记得把它写在某个地方!).
当然,汇编指令本身完全与操作系统无关.CPU 不关心它正在运行的操作系统.除非你正在进行极低级别的hackery(即OS开发),否则操作系统和CPU交互的细节几乎完全无关紧要.
当您与外部世界交互时,会出现汇编语言的问题:操作系统内核和其他用户空间代码.用户空间是最棘手的:你必须使ABI正确或你的汇编程序几乎没用.这部分通常不能在操作系统之间移植,除非你使用trampolines/thunks(基本上是另一层抽象,必须为你想要支持的每个操作系统重写).
ABI最重要的部分是C风格函数的调用约定.它们是最常支持的,如果你正在编写程序集,那么你可能会与它们进行交互.Agner Fog在他的网站上保留了几个很好的资源; 调用约定的详细描述特别有用.在他的回答中,Norman Ramsey提到PIC和动态库; 根据我的经验,如果你不想,你通常不必为此烦恼.静态链接适用于汇编语言的典型用法(如重写内循环或其他热点的核心功能).
调用约定在两个方向上工作:你可以从C语言中从程序集或程序集中调用C.后者往往更容易但是没有太大的区别.从汇编中调用C允许您使用类似C标准库输出函数的东西,而从C调用汇编通常是访问单个性能关键函数的汇编实现的方式.
你的程序将做的另一件事是进行系统调用.您可以编写一个完整且有用的汇编程序,它永远不会调用外部C函数,但如果您想编写一个纯汇编语言程序而不将Fun Stuff外包给其他人的代码,那么您将需要系统调用.不幸的是,系统调用在每个操作系统上完全不同.Unix风格的系统调用,你需要包括(但最稳妥不限于!) ,open
,creat
,read
,write
和所有重要的exit
,随着mmap
如果你喜欢动态分配内存.
虽然每个操作系统都不同,但大多数现代操作系统都遵循一般模式:您将所需的系统调用次数加载到寄存器中,通常EAX
是32位代码,然后加载参数(如何做到这一点变化很大),最后发出中断请求:它INT 2E
适用于Windows NT内核或INT 80h
Linux 2.x和FreeBSD(我相信,OSX).然后内核接管,执行系统调用,并将执行返回给您的程序.根据操作系统的不同,它可能会将寄存器或堆栈作为系统调用的一部分; 您必须确保阅读平台的系统调用文档以确定.
SYSENTER
Linux 2.6内核(我相信,Windows XP和更新版本,虽然我从未在Windows上实际尝试过它)也支持更新,更快的方法来进行系统调用:SYSENTER
英特尔在新的Pentium芯片中引入的指令.AMD芯片有SYSCALL
,但很少有32位操作系统使用它(虽然它是64位的标准,我认为;我不必从64位程序进行直接系统调用,所以我不确定这个) .SYSENTER
设置和使用要复杂得多(例如,参见Linus Torvalds实施SYSENTER
对Linux 2.6的支持:"我是一头令人作呕的猪,为它引导而自豪.")我个人可以证明它的特殊性; 我曾经写过一个SYSENTER
直接发布到Linux 2.6内核的汇编函数,我仍然不理解各种堆栈和寄存器技巧让它起作用......但是工作了!
SYSENTER
比发布要快一些INT 80h
,因此在可用时它的使用是可取的.为了便于编写快速和可移植代码,Linux将VDSO映射linux-gate
到每个程序的地址空间; 在此VDSO中调用特殊功能将通过最快的可用机制发出系统调用.不幸的是,使用它通常比它的价值更麻烦:INT 80h
在一个小的装配程序中这么简单,这值得小速度惩罚.除非你需要最终的性能......如果你需要它,你可能不想再打电话给VDSO,而且你知道你的硬件,所以你可以做出非常不安全的事情并SYSENTER
自己发布.
除了与内核和其他程序交互所带来的需求之外,操作系统之间的差异非常非常小.程序集暴露了机器的灵魂:你可以随心所欲地工作,并且在你自己的代码中你不受任何特定调用约定的约束.您可以免费使用FPU和SSE单元; 您可以PREFETCH
直接将数据从内存流式传输到L1缓存中,并确保它在您需要时很热; 你可以随意挖掘堆栈; INT 3
如果你想与(正确配置;祝你好运!)外部调试器接口,你可以发出.这些都不依赖于您的操作系统.唯一真正的限制是你在Ring 3运行,而不是在Ring 0运行,因此你将无法使用某些处理器控制寄存器.(但是如果你需要那些,你就是在编写操作系统代码,而不是应用程序代码.)除此之外,机器是裸露给你的:出去计算!
一般来说,只要您使用相同的汇编程序和相同的体系结构(例如,NASM和x86-64),您就应该能够在Windows和Mac上组装程序集.
但是,请务必记住可执行格式和执行环境可能不同.例如,Windows可能会以不同的方式模拟/处理某些特权指令,从而导致不同的行为.