linux仿真
Ⅰ 使用 Ptrace 去拦截和仿真 Linux 系统调用
ptrace(2) 系统调用在类 Unix 系统上是通过原生调试器监测被调试进程的主要机制,也是实现 strace(系统调用跟踪)的常见方法。使用 Ptrace,跟踪器可以暂停被跟踪进程,检查和设置寄存器和内存,监视系统调用,甚至可以拦截系统调用。
通过拦截功能,跟踪器可以篡改系统调用参数,篡改系统调用的返回值,甚至阻塞某些系统调用。这意味着一个跟踪器本身完全可以提供系统调用服务,这非常有趣,因为它意味着一个跟踪器可以仿真一个完整的外部操作系统,而这些都是在没有得到内核任何帮助的情况下由 Ptrace 实现的。
然而,同一时间只能有一个进程被一个跟踪器附着,因此在那个进程的调试期间,不可能再使用诸如 GDB 这样的工具去仿真一个外部操作系统。此外,仿真系统调用的开销非常高。
本文将专注于 x86-64 Linux 的 Ptrace,并将使用一些 Linux 专用的扩展。同时,本文将忽略掉一些错误检查,但完整的源代码仍然会包含这些错误检查。
以下是一个可直接运行的示例代码示例:github.com/skeeto/ptrac...
strace 的基本实现回顾:它不是 DTrace,但 strace 仍然非常有用。Ptrace 一直没有被标准化。它的接口在不同的操作系统上非常类似,尤其是在核心功能方面,但是在不同的系统之间仍然存在细微的差别。
最小化的 strace 不需要任何选项,因此需要做的第一件事情是假设它至少有一个参数,在 argv 尾部的 fork(2) 和 exec(2) 被跟踪进程。但是在加载目标程序之前,新的进程将告知内核,目标程序将被它的父进程继续跟踪。被跟踪进程将被这个 Ptrace 系统调用暂停。
父进程使用wait(2) 等待子进程的 PTRACE_TRACEME,当 wait(2) 返回后,子进程将被暂停。
在允许子进程继续运行之前,我们告诉操作系统,被跟踪进程和它的父进程应该一同被终止。一个真实的 strace 实现可能会设置其它的选择,比如:PTRACE_O_TRACEFORK。
剩余部分就是一个简单的、无休止的循环了,每循环一次捕获一个系统调用。循环体总共有四步:这个 PTRACE_SYSCALL 请求被用于等待下一个系统调用时开始,和等待那个系统调用退出。当 wait(2) 返回时,进行了系统调用的线程的寄存器中写入了该系统调用的系统调用号及其参数。接下来是采集系统调用信息,这是各个系统架构不同的地方。接下来是它的另一个 PTRACE_SYSCALL 和 wait(2),然后是另一个 PTRACE_GETREGS 去获取结果。结果保存在 rax 中。
系统调用拦截:假设我们想使用 Ptrace 去实现如 OpenBSD 的 pledge(2) 这样的功能,它是一个进程承诺只使用一套受限的系统调用。初步想法是,许多程序一般都有一个初始化阶段,这个阶段它们都需要进行许多的系统访问(比如,打开文件、绑定套接字、等等)。初始化完成以后,它们进行一个主循环,在主循环中它们处理输入,并且仅使用所需的、很少的一套系统调用。
使用与 strace 相同的模型,但不是输出所有的系统调用,我们既能够阻塞某些系统调用,也可以在它的行为异常时简单地终止被跟踪进程。终止它很容易:只需要在跟踪器中调用 exit(2)。因此,它也可以被设置为去终止被跟踪进程。阻塞系统调用和允许子进程继续运行都只是些雕虫小技而已。
最棘手的部分是当系统调用启动后没有办法去中断它。当跟踪器在入口从 wait(2) 中返回到系统调用时,从一开始停止一个系统调用的仅有方式是,终止被跟踪进程。然而,我们不仅可以“搞乱”系统调用的参数,也可以改变系统调用号本身,将它修改为一个不存在的系统调用。返回时,在 errno 中通过正常的内部信号,我们就可以报告一个“友好的”错误信息。
跟踪器与被跟踪进程如何沟通?使用人为的系统调用!
创建一个人为的系统调用:对于我的这个类似于 pledge 的系统调用 —— 我可以通过调用 xpledge() 将它与真实的系统调用区分开 —— 我设置 10000 作为它的系统调用号,这是一个非常大的数字,真实的系统调用中从来不会用到它。
为演示需要,我同时构建了一个非常小的接口,这在实践中并不是个好主意。它与 OpenBSD 的 pledge(2) 稍有一些相似之处,它使用了一个字符串接口。事实上,设计一个健壮且安全的权限集是非常复杂的,正如在 pledge(2) 的手册页面上所显示的那样。
跟踪器可以使用一个假冒的东西去代替系统调用号,允许它失败,以及为系统调用本身提供服务。但那样做的效率很低。其实质上是对每个系统调用做了三个上下文切换:一个是在入口上停止,一个是让系统调用总是以失败告终,还有一个是在系统调用退出时停止。
从 2005 年以后,对于这个技术,PTrace 的 Linux 版本有更高效的操作:PTRACE_SYSEMU。PTrace 仅在每个系统调用发出时停止一次,在允许被跟踪进程继续运行之前,由跟踪器为系统调用提供服务。
使用 PTRACE_SYSEMU 跟踪器、一个加载器(用于代替 exec(2)),和这个二进制程序所需要(或仅运行静态的二进制程序)的任何系统库,你就可以在任何具有(足够)稳定的系统调用 ABI 的相同架构的机器上运行一个二进制程序,无需系统调用,这个二进制程序可以一直运行。
实际上,这听起来有点像一个有趣的周末项目。参见 nullprogram.com/blog/20... 了解更多内容。
Ⅱ Linux下VCS2014和Verdi2015的联合仿真
VCS和Verdi是IC设计中的常用工具,分别是Synopsys公司的VCS和Nocas公司(已被Synopsys收购)的Verdi。虽然两者都需要在Linux环境中运行,对于习惯于Windows环境的用户来说,这可能不太友好,但在大型项目开发时,Linux环境的便利性对于管理和团队协作至关重要,同样适用于FPGA开发。
有许多开源镜像可用于在虚拟机上快速安装IC开发所需的全套工具,简化了安装过程。然而,使用过程中可能会遇到各种问题,例如VCS和Verdi的联合仿真。要实现VCS2014和Verdi2015的联合仿真,需要生成可以从Verdi识别的.fsdb文件,这需要在Testbench中添加特定代码,并执行Makefile中的“com”编译命令。
在尝试生成.fsdb文件的过程中,可能会遇到系统找不到Testbench中新增的系统函数的问题。解决此问题需要在Makefile中添加“-fsdb”选项,并确保在环境变量中正确设置了NOVAS_HOME路径。有时,错误信息可能指示环境变量设置未生效,这是因为需要在设置环境变量后重新启动虚拟机,否则设置将无效。
另一个常见的问题是版本兼容性问题,特别是在32位和64位版本之间。然而,经过多次尝试后发现,使用“-full64”选项在64位模式下进行编译,可以解决此类问题,生成可用于64位Verdi的.fsdb文件。这需要在Makefile中的“-fsdb”命令基础上添加此选项。
成功生成.fsdb文件后,下一步是在Verdi中打开并进行联合仿真。在联合仿真过程中,可能出现与库路径相关的问题,但通过查阅资料并进行适当的调整,可以解决这类问题。在完成所有步骤后,若一切顺利,即可在Verdi中打开.fsdb文件,实现VCS和Verdi的联合仿真。
虽然这是一个相对较小的问题,但对新手来说可能会是一个挑战。解决这类问题需要耐心、细心和向他人求教的态度。感谢那些帮助我解决此问题的热心朋友,我将秉持同样的热情,帮助更多的人。