1. 9
  1.  

  2. 2

    This is a portable libc from Managarm OS, initially with backends for Linux and Managarm, and later ported to other hobby OSes.

    mlibc is written in C++. For example, printf is a thin wrapper around StreamPrinter class implemented with C++ template.

    1. 3

      It looks interesting but I find their code structure completely opaque. I wish they’d put something in their README to give me some hints about where to look for the implementations of their code. Most libcs have a similar structure to the original UNIX libc. LLVM’s one is a bit different but is well documented. This one is completely different.

      1. 1

        Eh, it’s there in README?

        1. 5

          Okay, where is the printf code? The README tells me it’s probably somewhere in options/ (which contains mostly platform-independent code). That directory contains something called ansi, I guess that’s where I’d look since printf is an ISO C API? That directory contains generic, include and musl-generic-math. It’s definitely not either of the last two, so I guess it’s in the first one? There are some files in there that have stdio in their name, but they also have stubs in their name, so I guess that’s now where I’d find the implementation.

          In contrast, in most libc implementations, I’d find it in src/stdio/printf.c. I’ve worked on a few libc implementations and this is the only one where five minutes of browsing the source didn’t take me to the printf implementation. I’m sure I could use code search to find it but that doesn’t help me find the next thing unless I understand the underlying logic of the layout. I presume it has some and so I’d like the README to explain it to me.

      2. 1

        So it’s the C standard library, written in C++? What?

        1. 1

          As long as the functions are exported correctly, why would the internals matter? There’s also rust https://github.com/anp/rusl zig https://github.com/tiehuis/zligc and probably many others

      3. 2

        What’s the motivation for using this over another implementation, like musl? Not trying to crap on the project, just genuinely curious.

        1. 3

          musl is a libc for Linux. It won’t work for Managarm.

          1. 4

            musl has been ported to quite a few environments and is fairly common in bare-metal toolchains. That said, it doesn’t have a clean OS abstraction and the ports generally work by providing a stub layer that looks like Linux, which isn’t ideal. Musl is also a C codebase and a C standard library can be a lot cleaner and simpler if implemented in a higher-level language. For example, there are a bunch of things that, on modern systems, end up being reference counted (e.g. bits of locale state) and having std::shared_ptr available is much nicer than remembering to manually do the refcounting for these things.

            1. 2

              The Fuchsia libc is based on musl but a lot of hacking and slashing was required to free it from the assumptions of a posixy unix world.

              1. 1

                There seem to be a lot of Fuchsia people involved with LLVM libc, so I’m guessing that this experience wasn’t particularly pleasant for them?

                1. 1

                  My own involvement with Fuchsia’s libc has been pretty small so I’m not the best to answer authoritatively, but my sense is that it was great to have a relatively small libc to build our platform specific libc on, but they saw the value in having a portable libc that could be shared with other platforms. To get musl working on Fuchsia it had to be chopped up in ways that makes what we have a fork rather than a port - it would be nice to be able to take a cleaner approach.

        2. 2

          Is it just me or does the test suite seem woefully inadequate?

          Also, using C++ to implement a core C library feels a bit like the snake that eats its own tail.

          1. 1

            Also, using C++ to implement a core C library feels a bit like the snake that eats its own tail.

            LLVM’s libc is also using C++. The requirements for a language implementing libc are:

            • Must not depend on anything that libc provides (at least, in the parts that depend on that).
            • Must be able to export C symbols.

            C++ meets these requirements as does Rust with nostd. A lot of the things in libc end up being macros that provide error-prone implementations of C++ templates. For example, qsort, qsort_r and qsort_b are all exactly the same algorithm, with minor tweaks to how they invoke the callbacks. Some things, such as bits of locale state, need atomic reference counting. You can implement these in C with explicit calls to incref and decref functions but using C++ smart pointers makes it almost impossible to get wrong.

            I’ve worked a reasonable amount on things in libc implementations and a significant fraction of that time has involved thinking ‘this would be much less code and easier to audit if I wrote it in C++’.

            We’ve implemented malloc in C++ and that’s one of the lowest-level parts of libc. It’s about half the size of jemalloc (which is written in C) and performs better.

            1. 1

              I suppose I say this because I work in the embedded world, where C++ for C libs doesn’t fly.

              1. 1

                I don’t really buy that argument. C++ can generate at least as small code as C (there was a great talk someone linked to here using modern C++17 features to compile for a 6502 and generating code as good as hand-written assembly). The only embedded programming that I’ve done has been on things like the Cortex-M0 and the SDKs supported C++ out of the box and let me write high-level generic code that compiled down to a few hundred bytes of object code when instantiated for my specific SoC. Mind you, they were freestanding environments and so didn’t actually have a libc.

                There are only two reason that I wouldn’t use C++ in an embedded context. The first isthe lack of a C++ compiler. That still happens for some of the more esoteric ISAs but it isn’t a problem for any M-profile Arm cores and 16 KiB of RAM is plenty for C++. The other is if I’m right at the constrained end of the spectrum (things with on the order of hundreds of bytes of instruction memory, 1 KiB of data memory) and there I wouldn’t use C either, I’d use assembly, because any language that assumes a stack would be a problem (though the 6502 talk I mentioned above relied on inlining to completely eliminate the stack, so C++ might even be feasible there).

                You do generally need to disable exceptions and RTTI for embedded C++ work (but you often do that even on large systems). You also need to think about your use of templates, to ensure that you’re not bloating your code, but you need to do the same with C macros and C++ gives you tools like non-templated base classes for common logic that make this kind of thing easier. C++ also makes it easy to force certain bits of code to be evaluated at compile time (giving a compile failure if they aren’t), which means that you’re less at the whim of optimisers for code size than with C. Most of the C codebases I’ve seen that need this end up with something horrible like Python code that generates a C data structure or even C code, whereas in C++ everything can be in the same language.

                C++ is far from a perfect language but it’s a much better language than C for anything that C is used for.

            2. 1

              First, C++ is overall a better language than C. Second, libc is in no way a “core” library: it is a compatibility layer. For example, consider position of CRT in Windows. Third, Managarm kernel is written in C++, so it is natural to use the same in userland too.

              1. 1

                Second, libc is in no way a “core” library: it is a compatibility layer.

                This assumes you have an operating system.