首页 文章

操作系统如何影响汇编代码的运行方式?

提问于
浏览
19

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

运行代码的操作系统如何影响代码的作用,或者确定代码是否有效?我可以遵循基于Windows的教程,并修改一些命令,使其适用于Mac相对容易吗?更一般地说,Mac程序集程序员,特别是应该知道的是否有什么棘手的问题?谢谢!

5 回答

  • 2

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

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

    当然,汇编指令本身完全与操作系统无关 . CPU并不关心它做什么操作系统's running. Unless you'做了极低级别的hackery(即OS开发),操作系统和CPU交互的细节几乎完全无关紧要 .

    外面的世界

    当您与外部世界交互时,会出现汇编语言的问题:操作系统内核和其他用户空间代码 . 用户空间是最棘手的:你必须使ABI正确或你的汇编程序几乎没用 . 这部分通常不能在操作系统之间移植,除非你使用trampolines / thunks(基本上是另一层抽象,必须为你想要支持的每个操作系统重写) .

    ABI最重要的部分是C风格函数的调用约定 . 如果您正在编写程序集,它们可能会与之接口 . Agner Fog在his site上保留了几个很好的资源; detailed description of calling conventions特别有用 . 在他的回答中,Norman Ramsey提到PIC和动态库;根据我的经验,如果你不想,你通常不必为此烦恼 . 静态链接适用于汇编语言的典型用法(如重写内循环或其他热点的核心功能) .

    调用约定在两个方向上工作:你可以从C语言中从程序集或程序集中调用C.后者往往更容易但是没有太大的区别 . 从汇编中调用C允许您使用类似C标准库输出函数的东西,而从C调用汇编通常是访问单个性能关键函数的汇编实现的方式 .

    系统调用

    你的程序将做的另一件事是进行系统调用 . 您可以编写一个完整且有用的汇编程序,它永远不会调用外部C函数,但如果您想编写一个没有代码的纯汇编语言程序,那么您将需要系统调用 . 不幸的是,系统调用在每个操作系统上完全不同 . 您需要包含的Unix风格的系统调用(但绝对不限于!) opencreatreadwrite ,以及 exit ,如果您想动态分配内存,则需要 mmap .

    虽然每个操作系统都不同,但大多数现代操作系统都遵循一般模式:您将所需的系统调用次数加载到寄存器中,通常是32位代码中的 EAX ,然后加载参数(如何做到这一点变化很大),以及最后发出一个中断请求:对于Windows NT内核它是 INT 2E 或对于Linux 2.x和FreeBSD(我相信,OSX)是 INT 80h . 然后内核接管,执行系统调用,并将执行返回给您的程序 . 根据操作系统的不同,它可能会将寄存器或堆栈作为系统调用的一部分;您必须确保阅读平台的系统调用文档以确定 .

    SYSENTER

    Linux 2.6内核(我相信,Windows XP和更新版本,虽然我从未在Windows上实际尝试过它)也支持更新,更快的方法来进行系统调用:英特尔在新的Pentium芯片中引入了 SYSENTER 指令 . AMD芯片有 SYSCALL ,但很少32位操作系统使用它(虽然它必须从64位程序进行直接系统调用,所以我不确定这一点) . SYSENTER 设置和使用要复杂得多(例如,见Linus Torvalds on implementing SYSENTER support for Linux 2.6:"I'm a disgusting pig, and proud of it to boot.")我个人可以证明它的特殊性;我曾经写过一个汇编函数,它将 SYSENTER 直接发布到Linux 2.6内核,我仍然不理解各种堆栈和寄存器技巧让它工作......但是它做到了!

    SYSENTER 比发布 INT 80h 要快一些,所以在可用时它的使用是可取的 . 为了便于编写快速和可移植代码,Linux将名为 linux-gate 的VDSO映射到每个程序的地址空间;在此VDSO中调用特殊功能将通过最快的可用机制发出系统调用 . 不幸的是,使用它通常比它的 Value 更麻烦: INT 80h 在一个小的装配例程中要简单得多,无论如何都要调用VDSO,而且你知道你的硬件,所以你可以做到非常不安全事情和问题 SYSENTER 你自己 .

    其他一切

    除了与内核和其他程序交互所带来的需求之外,操作系统之间的差异非常非常小 . 程序集暴露了机器的灵魂:你可以随心所欲地工作,并且在你自己的代码中你不受任何特定调用约定的约束 . 您可以免费使用FPU和SSE单元;您可以 PREFETCH 直接将数据从内存流式传输到L1缓存中,并确保它在您需要时很热;你可以随意挖掘堆栈;如果你想与(正确配置;祝你好运!)外部调试器接口,你可以发出 INT 3 . 这些都不依赖于您的操作系统 . 唯一真正的限制是你在Ring 3运行,而不是在Ring 0运行,因此你将无法使用某些处理器控制寄存器 . (但是如果你需要那些,你就是在编写操作系统代码,而不是应用程序代码 . )除此之外,机器是裸露给你的:出去计算!

  • 6

    一般来说,只要您使用相同的汇编程序和相同的体系结构(例如,NASM和x86-64),您就应该能够在Windows和Mac上组装程序集 .

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

  • 1

    差异的另一个重要部分在于程序如何与外部世界通信 .

    例如,如果要向用户显示消息或读取文件或分配更多内存,则必须通过进行某种系统调用来要求操作系统执行此操作 . 操作系统之间的差别很大 .

    只要您使用相同的汇编程序,语言语法本身应该基本相同 . 不同的汇编程序有时在语法或不同的宏上有不同的排序,但没有什么是太难以习惯的 .

  • 2

    英特尔汇编语言的巨大鸿沟介于AT&T语法和英特尔语法之间 . 你需要一个Mac的汇编程序,它使用与你使用的任何教程相同的语法 . 由于我认为BSD变体MacOS Darwin使用AT&T语法,而Microsoft汇编程序使用Intel语法,因此您需要小心 .

    要注意的另一个区别是系统's Application Binary Interface (ABI), which covers calling conventions, stack layout, system calls, and so on. They may differ substantially between OS',特别是涉及 position-independent codedynamic linking 时 . 我有一些模糊不愉快的回忆,说PIC在PowerPC MacOS上特别复杂,但也许它在英特尔上更简单 .

    一条建议: learn x86_64 (也称为AMD64)-it 's a lot more fun to write assembly code by hand, and you'将会更加面向未来 .

  • 20

    当我在我的一个编程游客访问期间进入大会时,在每个教程中阻碍我的问题是无法以正确的二进制格式编译 . 大多数教程给出 elf (对于Linux)和 aoutb (对于BSD),但后者(逻辑选择?)OS X抱怨:

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

    然而 Mach-O 作为格式失败,如果你 man nasm 只得到 binaoutelf 文件格式 - man ld 不再有用 - macho 是为OS X制作Mach-O格式的选项:

    nasm -f macho hello.asm
    

    wrote up the journey here(包括指向汇编和其他信息的一个很好的TextMate包的链接),但是 - 简而言之 - 以上是您需要开始的 .

相关问题