Java字节码和文件以及机器码可执行文件(例如ELF)的区别是什么?

6
字节码二进制可执行文件(如Java类文件、Parrot字节码文件或CLR文件)与机器码可执行文件(如ELF、Mach-O和PE)之间有什么区别?
两者之间有哪些明显的差异?
例如,ELF结构中的“.text”区域等于类文件中的哪个部分?
它们都有头部,但是ELF和PE头部包含架构信息,而类文件没有。
Java类文件: Java Class file Elf文件: ELF File PE文件: PE File

3
两种类型的格式制作都没有固定规则,这意味着有很多差异很大的格式。换句话说,这个问题实际上无法百分之百地回答。 - Joachim Isaksson
投票关闭,原因是问题过于宽泛。具体来说,需要理解所有提到的格式和执行环境。 - Ciro Santilli OurBigBook.com
可能是重复的问题:https://dev59.com/CXI-5IYBdhLWcg3wlpYM - Ciro Santilli OurBigBook.com
这似乎更像是字节码指令集和汇编语言之间的差异。这更多地涉及可执行文件格式。 - zeitue
3个回答

13

字节码是编译成机器代码之前的中间步骤,就像imulsion所说的一样。由于最后一步留给了加载时间(通常情况下是运行时,比如Just-In-Time(JIT)编译),因此字节码具有架构无关性:运行时(CLR用于.net或JVM用于Java)负责将字节码操作码映射到它们的底层机器代码表示。

相比之下,本地代码(Windows: PE、PE32+、OS X/iOS: Mach-O、Linux/Android等: ELF)是编译后适用于特定架构的代码(Android/iOS: ARM,大部分其他平台: Intel 32位(i386)或64位)。这些都非常相似,但仍需要通过部分(或在Mach-O术语中称为"Load Commands")来设置可执行文件的内存结构,使其成为一个进程(旧版DOS支持".com"格式,即原始内存镜像)。在所有以上的内容中,您可以大致地说以下:

  • 以"."开头的部分是由编译器创建的,并且默认或期望具有默认行为
    • 可执行文件具有主要代码部分,通常称为"text"或".text"。这是可以在特定架构上运行的本地代码
    • 字符串存储在单独的部分中。这些用于硬编码输出(您打印出的内容)以及符号名称。
    • 符号 - 这是链接器用来将可执行文件与其库(Windows: DLL,Linux/Android: Shared Objects,OS X/iOS: .dylibs或框架)组合的元素 - 存储在单独的部分中。通常还有一个"PLT"(过程链接表),使得编译器可以简单地放置到调用的函数(printf、open等)的存根中,链接器可以在可执行文件加载时连接它们。
    • 导入表(在Windows术语中.. 在ELF中,这是一个DYNAMIC部分,在OS X中,这是一个LC_LOAD_LIBRARY命令)用于声明其他库。如果这些库在加载可执行文件时找不到,加载将失败,您将无法运行它。
    • 导出表(用于库/动态库等)是库(在Windows上甚至是.exe文件)可以导出的符号,以便其他人可以链接。
    • 常量通常位于您看到的".rodata"中。

希望这有所帮助。实际上,你的问题有点含糊不清...

TG


6

字节码是一个“中间步骤”。因此,Java编译器(javac)将源代码转换为字节码。机器码是下一步,计算机将获取字节码,将其转换为机器码(可以被计算机读取),然后通过读取机器码来执行您的程序。计算机无法直接读取源代码,同样编译器也无法立即将其翻译成机器码。您需要一个中间步骤来使程序工作。


这并不是他真正询问的内容。 - Joachim Isaksson
这些信息很有帮助,但并没有告诉我结构的差异和相似之处,但这些信息让我想到字节码只是函数调用".text"和变量定义".data"。 - zeitue
实际上,有一些计算机架构可以本地执行Java字节码。 - Antimony

1
请注意,ELF二进制文件不一定需要特定于机器/架构。
有趣的部分是“解释器”头字段:它保存了一个路径名,指向一个加载程序,该程序代替实际的二进制文件执行。然后,这个程序负责加载实际的程序、加载和链接库等。这就是例如ld.so的作用方式。
理论上可以创建一个包含Java字节码(或完整jar)的ELF二进制文件。这只需要一些适当的“解释器”程序,启动JVM并将代码从二进制文件加载到其中。
不确定是否以前真的做过这件事,但肯定是可能的。
同样的事情也可以用于几乎任何非本地代码。
它还可以通过一些VM(如qemu)直接提供多架构支持: 让目标平台(libc+链接脚本)将架构名称放入解释器程序名称中(例如/lib/ld.so.x86_64,/lib/ld.so.armhf,...)。 然后,在特定的架构(例如x86_64)上,具有本地架构名称的那个将指向原始的ld.so,而其他的则指向一些特殊的ld.so,调用类似qemu-system-XXX的东西。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接