在哪些Common Lisp实现中编写一个针对Intel x86-64的Linux内核模式调试器是否可行?

8
我希望开发一种针对x86-64的ring0内核模式调试器,使用Common Lisp编写并作为Linux内核模块加载。由于我更喜欢使用Common Lisp进行一般编程,因此我想知道不同的Common Lisp实现如何适用于这种编程任务。
调试器将使用一些外部的反汇编库,例如 udis86 通过一些 FFI。在我的看法中,用 C 写内核模块最容易,因为它们需要包含 C 函数 int init_module(void)void cleanup_module(void) (The Linux Kernel Module Programming Guide),所以内核空间模块代码将使用 CFFI 从 C 中调用 Common Lisp 代码。该想法是创建一个 64 位 Linux 下的 Ring0 调试器,灵感来自于 Rasta Ring 0 Debugger 的想法,它仅适用于 32 位 Linux 并需要 PS/2 键盘。我认为最具挑战性的部分将是实际的调试器代码,包括硬件和软件断点以及低级视频、键盘或 USB 输入设备处理。内联汇编会在这方面有很大帮助,似乎在 SBCL 中可以通过使用 VOPs (SBCL Internals: VOP) (SBCL Internals: Adding VOPs) 实现内联汇编,并且这个 IRC log 提到 ACL (Allegro Common Lisp)、CCL (Clozure Common Lisp) 和 CormanCL 都有 LAPs (Lisp Assembly Programs)。ACL 和 CormanCL 都是专有软件,因此被丢弃了,但 CCL (Clozure Common Lisp) 可能是一个选择。构建独立可执行文件的能力也是一个要求;我目前使用的 SBCL 有这个功能,但它们是整个 Lisp 映像,所以大小相当大。
我的问题是:是否可以使用Common Lisp创建一个针对Intel x86-64的ring0内核模式调试器,其中低级别代码采用C和/或汇编实现?如果可以,哪些64位Linux的Common Lisp实现最适合这种尝试,并且如果有多个适合的Common Lisp实现,有什么利弊?如果Scheme相比Common Lisp提供了一些优势,那么它也可以是一种可能的选择。我很清楚大多数内核模块都是用C编写的,我也很熟悉C和x86汇编,足以能够用C和/或汇编编写所需的低级别代码。这不是将Linux内核移植到Lisp的尝试(参见:https://stackoverflow.com/questions/1848029/why-not-port-linux-kernel-to-common-lisp),而是计划在Common Lisp中编写一个Linux内核模块,该模块将用作ring0调试器。

3
你可以在用户进程中运行Lisp部分,并从调试器与其通信。这应该更容易实现。 - Alexey Frunze
@AlexeyFrunze 这可能是个好主意,这样做可能会让事情变得更容易。 - nrz
一个踩,一个赞。我想知道为什么会有踩? - nrz
1
这基本上是不相关的问题,无法回答。请阅读常见问题解答。如果您想讨论项目想法和技术可用性问题,您可能需要去讨论论坛。Stackoverflow是一个针对实际问题的问答网站,而不是讨论“我能否用Lisp、Scheme或其他语言编写图形驱动程序”之类的一般性问题。如果您有像“我正在用Lisp编写图形驱动程序,在这个例程中我遇到了随机错误,该如何修复?”这样的问题-那么这里可能会有用。但是,如果领域太窄,最好在专业列表上提问。 - Rainer Joswig
1
这非常有趣。为什么不在Reddit的r/lisp上发布呢?那里更适合这些问题。 - Paul Nathan
@PaulNathan 发了一篇帖子到 Reddit 上。让我们看看反馈怎么样。 - nrz
2个回答

4
您可能想看一下Brad Beveridge在2008年2月2日的lispvan演讲“使用Common Lisp做坏事”,其中介绍了如何使用SBCL处理文件系统驱动程序。 演讲描述和文件 他在演讲中提到:
“用CL编写的C/C ++调试器?现在完全是天方夜谭
但是,那将是多么酷啊?
并不需要太多努力,只需要能够写入库所在位置的内存以插入断点,然后在Lisp侧捕获信号
可以使用不正当手段将C函数替换为对Lisp函数的调用
除了一些细节之外,这可能并不那么困难-肯定没有什么“新东西”
不正当的技巧涉及用另一个跳转(无链接分支)覆盖C代码,进入Lisp回调。当Lisp代码返回时,它可以通过链接寄存器直接跳回原始调用函数。
此外,我完全忽略了编写调试器的真正困难-这将是耗时的。”

1
这项任务似乎是可能的,但相当耗时,即使反编译比反汇编困难得多。我的计划是创建一个类似于SoftIce的常规汇编调试器,带有Vim/Perl正则表达式内存搜索功能。看起来可以编写一个主要使用Common Lisp编写的内核模式调试器,特别是考虑到ECL(Embeddable Common-Lisp)支持使用(ffi:cline c-code*)(ffi:c-inline (lisp-value*))特殊形式的内联C,因此我认为ECL将是最适合这样的任务的Common Lisp实现之一。 - nrz

2
抱歉,由于显而易见的原因,实现一个CL内核模块是不可行的。要使其工作,您需要进行大量的黑客攻击,可能会失去Lisp语言提供的所有优势,而且您的Lisp代码将看起来像S表达式中的C代码。
请编写您的内核模块以导出所需的任何功能/数据,然后使用ioctl或读/写操作,任何用户模式程序都可以与该模块通信。
我不确定是否有任何内核模块足够通用,以实现Lisp(或其子集),使您可以在Lisp中编写代码,而此通用模块将读取Lisp代码并将其作为子组件运行,基本上是:内核-> 通用Lisp模块->您的Lisp代码(在内核中运行)。

2
在C中实现内核模块代码,使用Common Lisp编写用户空间代码是一个不错的选择。然而,我发现ECL(嵌入式Common Lisp)支持内联C并编译为独立可执行文件,这是任何CL实现的两个最重要的要求,因此我首先尝试使用ECL和内联C。 - nrz
经过几天的内核模块代码破解,似乎嵌入通用Lisp(ECL)将是一项艰巨的任务。在内核代码中,只有C的子集是可能的,因此即使ECL生成C代码(这是非常好的事情),那个C代码可能也不是有效的内核C代码。使用正则表达式编辑ECL生成的C代码会创建另一个编程层,在该代码可以使用“gcc”编译之前。因此,与其使用“.lisp”->“ecl”->“.c”->“gcc”->“.o”->“ld”->“.ko”管道,不如使用“.lisp”->“ecl”->“.c”->“vim”或“perl”->“.c”->“gcc”->“.o”->“ld”->“.ko”管道。 - nrz
在我看来,使用ECL编写内核模块代码可能需要进行一些(重大?)更改,以便它可以选择性地输出适用于内核模块的C代码。在C中编写内核模块并在ECL中编写用户空间代码,并通过/dev和/或端口之间进行通信要容易得多。这已经是现在可以实现的,也是我要走的路。 - nrz

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