自编译系统工程教育平台
Selfie是一个系统工程教育平台,通过12000行C代码实现了自编译编译器、自执行模拟器和自托管虚拟机监视器。该项目聚焦系统代码中的自引用挑战,为本科生和研究生提供编译器、库、操作系统和虚拟机监视器的实践构建经验,全面覆盖从编程语言到运行时系统的设计与实现。
自拍是奥地利萨尔茨堡大学计算机科学系计算系统组的一个项目。
自拍项目为本科生和研究生提供了一个教育平台,用于教授编程语言和运行时系统的设计和实现。重点是构建编译器、库、操作系统和虚拟机监控器。共同的主题是识别和解决系统代码中的自引用,这被视为教授系统工程时的关键挑战,因此得名。
自拍是一个自包含的64位、12KLOC C语言实现,包括:
自拍在一个(!)文件中实现,并保持最小化以简化。还有一个简单的内存链接器、RISC-U反汇编器、垃圾收集器、L1指令和数据缓存、分析器,以及带有重放功能的调试器,以及以RISC-V系统调用形式内置于仿真器和虚拟机监控器中的最小操作系统支持。垃圾收集器是保守的,甚至是自收集的。它可以作为库在与变异器相同的地址空间中运行,和/或作为内核地址空间中仿真器的一部分运行。
自拍生成的ELF二进制文件可以在真实的RISC-V硬件和QEMU上运行,并与官方RISC-V工具链兼容,特别是spike仿真器和pk内核。
自拍被设计为64位系统,因此需要64位系统才能运行(LP64数据模型)。然而,自拍也可以在支持编译和执行32位二进制文件的系统上编译(ILP32数据模型)。在这种情况下,自拍成为一个32位系统,可以直接生成和执行32位二进制文件。这是可能的,因为自拍的实现在整个系统中都小心避免了32位溢出。
垃圾回收:除了selfie中保守但时间复杂度为O(n^2)的垃圾回收器外,还有一个针对小内存块的O(n)时间复杂度的Boehm垃圾回收器实现,对于大内存块则回退使用selfie中的垃圾回收器。
模糊测试:有一个基于selfie的简单但自我模糊测试的模糊器,名为buzzr,用于对RISC-U代码(包括selfie的所有代码和自身)进行模糊测试。
符号执行:有一个基于selfie的自执行符号执行引擎,名为monster,它将RISC-U代码(包括selfie的所有代码和自身)转换为SMT-LIB公式。当且仅当存在使代码以非零退出代码退出或在给定的机器指令数内执行除零操作的输入时,这些公式是可满足的。
有界模型检查:有一个基于selfie的自翻译建模引擎,名为beator,它将RISC-U代码(包括selfie的所有代码和自身)转换为BTOR2公式。当且仅当存在使代码以非零退出代码退出、执行除零操作或访问已分配内存块之外的内存的输入时,这些公式是可满足的。
位精确代码分析和合成:有一个基于selfie的自翻译建模引擎,名为rotor,它将完整的RISC-V代码(包括selfie的所有代码和自身)转换为BTOR2和SMT-LIB公式。当且仅当存在使代码以非零退出代码退出、执行除零操作或访问内存段之外的内存 的输入时,这些公式是可满足的。Rotor还生成支持RISC-V代码合成的模型。
BTOR2可视化:有一个名为beatle的可视化工具,它将从RISC-U二进制文件生成的BTOR2公式显示为有向无环图。
SAT求解:有一个基于selfie的暴力SAT求解器,名为babysat,用于计算DIMACS CNF格式的SAT公式的可满足性。
二进制翻译:有一个基于selfie的自翻译二进制翻译器,它将RISC-U代码(包括selfie的所有代码和自身)翻译成x86二进制代码。
Selfie可以在Linux、macOS和Windows机器上原生运行,可能还支持其他安装了终端和C编译器的系统。即使您的机器上没有安装C编译器,或者您只能访问网络浏览器,您也可以运行selfie。安装和运行selfie至少有三种方式:
在您的机器上原生安装(推荐):下载并解压selfie。然后,打开终端运行selfie,具体见下文。为此,您需要在机器上安装C编译器。我们推荐使用clang或gcc(在Windows上使用cygwin)。
在您的机器上使用docker(高级):下载并安装docker。然后,打开终端并输入docker run -it cksystemsteaching/selfie
。使用docker的优点是您可以在机器上直接运行selfie,而无需安装任何工具,如C编译器。所有必要的甚至可选的工具都预先安装在selfie docker镜像中。但是,您需要知道如何使用docker。
在云端(简单但需要互联网连接):如果您只能访问网络浏览器,只需点击这里。或者,创建一个github账户(如果您还没有),然后将selfiefork到您的github账户中。接着,创建一个cloud9学生账户,将其连接到您的github账户,验证您的电子邮件地址并设置密码(重要!),最后将您fork的selfie克隆到一个新的cloud9工作空间中。
此时,我们假设您已经有一个支持运行selfie的系统。以下我们使用make
命令,假设它已安装在您的系统上,这通常是默认的。但是,我们也会展示make
调用的命令,这样如果您的系统没有安装make
,您也可以手动执行该命令。
下一步是生成selfie二进制文件。为此,在终端中cd
到selfie文件夹,然后输入make
。使用docker时,系统会回应make: 'selfie' is up to date
,因为已经预先安装了selfie二进制文件。不使用docker时,make
将在您的机器上或cloud9工作空间中调用C编译器:
cc -Wall -Wextra -O3 -D'uint64_t=unsigned long' selfie.c -o selfie
然后将selfie.c
编译成一个名为selfie
的可执行文件,如-o
选项所指定。这个可执行文件包含C*编译器、mipster模拟器和hypster虚拟机监控器。-Wall
和-Wextra
选项启用所有编译器警告,这在selfie的进一步开发中很有用。-O3
选项指示编译器生成优化代码。-D'uint64_t=unsigned long'
选项用于引导代码。它定义了数据类型uint64_t
,否则由于C*不包含必要的定义,该类型将是未定义的。如果您的系统支持编译和执行32位二进制文件,您也可以尝试make selfie-32
,这将生成一个32位系统,然后生成和执行32位二进制文件。
一旦您成功编译了selfie.c
,您可以不带任何参数调用selfie
,如下所示:
$ ./selfie ./selfie { -c { source } | -o binary | [ -s | -S ] assembly | -l binary } [ ( -m | -d | -r | -y ) 0-4096 ... ]
在这种情况下,selfie
会回应其使用模式。
为了充分利用自 指性,提供选项的顺序很重要。
-c
选项调用C*编译器处理给定的source
文件列表,将它们编译并链接成内部存储的RISC-U代码。例如,可以使用selfie
来编译它自己的源代码selfie.c
,如下所示:
$ ./selfie -c selfie.c
-o
选项将最近一次编译器调用生成的RISC-U代码写入指定的binary
文件。例如,可以指示selfie
编译自身,然后将生成的RISC-U代码输出到名为selfie.m
的RISC-U二进制文件:
$ ./selfie -c selfie.c -o selfie.m
-s
选项将最近一次编译器调用生成的RISC-U代码的RISC-U汇编写入指定的assembly
文件,而-S
选项还会包括近似的行号和指令的二进制表示。类似地,可以指示selfie
编译自身,然后将生成的RISC-U代码输出到名为selfie.s
的RISC-U汇编文件:
$ ./selfie -c selfie.c -s selfie.s
-l
选项从给定的binary
文件加载RISC-U代码。-o
和-s
选 项也可以在-l
选项之后使用。但在这种情况下,-s
选项不会生成近似的源代码行号。例如,可以按如下方式加载之前生成的RISC-U二进制文件selfie.m
:
$ ./selfie -l selfie.m
-m
选项调用mipster模拟器执行最近加载或由编译器调用生成的RISC-U代码。模拟器创建一个具有0-4096
MB内存的机器实例。RISC-U代码的source
或binary
名称以及任何剩余的...
参数都会传递给代码的main函数。例如,以下调用使用mipster执行selfie.m
:
$ ./selfie -l selfie.m -m 1
这在语义上等同于不带任何参数执行selfie
:
$ ./selfie
-d
选项类似于-m
选项,但mipster会输出每条执行的指令、其近似源代码行号(如果可用)以及相关的机器状态。另外,-r
选项通过让mipster仅在发生运行时错误(如除以零)时重放代码执行,来限制-d
选项创建 的输出量。在这种情况下,mipster只输出错误发生前刚刚执行的指令。
如果你使用docker,也可以直接在spike和pk上执行selfie.m
,如下所示:
$ spike pk selfie.m
这在语义上再次等同于不带任何参数执行selfie
。
-y
选项调用hypster虚拟机监控器执行RISC-U代码,类似于mipster模拟器。与mipster的区别在于,hypster创建RISC-U虚拟机而不是RISC-U模拟器来执行代码。请参见下面的示例。
以下是如何执行selfie.c
的自编译,然后检查通过执行./selfie
二进制文件为selfie.c
生成的RISC-U代码selfie1.m
是否等同于通过执行刚刚生成的selfie1.m
二进制文件生成的代码selfie2.m
的示例:
$ ./selfie -c selfie.c -o selfie1.m -m 2 -c selfie.c -o selfie2.m $ diff -s selfie1.m selfie2.m Files selfie1.m and selfie2.m are identical
注意,这需要至少2MB的内存才能工作。
以下示例展示了如何执行mipster模拟器的自执行。在这种情况下,我们调用mipster来调用自身以执行selfie
:
$ ./selfie -c selfie.c -o selfie.m -m 2 -l selfie.m -m 1
这在语义上再次等同于不带任何参数执行selfie
,但这次selfie
打印其使用模式的速度要慢得多,因为有一个mipster运行在另一个mipster之上。
前面的示例也可以通过在mipster上运行hypster来完成。这显著更快,并且需要更少的内存,因为hypster不会在第一个模拟器实例之上创建第二个模拟器实例。相反,hypster创建一个虚拟机来执行selfie,该虚拟机与第一个模拟器实例上的hypster并行运行:
$ ./selfie -c selfie.c -o selfie.m -m 1 -l selfie.m -y 1
我们甚至可以在mipster上运行hypster上的hypster,这仍然相当快,因为仍然只涉及一个模拟器实例,而hypster本身并不增加太多开销:
$ ./selfie -c selfie.c -o selfie.m -m 2 -l selfie.m -y 1 -l selfie.m -y 1
要编译任何C*源代码并立即执行它,而不生成RISC-U二进制文件,可以在单次调用selfie
时使用:
$ ./selfie -c any-cstar-file.c -m 1 "arguments for any-cstar-file.c"
同样,你也可以使用selfie编译的selfie
版本,让mipster模拟器执行selfie来编译任何C*源代码,然后立即用hypster在同一模拟器实例上执行它:
$ ./selfie -c selfie.c -m 1 -c any-cstar-file.c -y 1 "arguments for any-cstar-file.c"
你也可以用这两种方式生成RISC-U二进制文件,它们将是相同的:
$ ./selfie -c any-cstar-file.c -o any-cstar-file1.m $ ./selfie -c selfie.c -m 1 -c any-cstar-file.c -o any-cstar-file2.m $ diff -s any-cstar-file1.m any-cstar-file2.m Files any-cstar-file1.m and any-cstar-file2.m are identical
这也可以在单次调用selfie
时完成:
$ ./selfie -c any-cstar-file.c -o any-cstar-file1.m -c selfie.c -m 1 -c any-cstar-file.c -o any-cstar-file2.m $ diff -s any-cstar-file1.m any-cstar-file2.m Files any-cstar-file1.m and any-cstar-file2.m are identical
然后可以按如下方式加载和执行生成的RISC-U二进制文件:
$ ./selfie -l any-cstar-file1.m -m 1 "arguments for any-cstar-file1.m"
要从多个源文件编译和链接任何C*源代码,请使用:
$ ./selfie -c any-cstar-file1.c any-cstar-file2.c ... -m 1
例如,要将 selfie.c
的源代码作为库代码在任何 C* 源代码中使用,请执行:
$ ./selfie -c any-cstar-file.c selfie.c -m 1
请注意,编译器会忽略多重符号定义,并发出警告。
Selfie 的控制台消息始终以当前运行的源文件或二进制文件的名称开头。Mipster 模拟器还会显示为其机器实例分配的内存量以及执行如何终止(退出代码)。
如前所述,selfie
和任何其他 C* 文件的 RISC-U 汇编代码可以通过以下方式生成:
$ ./selfie -c selfie.c -s selfie.s
如果汇编代码是由编译器生成的二进制文件生成的(而不是从文件加载的),汇编文件中会包含大致的源代码行号。
使用 -d
选项可以打印详细的调试信息,例如:
$ ./selfie -c selfie.c -d 1
同样,如果执行的二进制文件是由编译器生成的(而不是从文件加载的),调试信息中会包含大致的源代码行号。
AI辅助编程,代码自动修复
Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。
AI小说写作助手,一站式润色、改写、扩写
蛙蛙写作—国内先进的AI写作平台,涵盖小说、学术、社交媒体等多场景。提供续写、改写、润色等功能,助力创作者高效优化写作流程。界面简洁,功能全面,适合各类写作者提升内容品质和工作效率。
全能AI智能助手,随时解答生活与工作的多样问题
问小白,由元石科技研发的AI智能助手,快速准确地解答各种生活和工作问题,包括但不限于搜索、规划和社交互动,帮助用户在日常生活中提高效率,轻松管理个人事务。
实时语音翻译/同声传译工具
Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。
一键生成PPT和Word,让学习生活更轻松
讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。
深度推理能力全新升级,全面对标OpenAI o1
科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写作辅助、编程解答、科研文献解读等功能,能为不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。
一种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型
Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。
AI助力,做PPT更简单!
咔片是一款轻量化在线演示设计工具,借助 AI 技术,实现从内容生成到智能设计的一站式 PPT 制作服务。支持多种文档格式导入生成 PPT,提供海量模板、智能美化、素材替换等功能,适用于销售、教师、学生等各类人群,能高效制作出高品质 PPT,满足不同场景演示需求。
选题、配图、成文,一站式创作,让内容运营更高效
讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。
专业的AI公文写作平台,公文写作神器
AI 材料星,专业的 AI 公文写作辅助平台,为体制内工作人员提供高效的公文写作解决方案。拥有海量公文文库、9 大核心 AI 功能,支持 30 + 文稿类型生成,助力快速完成领导讲话、工作总结、述职报告等材料,提升办公效率,是体制打工人的得力写作神器。
最新AI工具、AI资讯
独家AI资源、AI项目落地
微信扫一扫关注公众号