首页 文章

为什么Windows,Linux等汇编语言之间存在差异?

提问于
浏览
17

我对所有这些低级别的东西,汇编语言相对较新 . 并且想要了解更多细节 . 为什么Linux,Windows汇编语言之间存在差异?

据我所知,当我编译C代码时,操作系统并不真正生成纯机器或汇编代码,它会生成依赖于操作系统的二进制代码 . 但为什么呢?

例如,当我使用x86系统时,CPU只能理解x86 ASM我是对的吗? . 为什么我们不编写纯x86汇编代码以及为什么基于操作系统有不同的汇编变体?如果我们编写纯ASM或OS生成纯ASM,那么操作系统之间是否会出现二进制兼容问题?

我真的很想知道他们背后的所有原因 . 任何详细的答案,文章,书都会很棒 . 谢谢 .

9 回答

  • 5

    除了其他答案 .

    操作系统指示其Application Binary Interface(ABI),其中包括可执行对象的格式 . 这些是Linux(以及许多其他类Unix系统)的Executable and Linkable Format(ELF),以及Windows上的Portable Executable(PE) . 有关其他格式,请参阅this table .

  • 1

    历史上,Linux程序集倾向于使用AT&T语法完成,因为这是GNU Assembler支持的 . 同样,Windows汇编程序倾向于使用Intel语法,如MASMNASM .

    所有x86汇编器都生成相同的输出 - 即x86机器代码 . 你可以在Linux上使用NASM或GNU Assembler在Intel语法下编程,在Windows上使用GNU Assembler在AT&T语法下编程 .

  • 3

    汇编语言与CPU架构有关,而不是O.S.,而是O.S.有一系列以二进制编译的系统函数,汇编程序可以通过中断调用来调用它们 . 例如标准输入输出,操作ecc ....

  • 1

    没有区别 . 如果处理器相同,则汇编代码相同 . 在Windows上编译的x86代码与Linux上的x86代码二进制兼容 . 编译器不产生依赖于操作系统的二进制代码,但它可以以不同的格式(例如,PE与ELF)打包代码 .

    不同之处在于使用了哪些库 . 为了使用OS东西(例如I / O),您必须链接到操作系统的库 . 不出所料,Windows系统库在Linux机器上不可用(除非你当然有Wine),反之亦然 .

  • 0

    操作系统确定了两件事:(1)calling convention,它定义了参数如何进入堆栈并因此影响汇编代码,以及(2)实现常见功能的运行时库,如内存分配,输入/输出,更高 - 等级数学等

    因此,虽然 x+y 在x86处理器上编译为Windows或Linux下的相同汇编代码,但由于不同的调用约定和不同的数学库, y = sin(x) 会有所不同 .

    除此之外,汇编语言本身依赖于处理器 . x86,x86_64,ARM,PowerPC,每个都有自己的汇编语言 .

  • 3

    汇编语言没有区别(虽然汇编程序之间可能存在差异,因此使用的符号也是如此),前提是我们坚持使用x86 . Linux和Microsoft Windows都可以在其他体系结构上运行,在Linux的情况下更是如此 .

    但是,现在的操作系统并不只是将程序加载到内存中而是让它继续运行 . 它提供了大量的服务 . 由于它还保护程序彼此,因此它施加了限制 . 要执行除基本计算之外的任何操作,通常需要通过操作系统 . (对于较旧的操作系统,例如MS-DOS和CP / M,它可能会加载独立运行的程序,但现在几乎每个非嵌入式系统都有一个现代操作系统 . )

    程序也不存储为纯二进制blob . 通常需要链接其他库,通常是在程序加载执行时(例如DLL的工作方式),并且必须与操作系统链接 . 操作系统可能需要其他信息,因此必须在可执行文件中提供有关二进制blob的某种信息 . 这在OS之间有所不同 .

    因此,可执行文件必须采用要加载到内存中的格式,这在操作系统与操作系统之间会有所不同 . 要做任何有用的事情,他们必须进行OS调用,这些调用在系统之间是不同的 . 这就是为什么你不能使用Windows可执行文件和相关的库并在Linux上运行它 .

  • 0

    除非您使用的是嵌入式系统开发环境,否则您将使用针对特定运行时的编译器进行编译 . 该运行时定义了使用硬件的约定:参数传递,异常处理等 . 这些约定与操作系统交互,或者至少与程序需要链接的可用运行时库交互 .

  • 17

    存在一些用于各种平台的汇编程序,在给定源文件的情况下,将直接生成输出二进制文件被加载到特定地址 . 这种组装器已经广泛用于一些小型微控制器,或者用于6502和Z80等历史处理器 . 在组装程序时,有必要知道预期驻留的地址;使用不同的地址需要重新组装程序 . 另一方面,在这样的系统中组装是一步过程 . 在源代码上运行汇编程序并获取可执行输出 . 在某些情况下,可以将源代码,汇编器和输出一次全部存储在内存中(在我的Commodore 64上,我使用的汇编程序发布在Compute的Gazette杂志中,就像那样工作) .

    尽管重新组装所有内容的地址更改可能对于“接管机器”的程序是实用的,但在许多情况下,最好使用多步骤过程,其中源文件被处理为目标代码文件,其中包含汇编指令,但也包含各种关于它们的“象征性”信息;然后以各种方式处理这些文件,以便产生可以直接加载到内存中的内存映像,或者是一个组合的可重定位目标文件,操作系统的加载器将知道如何调整它可能加载到的任何地址 .

    为了使对象链接系统有用,它必须允许延迟某些类型的地址计算,直到链接或加载程序 . 一些系统仅允许在链路/加载时执行极其简单的计算,而其他系统允许更复杂的计算 . 当它们可行时,更简单的方案可能更有效,但它们的局限性可能会迫使解决方法 . 例如,将使用BX循环小于256字节的数据结构的例程可能被写为:

    mov bx,StartAddr
    

    lp:mov al,[bx] ...做一些计算inc bx cmp bl,<(StartAddr Length); <prefix operator表示“jnz lp的LSB”

    可以使用 cmp bx,(StartAddr+Length) ,但如果编译工具可以支持它,那么仅比较低字节会更快 . 另一方面,某些16位汇编/链接工具可能要求所有地址修复都使用存储在代码中的16位地址来完成 .

    由于不同的系统允许其对象代码格式中的不同功能,因此它们需要在其汇编语言中使用不同的功能来控制它们 . 指令集可以由芯片制造商指定,但是用于表示可重新定位的地址计算的特征通常不是 .

  • 4

    好吧,你不要直接组装 . 代码必须采用某种可执行格式:windows使用PE,大多数Unices现在都使用ELF(尽管还有其他的,比如a.out) .

    基本组装说明是相同的,您使用它们创建的功能是相同的 .

    问题在于访问其他资源 . 处理器非常擅长计算,但无法访问硬盘,或者无法在屏幕上打印字符或连接到蓝牙手机 . 这些元素总是以某种方式依赖于操作系统 . 它们是根据系统调用实现的,其中处理器通知操作系统执行某项任务 . linux上的任务编号17不一定是Windows上的任务17;他们甚至可能没有等价物 .

    由于大多数库都有一些最低级别的系统调用,这就是为什么不能在每种情况下重新编译代码的原因 .

相关问题