日新计划6月更文 Day 27
事情是这样的:
装了个奇怪的 SDK:
paru -S dotnet-sdk-6.0写了段奇怪的代码:
// hello.cs using System; Console.writeLine("Hello Maomao!");编译了一下:
csc hello.cs运行了一下下:
mono hello.exe然后我
ls -a hello.exe一下,发现它的权限是 644 ,想到gcc hello.c -o hello得到的可执行文件权限是 755,然后做出了一件丧尽天良的事:chmod +x hello.exe ./hello.exe然后……它跑起来了!!
所以,它是怎么跑起来的呢?这个“hello.exe”到底是何方神圣?让我们一步一步解开这个问题的答案。
文件类型
在 MS Windows 下,通常使用文件扩展名来判断文件类型,在资源管理器中双击打开一个文件时会根据其扩展名确定使用那个程序打开这个文件,如使用“命令提示符”运行.bat文件、使用系统中安装的视频播放器打开.mp4文件……
其实,不同的文件类型对应的是不同的文件格式,即按照什么格式“理解”这个文件:文件哪一部分是什么含义;怎样分割这个文件;这个类型的文件有什么明显的特征;文件有没有冗余校验;如果有冗余校验,怎样校验……
比如,我们编写以下的 Java 代码:
// Main.java
public class Main{
public static void main(String[] args){
System.out.println("Hello World");
}
}
编译该文件:
javac Main.java
使用hexyl Main.class查看编译结果,可以看到文件开头是一组魔数(Magic Number)CA FE BA BE(十六进制形式),它是 Java 的.class文件的标识。随后的是该文件的“小版本号”(00 00)和“大版本号”(00 3D),即 0 和 61……
在 *nix 操作系统下,可以使用file命令根据不同文件格式的特征来查看文件类型,如:
file Main.class
输出结果为:
Main.class: compiled Java class data, version 61.0 (Java SE 17)
编写以下的 Go 代码:
package main
import "fmt"
func main(){
fmt.Println("Hello Maomao!")
}
查看生成的“hello”文件的类型,结果如下:
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=WXIJwxY0yyBFQrnvkKtp/unUgRoU3MZ9Ea2y Bl5OE/xVw_ZseI5tWrBdIBOAgy/SreV_DJj68_9QKbX5El1, with debug_info, not stripped
这是一个 “ELF” 可执行文件。
我们尝试查看“hello.exe”的文件类型,结果如下:
hello.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections
file告诉我们,这是一个用于 MS Windows 的 “PE32” 可执行文件……
可是……我这是 Arch Linux 啊,喂。为啥能运行 Windows 的 PE32 啊?!
可执行文件
在 GNU/Linux 中,有执行(x)权限的文件可通过在 shell 中给出完整路径的方式来执行,如/foo/bar/a.out,可被执行的文件一般情况下有三种——脚本文件、 可执行的ELF 文件和 a.out文件(Linux Kernel 对a.out文件的支持已被取消,使用 gcc 等工具编译得到的“a.out”文件实际上是 ELF 文件)。
- ELF 是一种文件格式,ELF 格式的文件不一定可被执行,如多数动态链接库(
.so)文件
使用#!(读作SheBang)给出解释器的纯文本文件执行时,Shell 会调用相关解释器执行该脚本,如:
#!/usr/bin/python
print("Hello Maomao")
ELF 就比较复杂了,具有特定的格式,分为可重定位文件、可执行文件、各项目标文件和内核转储文件,但就是不包含 PE 文件……
所以,为啥那个“hello.exe”文件可以运行,我还是找不到答案……