简体   繁体   English

libc 是如何工作的?

[英]How does libc work?

I'm writing a MIPS32 emulator and would like to make it possible to use the whole Standard C Library (maybe with the GNU extensions) when compiling C programs with gcc.我正在编写一个 MIPS32 仿真器,并希望在使用 ZE0D511356BD94120AF49CCB96 编译 C 程序时可以使用整个标准 C 库(可能带有 GNU 扩展)

As I understand at this point, I/O is handled by syscalls on the MIPS32 architecture.据我所知,I/O 由 MIPS32 架构上的系统调用处理。 To successfully run a program using libc/glibc, how can I tell what syscalls do I need to emulate?要使用 libc/glibc 成功运行程序,我如何知道我需要模拟哪些系统调用? (without trial and error) (无需反复试验)

Edit: See this for an example of what I mean by syscalls.编辑:有关我所说的系统调用的示例,请参阅this

(You can check out the project here if you are interested, any feedback is welcome. Keep in mind that it's in a very early stage) (如果您有兴趣,可以在这里查看该项目,欢迎任何反馈。请记住,它处于非常早期的阶段)

Very Short Answer非常简短的回答

Read the much longer answer.阅读更长的答案。

Short Answer简答

If you intend to provide a custom libc that uses some feature of your emulator to have the host OS execute your system calls, you have to implement all of them.如果您打算提供一个自定义 libc,它使用您的模拟器的某些功能让主机操作系统执行您的系统调用,您必须实现所有这些。

Much Longer Answer更长的答案

Step back for a minute and look at the way things are typically layered in a real (non-emulated) system:退后一步,看看事物在真实(非仿真)系统中的典型分层方式:

  1. The peripherals have some I/O interface (eg, numbered ports or memory mapping) that the CPU can tickle to make them do whatever they do.外围设备有一些 I/O 接口(例如,编号端口或 memory 映射),CPU 可以通过这些接口让它们做任何事情。
  2. The CPU runs software that understands how to manipulate the hardware. CPU 运行了解如何操作硬件的软件。 This can be a single-purpose program or an operating system that runs other programs.这可以是单一用途的程序或运行其他程序的操作系统。 Since libc is in the picture, let's assume there's an OS and that it's something Unix-y.由于 libc 在图片中,我们假设有一个操作系统并且它是 Unix-y 的东西。
  3. Userspace programs run by the OS use a defined interface between themselves and OS to ask for certain "system" functions to be carried out.操作系统运行的用户空间程序使用它们与操作系统之间定义的接口来请求执行某些“系统”功能。

What you're trying to accomplish takes place between layers 3 and 2, where a function in libc or user code does whatever the OS defines as triggering a system call.您要完成的工作发生在第 3 层和第 2 层之间,其中 libc 或用户代码中的 function 执行操作系统定义为触发系统调用的任何操作。 This opens up numerous cans of worms:这会打开许多蠕虫罐:

  • What the OS defines as triggering a system call differs from OS to OS and (rarely) between versions of the same OS.操作系统定义为触发系统调用的内容因操作系统而异,并且(很少)在同一操作系统的版本之间有所不同。 This problem is mitigated on "real" systems by providing a dynamically-linkable libc that takes care of hiding those details.通过提供一个负责隐藏这些细节的可动态链接的 libc,在“真实”系统上缓解了这个问题。 That aside, if you have a MIPS32 binary you want to run, does it use a system call convention that your emulator supports?除此之外,如果您要运行 MIPS32 二进制文件,它是否使用您的模拟器支持的系统调用约定?

  • You would need to provide a custom libc that does something your emulator can recognize as making a particular system call and carry it out.您需要提供一个自定义 libc,它可以执行您的模拟器可以识别为进行特定系统调用并执行它的操作。 Any program you wish to run will have to be cross-compiled to MIPS32 and statically linked with it, as would any other libraries the program requires (libm comes to mind).您希望运行的任何程序都必须交叉编译为 MIPS32 并与之静态链接,程序所需的任何其他库也是如此(想到 libm)。 Alternately, your emulator package will need to provide a simulation of a dynamic linker plus dynamically-linkable copies of all required libraries, because opening those on the host won't work.或者,您的模拟器 package 将需要提供动态 linker 的模拟以及所有必需库的动态可链接副本,因为在主机上打开这些库将不起作用。 If you have enough source to recompile the program from scratch, porting might be better than emulation.如果您有足够的源代码从头开始重新编译程序,那么移植可能比仿真更好。

  • Any code that makes assumptions about paths to files on a particular system or other assumptions about what they'll find in certain devices (which are themselves files) won't run correctly.任何对特定系统上文件的路径做出假设或对它们在某些设备(它们本身就是文件)中会找到什么做出假设的任何代码都不会正确运行。

  • If you're providing layer 2, you're signing yourself up to provide a complete, correct simulation of the behavior of one particular version of an entire operating system.如果您提供第 2 层,则您自己注册以提供对整个操作系统的一个特定版本的行为的完整、正确的模拟。 Some calls like read() and write() would be easy to deal with;read()write()这样的调用很容易处理; others like fork() , uselib() and ioctl() would be much more difficult.其他像fork()uselib()ioctl()会更困难。 There also isn't necessarily a one-to-one mapping of calls and behaviors your program uses with those your host OS provides.您的程序使用的调用和行为与主机操作系统提供的调用和行为也不一定是一对一的映射。 All of this assumes the host is Unix and the target program is, too.所有这些都假设主机是 Unix 并且目标程序也是。 If the target is compiled for some other environment, all bets are off.如果目标是为其他环境编译的,那么所有的赌注都没有了。

That last point is why most emulators provide just a CPU and the hardware behaviors of some target system (ie, everything in layer 1).最后一点就是为什么大多数仿真器只提供一个 CPU 和某些目标系统的硬件行为(即,第 1 层中的所有内容)。 With those in place, you can run an original system's boot ROM, OS and user programs, all unaltered.有了这些,您就可以运行原始系统的引导 ROM、操作系统和用户程序,所有这些都不会改变。 There are a number of existing MIPS32 emulators that do just this and can run unaltered versions of the operating systems that ran on the hardware they emulate.有许多现有的 MIPS32 仿真器可以做到这一点,并且可以运行在它们仿真的硬件上运行的操作系统的未更改版本。

HTH and best of luck on your project. HTH 并祝您项目好运。

Most of the ISO standard C library can be written in straight C.大部分ISO标准C库都可以直接写成C。 Only a few portions need access to lower level OS functionality.只有少数部分需要访问较低级别的操作系统功能。

At a minimum, you'll need to emulate basic I/O at the block or character level for fopen , fread , and fwrite .至少,您需要在块或字符级别模拟fopenfreadfwrite的基本 I/O。 You could take the Unix approach, though, and implement those on top of the lower-level open , read , and write calls.不过,您可以采用 Unix 方法,并在较低级别的openreadwrite调用之上实现这些方法。

And you'll have to manage dynamic memory allocation for malloc and free .而且您必须管理mallocfree的动态 memory 分配。

And setjmp and longjmp , which needs access to the execution stack.还有setjmplongjmp ,它们需要访问执行堆栈。

Also time and the signal.h functions.还有timesignal.h函数。

I don't know exactly how MIPS works, but on Win32 then OS calls have to be explicitly imported in to a process via the DLL/EXE import table.我不确切知道 MIPS 是如何工作的,但是在 Win32 上,操作系统调用必须通过 DLL/EXE 导入表显式导入到进程中。 There could be something similar in the executable format used by the MIPS system. MIPS 系统使用的可执行格式可能有类似的东西。

The usual approach is to emulate not only the CPU, but also a representive set of standard peripherals.通常的方法是不仅模拟 CPU,而且模拟一组具有代表性的标准外设。 Then you start an operating system in your emulator which comes with a libc and hardware drivers included.然后你在你的模拟器中启动一个操作系统,其中包含一个 libc 和硬件驱动程序。 Libc will invoke the OSes drivers which invoke the virtual hardware in your emulator. Libc 将调用操作系统驱动程序,这些驱动程序调用仿真器中的虚拟硬件。 For a popular example, see DosBox.有关流行的示例,请参阅 DosBox。

The other interpretation of your question is that you don't want to write a full emulator, but a binary compatibility layer that allows you to execute mips32 binaries on a non-mips32 system.您的问题的另一种解释是您不想编写一个完整的模拟器,而是一个允许您在非 mips32 系统上执行 mips32 二进制文件的二进制兼容层。 A popular example of that is MacOsX (Intel) that can also execute PowerPC applications.一个流行的例子是 MacOsX (Intel),它也可以执行 PowerPC 应用程序。

In the latter scenario you need to emulate either the OSes ABI (application binary interface) or maybe you can get away with libc's ABI.在后一种情况下,您需要模拟 OSes ABI(应用程序二进制接口),或者您可以摆脱 libc 的 ABI。 In both cases you need to implement stub code running on the emulator and proxy code running on the host:在这两种情况下,您都需要实现在模拟器上运行的存根代码和在主机上运行的代理代码:

  • The stub serializes the function call arguments存根序列化 function 调用 arguments
  • ...and transmits them from emulator memory to host memory using some special virtual instructions ...并使用一些特殊的虚拟指令将它们从模拟器 memory 传输到主机 memory
  • The proxy needs to patch the arguments (endianness, integer length, address space...)代理需要修补 arguments(字节序,integer 长度,地址空间...)
  • ...and executes the function call on the host system ...并在主机系统上执行 function 调用
  • The proxy then paches and serializes the outgoing function arguments然后代理对传出的 function arguments 进行打包和序列化
  • ...and transmits them back to the stub ...并将它们传输回存根
  • ...which returns the data to the caller ...将数据返回给调用者

Most calls will not be able to work with generic stub/proxy, but need a specific solutions.大多数调用将无法使用通用存根/代理,但需要特定的解决方案。

Good luck!祝你好运!

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

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