⚠️阅读前请注意
- 本博客适用于Cosmopolitan Libc 3.X版本,不适用于Cosmopolitan Libc 2.X版本。
- Cosmopolitan Libc 是一个非常年轻的项目,可能存在各种问题。
- Cosmopolitan Libc 仍处于快速迭代开发之中,本文内容在一定时期内会持续更新。
Cosmopolitan Libc 工作原理 与 多平台使用
目录
Cosmopolitan Libc 简介
Cosmopolitan Libc makes C a build-anywhere run-anywhere language, like Java, except it doesn’t need an interpreter or virtual machine. Instead, it reconfigures stock GCC and Clang to output a POSIX-approved polyglot format that runs natively on Linux + Mac + Windows + FreeBSD + OpenBSD + NetBSD + BIOS on AMD64 and ARM64 with the best possible performance.
要点提炼
Cosmopolitan Libc 是由 Justine Tunney 主导开发的一款跨架构跨平台的 C 标准库。配合 Cosmopolitan 编译工具链,用户编译出的二进制文件可以在不同架构的不同系统平台上原生运行,包括 AMD64 和 ARM64 架构上的 Linux、macOS、Windows、FreeBSD、OpenBSD、NetBSD 和 BIOS 环境。
Cosmopolitan 项目主页:https://justine.lol/cosmopolitan/index.html
Cosmopolitan 代码仓库:https://github.com/jart/cosmopolitan
Cosmopolitan 项目是什么?
- Cosmopolitan 项目的核心是一个 C 标准库 —— Cosmopolitan Libc;
- 项目还包含一个基于 LLVM 的 C++ 标准库和一套基于 GCC 和 Clang 的 C/C++ 编译工具链;
- 该标准库和工具链构建出的 C/C++ 程序为 APE 格式,可以在不同架构的不同系统平台上原生运行;
- 项目提供预编译的标准库和APE格式的工具链,将 C 语言转变为一种“随处构建,随处运行”的语言。
Cosmopolitan Libc 3.3.3 的平台支持表
Platform | Min Version | Circa |
---|---|---|
AMD | K8 Venus | 2005 |
Intel | Core | 2006 |
Linux | 2.6.18 | 2007 |
Windows | 8 | 2012 |
Darwin (macOS) | 23.1.0+ | 2023 |
OpenBSD | 7 | 2021 |
FreeBSD | 13 | 2020 |
NetBSD | 9.2 | 2021 |
跨平台运行原理
习惯上,我们把可执行文件与库文件统称为二进制文件
- Windows平台原生支持的二进制文件为
PE
格式(Portable Executable 可移植的可执行文件)- Unix平台原生支持的二进制文件为
ELF
格式(Executable and Linkable Format 可执行并可链接的文件格式)
为了顾及上述两个平台的兼容性,Cosmopolitan工具链
编译出的二进制文件为APE
格式(Actually Portable Executable 确实可移植的可执行文件)。Justine Tunney之所以取这个名字,可能是为了调侃Windows的PE
格式名不副实,不够“可移植”。
APE
格式的程序能同时在Windows与Linux平台运行,它的绝妙之处在于以下两点:
1.APE
本身就是一种特殊的Windows PE
格式,可以在Windows系统上原生运行。
2.APE
的文件开头处存放了一段Unix Shell
脚本,而Shell会将未知格式的文件当做Shell脚本执行。
APE
是一种完全合法的Windows PE
格式。APE
不仅记录了程序运行时所必需的二进制信息,还利用PE
文件开头的DOS Header段与DOS Stub段等空间来存放Shell脚本,并利用了PE
结尾的空白空间来存放ELF
文件内容。
因此在Windows平台下,APE
格式的文件会被当作正常的PE
格式文件执行。读者可以通过PE-bear(Windows)或readpe(Linux)工具查看它的具体结构。
APE
也是一段可以执行的Unix Shell
脚本。根据Unix Shell的特性,用户若在Unix平台直接执行未知格式的文件,文件会被默认当做Shell脚本执行。此时位于APE
开头的Shell脚本,将会发挥它的作用:
- 脚本自动将APE Loader解压到本地特定目录下,并运行解压后的APE Loader;
- APE Loader读取
APE
中存储的ELF
文件头与段信息; - APE Loader执行
mmap
操作,把藏在APE
文件里的ELF
信息加载到内存中; - 内存中的程序被Unix系统内核调度运行。
Unix平台下,我们可以通过Shell的调试手段来观察APE
文件的执行原理
sh -x APE格式的可执行文件
在 x64 Linux / WSL2 平台使用 Cosmopolitan Libc
参考测试环境
- Ubuntu 22.04.3 LTS
获取Cosmopolitan工具链
若想直接体验Cosmopolitan Libc的功能,可以按照其官网主页的指示,直接获取编译好的Cosmopolitan编译工具链
。按如下命令执行,工具链将被放置在./cosmocc/
目录,下列命令执行后,当前工作路径即为该目录。
mkdir cosmocc
cd cosmocc
wget https://cosmo.zip/pub/cosmocc/cosmocc.zip
unzip cosmocc.zip
和GCC编译工具链类似,Cosmopolitan编译工具链
主要包含
- C编译器
bin/cosmocc
工具链的C编译器。为提供跨架构跨平台支持,该文件实际上是一个Shell脚本。在构建目标时,其会调用bin/
目录下具体针对不同架构、不同平台的C编译工具。编译工具均为APE
格式。 - C标准库
x86_64-linux-cosmo/lib/libcosmo.a
与aarch64-linux-cosmo/lib/libcosmo.a
工具链中,针对不同架构的静态C标准库。Cosmopolitan Libc本体。静态库中被归档的二进制对象文件均为ELF
格式。 - C++编译器
bin/cosmoc++
工具链的C++编译器。为提供跨架构跨平台支持,该文件实际上是一个Shell脚本。在构建目标时,其会调用bin/
目录下具体针对不同架构、不同平台的C++编译工具。编译工具均为APE
格式。 - C++标准库
x86_64-linux-cosmo/lib/libcxx.a
与aarch64-linux-cosmo/lib/libcxx.a
工具链中,针对不同架构的,基于Cosmopolitan Libc与LLVM项目的静态C++标准库。静态库中被归档的二进制对象文件均为ELF
格式。 - C/C++头文件目录
include/
工具链的C/C++头文件存放与搜寻目录。 - 其它构建工具
工具链所需要的构建工具,如bin/make
、bin/ctags
等。构建工具均为APE
格式。
测试运行APE文件
执行以下命令,测试您的系统能否运行APE
格式的可执行文件。以工具链中的bin/make
工具为例
bin/make --version
参考输出
GNU Make 4.4.1
Built for x86_64-linux-cosmo
Copyright (C) 1988-2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Zsh 与 Fish 用户
如果出现zsh: exec format error
,则需要升级到 zsh 5.9 以上版本。Fish 也是如此。
若要临时运行单个APE
文件,根据APE
文件的运行原理,我们可以显式地把APE
文件当作Shell脚本运行sh bin/make --version
WSL2 用户
如果出现error: APE is running on WIN32 inside WSL. You need to run: sudo sh -c 'echo -1 > /proc/sys/fs/binfmt_misc/WSLInterop'
,代表WSL2启用了WSLInterop支持。
若WSL2启用了该支持,当WSL2检测到可执行文件属于Windows原生程序(如PE
文件)时,会以WIN32方式执行该文件,因此会报错。
若要彻底解决此问题,请参见“安装 APE Loader 并配置 binfmt_misc”一节。
若要临时运行单个APE
文件,根据APE
文件的运行原理,我们可以显式地把APE
文件当作Shell脚本运行sh bin/make --version
【可选】安装 APE Loader 并配置 binfmt_misc
WSL2 用户
若WSL2启用了WSLInterop支持(应当默认启用),当WSL2检测到可执行文件属于Windows原生程序(如PE
文件)时,会以WIN32方式执行该文件。
为了在不影响WSLInterop功能的情况下运行APE
文件,请WSL2使用者务必执行该步骤。
Wine 用户
如果Linux中安装了Wine,且Linux会自动通过Wine运行PE
可执行文件,则必须要执行该步骤。
执行该步骤后,APE
文件会被绑定到APE Loader。以后运行APE
文件时,系统会直接将该文件交给APE Loader运行。
根据跨平台运行原理所述,在Unix平台上运行APE
文件时,该文件会被当作Shell脚本执行,并且自动运行APE Loader。在此过程中,APE
文件会作为入参被传递给APE Loader,由其提取并加载文件中的ELF信息到内存。
若Unix平台未安装APE Loader(即ape
命令不存在),则Shell脚本会自动将APE Loader解压到本地。解压位置规则如文档所描述,典型位置是$HOME/.ape*
或$TMPDIR/.ape*
或./.ape*
。
如果Unix平台已经安装APE Loader,ape
命令已存在,则APE
文件会直接调用ape
命令。我们可以执行下列命令,把APE Loader安装到Linux系统的/usr/bin/ape
位置
sudo wget -O /usr/bin/ape https://cosmo.zip/pub/cosmos/bin/ape-$(uname -m).elf
sudo chmod +x /usr/bin/ape
binfmt_misc是Linux内核提供的一个功能特性——用户可以指定使用某个程序去运行某类文件。效果上有点类似于Unix的Shebang或者Windows的“文件默认打开方式”。
执行以下命令,可以临时指定使用/usr/bin/ape
程序来运行APE
格式文件(靠文件头的magic number识别),重启后失效
sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
sudo sh -c "echo ':APE-jart:M::jartsr::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
当我们再次从Shell中运行bin/make
文件时,系统将自动运行/usr/bin/ape bin/make
命令。不再需要依靠APE
文件头部的Shell脚本去运行APE Loader。
永久性配置binfmt_misc
下文介绍了如何在Linux系统(包括WSL2)上永久性配置binfmt_misc。
非 WSL2 用户
对于使能了systemd-binfmt.service
服务的Linux发行版,可以在/etc/binfmt.d/
目录下创建相应的注册项,然后重启systemd-binfmt.service
服务,即可永久生效。sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/etc/binfmt.d/ape.conf" sudo sh -c "echo ':APE-jart:M::jartsr::/usr/bin/ape:' >/etc/binfmt.d/ape-jart.conf"
重启
systemd-binfmt.service
服务sudo systemctl restart systemd-binfmt.service
WSL2 用户
WSL2的WSLInterop机制可能会与systemd-binfmt.service
服务、update-binfmt
等常见binfmt配置方式冲突。考虑到不同WSL2版本间的兼容性,最好不要修改相关配置。
下面介绍一种非侵入式的配置方式。我们为APE
自定义一个binfmt注册服务。创建一个新的服务文件/etc/systemd/system/ape-binfmt-register.service
,文件内容如下[Unit] Description=Register APE binfmt_misc entries after shell login After=multi-user.target After=getty@tty1.service ConditionPathExists=/proc/sys/fs/binfmt_misc [Service] Type=oneshot ExecStart=/bin/bash -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register && echo ':APE-jart:M::jartsr::/usr/bin/ape:' >>/proc/sys/fs/binfmt_misc/register" RemainAfterExit=true [Install] WantedBy=multi-user.target
上述服务只在Shell准备就绪后,且
/proc/sys/fs/binfmt_misc
存在时运行。服务运行时会自动为APE
文件注册binfmt_misc项目。执行如下命令,让服务开机自启sudo systemctl daemon-reload sudo systemctl enable ape-binfmt-register.service # 如果不再希望自启该服务 # sudo systemctl disable ape-binfmt-register.service
在PowerShell中执行
wsl --shutdown
停止运行所有WSL2发行版,然后重新打开刚刚配置好WSL2发行版。此后,APE
程序应当能成功运行。这种方法不会影响WSLInterop的正常功能。
编译运行 Hello World
确认APE
文件可以正常运行后,创建一个简单的测试用的C文件hello.c
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("hello world\n");
}
和GCC编译工具链的使用方式一致,使用bin/cosmocc
C语言编译器来编译hello.c
,得到APE
格式的./hello
可执行文件
bin/cosmocc -o hello hello.c
跨平台运行
x64 Linux / WSL2
得到APE
格式的./hello
可执行文件后,首先尝试在本平台运行
./hello
x64 Windows
将其复制到Windows系统下。更改其文件名为hello.exe
,增加Windows可执行文件的后缀.exe
。尝试在PowerShell或者CMD中运行
.\hello.exe
在Windows平台运行APE
文件时,可能会触发Windows Defender的报警。请仔细阅读如下Issue。若信任Cosmopolitan项目,请将APE
文件列入Windows Defender白名单,再尝试运行。
https://github.com/search?q=repo%3Ajart%2Fcosmopolitan+windows+defender&type=issues
在 x64 Windows 平台使用 Cosmopolitan Libc
环境需求
使用 Cosmopolitan Libc 3.X 编译的程序,需要在 Windows 8 以上的 Windows 系统中才能正常运行。
参考测试环境:Windows 11
+ Windows Terminal
+ PowerShell 7
准备 Shell 环境
使用Cosmopolitan编译工具链来编译C/C++代码,是体验Cosmopolitan Libc最简单的方式。通过Cosmopolitan编译工具链编译出的可执行文件,可以跨架构跨平台运行。
打开PowerShell
,在你喜欢的位置创建cosmos/
目录。在本文中,该目录用于存放Cosmopolitan Libc的开发环境
mkdir cosmos
cd cosmos
Cosmopolitan编译工具链需要Shell环境才能正常运行。因此在这一步,我们首先从Cosmos文件网站下载APE
格式的Shell解释器与Unix常用的命令行工具。
# bin目录用于存放APE可执行文件,如Shell解释器和命令行工具等
mkdir bin
# 下载APE格式的wget可执行文件(命令行下载工具)
Invoke-WebRequest https://cosmo.zip/pub/cosmos/bin/wget -OutFile bin\wget
# 下载APE格式的bash可执行文件(Shell解释器)
Invoke-WebRequest https://cosmo.zip/pub/cosmos/tiny/bash -OutFile bin\bash
运行刚才下载好的Shell解释器bin\bash
,进入Shell环境
Start-Process -FilePath bin\bash -Wait -NoNewWindow
在Shell环境中,使用刚才下载好的bin/wget
命令行下载器,下载编译工具链所需的更多命令行工具。
这一步下载文件较多(约140个文件,按字母顺序下载),请保持网络畅通。--reject=
后面指定了排除在外的文件,如果之后需要使用它们,可以单独下载。
bin/wget -r -nH --cut-dirs=3 --no-parent --reject="index.html*, datasette, emacs*, llama, python, redbean, sqlite*, verynice" -P ./bin https://cosmo.zip/pub/cosmos/tiny/
Shell解释器与Unix常用的命令行工具下载完毕后,在当前工作目录下创建Bash启动脚本.bashrc
。之后每次启动Bash解释器时,解释器都会自动将bin
目录加入Shell环境变量PATH
。这样一来,Shell就能找到我们安装的命令行工具。
执行完毕后,结束Shell解释器的运行,退出Shell环境。
# 设置Shell环境变量PATH与HOME
echo "export PATH=$PWD/bin"$'\nexport HOME=~' > ./.bashrc
# 结束Shell解释器的运行,退出Shell环境
exit
再次进入 用于开发Cosmopolitan Libc程序的 Shell环境
今后需要开发基于Cosmopolitan Libc的程序时,可以按照以下方法进入刚才配置好的Shell环境。
通用方法
打开PowerShell
,进入用于存放Cosmopolitan Libc开发环境的cosmos/
目录cd cosmos
运行Shell解释器
bin\bash
,进入Shell环境Start-Process -FilePath bin\bash -Wait -NoNewWindow
在Shell解释器中执行
exit
命令,即可结束运行Shell解释器,退出Shell环境。
Windows Terminal 用户
如果您使用了Windows Terminal,可以为用于开发基于Cosmopolitan Libc的程序的Shell环境创建一个新的Profile
属性 值 Name cosmos
Command line cosmos目录的绝对路径\bin\bash
Starting directory cosmos目录的绝对路径
获取Cosmopolitan工具链
首先,以管理员身份运行PowerShell
或Windows Terminal
,进入用于开发基于Cosmopolitan Libc的程序的Shell环境。
Cosmopolitan工具链的压缩包中含有一些Unix符号链接。在执行下文的
unzip
解压命令时,创建符号链接操作需要管理员权限,因此这一步会涉及到“以管理员身份运行”操作。
今后使用工具链时,若要创建符号链接,同样需要以管理员身份运行,否则会报权限错误。下面展示了一个创建符号链接的案例# 若要使用Shell环境中的Vim,需要提供为其sh命令。可以为其创建符号链接 cd ~/bin ln -s bash sh
在Shell环境中,按照Cosmopolitan官网主页的指示,直接获取编译好的Cosmopolitan编译工具链
。按如下命令执行,工具链将被放置在./cosmocc/
目录,下列命令执行后,当前工作路径即为该目录。
mkdir cosmocc
cd cosmocc
wget https://cosmo.zip/pub/cosmocc/cosmocc.zip
unzip cosmocc.zip
和GCC编译工具链类似,Cosmopolitan编译工具链
主要包含
- C编译器
bin/cosmocc
工具链的C编译器。为提供跨架构跨平台支持,该文件实际上是一个Shell脚本。在构建目标时,其会调用bin/
目录下具体针对不同架构、不同平台的C编译工具。编译工具均为APE
格式。 - C标准库
x86_64-linux-cosmo/lib/libcosmo.a
与aarch64-linux-cosmo/lib/libcosmo.a
工具链中,针对不同架构的静态C标准库。Cosmopolitan Libc本体。静态库中被归档的二进制对象文件均为ELF
格式。 - C++编译器
bin/cosmoc++
工具链的C++编译器。为提供跨架构跨平台支持,该文件实际上是一个Shell脚本。在构建目标时,其会调用bin/
目录下具体针对不同架构、不同平台的C++编译工具。编译工具均为APE
格式。 - C++标准库
x86_64-linux-cosmo/lib/libcxx.a
与aarch64-linux-cosmo/lib/libcxx.a
工具链中,针对不同架构的,基于Cosmopolitan Libc与LLVM项目的静态C++标准库。静态库中被归档的二进制对象文件均为ELF
格式。 - C/C++头文件目录
include/
工具链的C/C++头文件存放与搜寻目录。 - 其它构建工具
工具链所需要的构建工具,如bin/make
、bin/ctags
等。构建工具均为APE
格式。
测试运行APE文件
执行以下命令,测试您的系统能否运行APE
格式的可执行文件。以工具链中的bin/make
工具为例
bin/make --version
参考输出
GNU Make 4.4.1
Built for x86_64-linux-cosmo
Copyright (C) 1988-2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
编译运行 Hello World
确认APE
文件可以正常运行后,创建一个简单的测试用的C文件hello.c
。对于Windows平台,可以用自己喜欢的方式新建此文件。
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("hello world\n");
}
和GCC编译工具链的使用方式一致,使用bin/cosmocc
C语言编译器来编译hello.c
,得到APE
格式的./hello
可执行文件
bin/cosmocc -o hello hello.c
跨平台运行
x64 Windows
得到APE
格式的./hello
可执行文件后,首先尝试在Shell内部运行
./hello
若要在Shell外部运行,需要更改其文件名为hello.exe
,增加Windows可执行文件的后缀.exe
。尝试在PowerShell或者CMD中运行
.\hello.exe
在Windows平台运行APE
文件时,可能会触发Windows Defender的报警。请仔细阅读如下Issue。若信任Cosmopolitan项目,请将APE
文件列入Windows Defender白名单,再尝试运行。
https://github.com/search?q=repo%3Ajart%2Fcosmopolitan+windows+defender&type=issues
x64 Linux / WSL2
对于WSL2平台,请先阅读安装 APE Loader 并配置 binfmt_misc一节。
将./hello
文件复制到Linux系统或WSL2平台,直接运行即可
./hello
参考文档
Shell中可执行文件的执行机制
- https://stackoverflow.com/questions/3009192/how-does-the-shebang-work
- https://stackoverflow.com/questions/12296308/shell-script-working-fine-without-shebang-line-why
- http://www.faqs.org/faqs/unix-faq/faq/part3/section-16.html
对于Linux平台而言,当用户在Shell中执行一个带有可执行权限的文件时,Shell与内核会进行如下操作:
- 用户在Shell中运行带有可执行权限的文件
./executable
。 - Shell向操作系统内核发出
exec
系统调用请求,内核进行exec
系统调用处理。 - 处理时,内核首先检查文件
./executable
是否以#!
的Shebang开头。如果文件开头具有Shebang,按Shebang所写命令执行文件。 - 如果没有Shebang,检查文件特征(比如文件开头字节)是否匹配已知格式。已知格式包括
ELF
aout
等Linux系统二进制可执行文件格式,以及在binfmt_misc
里面注册的文件格式。 - 如果文件属于已知格式,按照对应格式指定的方式去执行该文件。
- 如果文件不属于已知格式,将该文件默认当作Shell脚本解释执行。