20

我希望学习 x86 的汇编语言。我在 Mac 上,我假设大多数 x86 教程/书籍都使用适用于 Windows 的代码。

运行代码的操作系统如何影响代码的作用,或者确定代码是否有效?我可以按照基于 Windows 的教程,修改一些命令以使其相对轻松地在 Mac 上工作吗?更一般地说,Mac 汇编程序员应该知道什么棘手的问题?谢谢!

4

5 回答 5

21

(当然,以下所有内容仅适用x86 和 x86-64 汇编语言,适用于 IA-32 和 AMD64 处理器和操作系统。)

目前可见的其他答案都是正确的,但在我看来,没有抓住重点。AT&T 与 Intel 语法完全不是问题。任何体面的工具都可以使用这两种语法,或者有对应的或替代的。无论如何,它们的组装方式相同。(提示:你真的想使用 Intel 语法。所有官方处理器文档都这样做。AT&T 语法只是一个令人头疼的问题。)是的,找到传递给汇编器和链接器的正确标志可能很棘手,但你会知道什么时候您已经掌握了它,并且每个操作系统只需执行一次(如果您记得在某处写下来!)。

当然,汇编指令本身完全与操作系统无关。CPU不关心它运行的是什么操作系统。除非您正在做极低级别的黑客(即操作系统开发),否则操作系统和 CPU 如何交互的具体细节几乎完全无关紧要。

外面的世界

当您与外部世界交互时,汇编语言的麻烦就来了:操作系统内核和其他用户空间代码。用户空间是最棘手的:您必须正确使用 ABI,否则您的汇编程序几乎毫无用处。这部分通常不能在操作系统之间移植,除非您使用蹦床/thunk(基本上是另一个抽象层,必须为您打算支持的每个操作系统重写)。

ABI 最重要的部分是 C 风格函数的调用约定。它们是最常见的支持,如果您正在编写汇编,您可能会与之交互。Agner Fog 在他的网站上维护着一些很好的资源;调用约定的详细描述特别有用。在他的回答中,Norman Ramsey 提到了 PIC 和动态库;根据我的经验,如果您不想这样做,通常不必打扰这些。静态链接适用于汇编语言的典型用途(例如重写内部循环或其他热点的核心函数)。

调用约定在两个方向上起作用:您可以从汇编中调用 C 或从 C 中调用汇编。后者往往更容易一些,但差别不大。从汇编调用 C 可以让您使用诸如 C 标准库输出函数之类的东西,而从 C 调用汇编通常是您访问单个性能关键函数的汇编实现的方式。

系统调用

您的程序要做的另一件事是进行系统调用。您可以编写一个从不调用外部 C 函数的完整且有用的汇编程序,但如果您想编写一个不将 Fun Stuff 外包给其他人的代码的纯汇编语言程序,您将需要系统调用。而且,不幸的是,系统调用在每个操作系统上都是完全不同的。您需要的 Unix 风格的系统调用包括(但肯定不限于!)opencreatreadwrite和 all-important exit,以及mmap如果您喜欢动态分配内存。

虽然每个操作系统都不同,但大多数现代操作系统都遵循一个通用模式:将所需的系统调用的编号加载到寄存器中,通常EAX是 32 位代码,然后加载参数(您的操作方式千差万别),最后发出中断请求:它INT 2E适用于 Windows NT 内核或INT 80hLinux 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 上运行,因此您将无法使用某些处理器控制寄存器。(但如果你需要这些,你正在编写操作系统代码,而不是应用程序代码。)除此之外,机器对你来说是赤裸裸的:去计算吧!

于 2009-07-26T10:36:50.710 回答
6

一般来说,只要你使用相同的汇编器,相同的架构(例如,NASM 和 x86-64),你应该能够在 Windows 和 Mac 上进行汇编。

但是,重要的是要记住可执行格式和执行环境可能不同。例如,Windows 可能会以不同于 Mac 的方式模拟/处理某些特权指令,从而导致不同的行为。

于 2009-07-24T01:25:55.373 回答
2

差异的很大一部分在于程序与外界的通信方式。

例如,如果您想向用户显示消息或读取文件或分配更多内存,则必须通过某种系统调用要求操作系统来完成。这将在操作系统之间完全不同。

只要您使用相同的汇编程序,语言语法本身就应该基本相同。不同的汇编程序有时在语法或不同的宏上的顺序略有不同,但没有什么很难习惯的。

于 2009-07-24T01:58:13.210 回答
2

英特尔汇编语言的最大区别在于 AT&T 语法和英特尔语法。您需要一个适用于您的 Mac 的汇编程序,它使用与您使用的任何教程相同的语法。由于我相信 BSD 变体 MacOS Darwin 使用 AT&T 语法,而 Microsoft 汇编器使用 Intel 语法,因此您需要小心。

另一个需要注意的区别是系统的应用程序二进制接口 (ABI),它涵盖了调用约定、堆栈布局、系统调用等。它们在操作系统之间可能存在很大差异,尤其是在涉及与位置无关的代码动态链接时。我有模糊的不愉快记忆,PIC 在 PowerPC MacOS 上特别复杂,但在 Intel 上可能更简单。

一条建议:学习 x86_64(也称为 AMD64)——手工编写汇编代码会更有趣,而且你会更加面向未来。

于 2009-07-24T02:22:15.640 回答
1

当我在一次编程旅游访问期间深入了解 Assembly 时,我在每个教程中遇到的问题是无法以正确的二进制格式编译。大多数教程都给出elf了(对于 Linux)和aoutb(对于 BSD),但是对于后者(逻辑选择?)OS X 抱怨:

ld: hello.o bad magic number (not a Mach-O file)

然而Mach-O作为一种格式失败了,如果你man nasm只得到bin,aoutelf文件格式 -man ld没有更多帮助 -macho是为 OS X 制作 Mach-O 格式的选项:

nasm -f macho hello.asm

在这里写下了旅程(包括一个链接到一个不错的 TextMate 捆绑包,用于组装和其他信息),但是 - 简而言之 - 以上是您需要开始的内容。

于 2009-07-26T09:35:36.247 回答