繁体   English   中英

libc 是如何工作的?

[英]How does libc work?

我正在编写一个 MIPS32 仿真器,并希望在使用 ZE0D511356BD94120AF49CCB96 编译 C 程序时可以使用整个标准 C 库(可能带有 GNU 扩展)

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

编辑:有关我所说的系统调用的示例,请参阅this

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

非常简短的回答

阅读更长的答案。

简答

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

更长的答案

退后一步,看看事物在真实(非仿真)系统中的典型分层方式:

  1. 外围设备有一些 I/O 接口(例如,编号端口或 memory 映射),CPU 可以通过这些接口让它们做任何事情。
  2. CPU 运行了解如何操作硬件的软件。 这可以是单一用途的程序或运行其他程序的操作系统。 由于 libc 在图片中,我们假设有一个操作系统并且它是 Unix-y 的东西。
  3. 操作系统运行的用户空间程序使用它们与操作系统之间定义的接口来请求执行某些“系统”功能。

您要完成的工作发生在第 3 层和第 2 层之间,其中 libc 或用户代码中的 function 执行操作系统定义为触发系统调用的任何操作。 这会打开许多蠕虫罐:

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

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

  • 任何对特定系统上文件的路径做出假设或对它们在某些设备(它们本身就是文件)中会找到什么做出假设的任何代码都不会正确运行。

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

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

HTH 并祝您项目好运。

大部分ISO标准C库都可以直接写成C。 只有少数部分需要访问较低级别的操作系统功能。

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

而且您必须管理mallocfree的动态 memory 分配。

还有setjmplongjmp ,它们需要访问执行堆栈。

还有timesignal.h函数。

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

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

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

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

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

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

祝你好运!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM