libc如何工作?

时间:2011-05-04 10:57:48

标签: c++ c emulation libc mips32

我正在编写一个MIPS32模拟器,并希望在使用gcc编译C程序时可以使用整个标准C库(可能使用GNU扩展)。

据我所知,I / O由MIPS32架构上的系统调用处理。要使用libc / glibc成功运行程序,如何判断我需要模拟哪些系统调用?(没有反复试验)

编辑:有关系统调用的含义示例,请参阅this

(如果您有兴趣,可以查看项目here,欢迎任何反馈。请记住,它是在非常早期阶段)

4 个答案:

答案 0 :(得分:6)

非常简短的回答

阅读更长的答案。

简答

如果您打算提供使用模拟器某些功能的自定义libc让主机操作系统执行系统调用,则必须实现所有这些功能。

更长的答案

退一步,看看事情通常在真实(非模拟)系统中分层的方式:

  1. 外围设备有一些I / O接口(例如编号端口或内存映射),CPU可以发痒,让他们做任何事情。
  2. CPU运行了解如何操作硬件的软件。这可以是单用途程序或运行其他程序的操作系统。由于libc在图片中,让我们假设有一个操作系统,它是Unix-y。
  3. 操作系统运行的用户空间程序使用自身与操作系统之间定义的接口来要求执行某些“系统”功能。
  4. 您要完成的任务发生在第3层和第2层之间,其中libc或用户代码中的函数执行OS定义为触发系统调用的任何内容。这开辟了无数的蠕虫:

    • OS定义为触发系统调用的内容因操作系统而异,并且(很少)在同一操作系统的版本之间。通过提供可动态链接的libc来处理隐藏这些细节,可以在“真实”系统上减轻这个问题。除此之外,如果您要运行MIPS32二进制文件,它是否使用模拟器支持的系统调用约定?

    • 您需要提供一个自定义libc,它可以让您的模拟器识别为进行特定系统调用并执行它。您希望运行的任何程序都必须交叉编译到MIPS32并与其静态链接,程序需要的任何其他库(libm都会想到)也是如此。或者,您的模拟器包需要提供动态链接器的模拟以及所有必需库的动态链接副本,因为在主机上打开它们将不起作用。如果你有足够的资源从头开始重新编译程序,移植可能比模拟更好。

    • 任何对特定系统上的文件路径做出假设的代码,或者对某些设备(它们本身是文件)中的内容的其他假设都不会正确运行。

    • 如果您要提供第2层,那么您需要自行注册,以便对整个操作系统的某个特定版本的行为进行完整,正确的模拟。像read()write()这样的调用很容易处理;像fork()uselib()ioctl()这样的其他人会更困难。您的程序使用的调用和行为与主机操作系统提供的调用和行为之间也不一定是一对一的映射。所有这些假设主机是Unix,目标程序也是。如果目标是为某些其他环境编译的,则所有投注均已关闭。

    最后一点是大多数仿真器只提供CPU和某些目标系统的硬件行为(即第1层中的所有内容)的原因。有了这些,您可以运行原始系统的引导ROM,操作系统和用户程序,所有这些都不会改变。有许多existing MIPS32 emulators可以做到这一点,可以运行在他们模拟的硬件上运行的操作系统的未更改版本。

    HTH,祝你的项目好运。

答案 1 :(得分:0)

大多数ISO标准C库都可以直接编写C.只有少数部分需要访问较低级别的OS功能。

至少,您需要在fopenfreadfwrite的块或字符级别模拟基本I / O.不过,您可以采用Unix方法,并在较低级openreadwrite调用之上实现这些方法。

您必须管理mallocfree的动态内存分配。

setjmplongjmp,需要访问执行堆栈。

timesignal.h函数。

答案 2 :(得分:0)

我不确切知道MIPS是如何工作的,但是在Win32上,必须通过DLL / EXE导入表将OS调用显式地导入到进程中。 MIPS系统使用的可执行格式可能类似。

答案 3 :(得分:0)

通常的做法是不仅模拟CPU,还模拟一组代表性的标准外设。然后在模拟器中启动一个操作系统,其中包含一个libc和硬件驱动程序。 Libc将调用OSes驱动程序,该驱动程序调用模拟器中的虚拟硬件。有关一个流行的示例,请参阅DosBox。

您的问题的另一种解释是您不想编写完整的模拟器,而是一个二进制兼容层,允许您在非mips32系统上执行mips32二进制文件。一个流行的例子是MacOsX(英特尔),它也可以执行PowerPC应用程序。

在后一种情况下,您需要模拟操作系统ABI(应用程序二进制接口),或者您可以使用libc的ABI。在这两种情况下,您都需要实现在模拟器上运行的存根代码和在主机上运行的代理代码:

  • 存根序列化函数调用参数
  • ...并使用一些特殊的虚拟指令将它们从仿真器内存传输到主机内存
  • 代理需要修补参数(字节顺序,整数长度,地址空间......)
  • ...并在主机系统上执行函数调用
  • 代理然后对传出函数参数进行缓存和序列化
  • ...并将它们传回存根
  • ...将数据返回给调用者

大多数调用无法使用通用存根/代理,但需要特定的解决方案。

祝你好运!