Closed cjdb closed 10 months ago
Some folks seem to equate freestanding with "not having a C library". That isn't how I see things -- Freestanding is for when there is no significant runtime support available (for example, printf
isn't there). However, there's no reason why even a kernel environment wouldn't provide a <stddef.h>
header with stuff like size_t
or even <string.h>
with an implementation of memcpy
. For me, freestanding is not a distinction between "there's a C library or there isn't" -- it's a distinction between "I have a lot of runtime support" and "I don't".
I believe the only thing we'd achieve by making libc++ not require a C library in Freestanding mode is that it would be more user friendly for vendors to create a libc++ for Freestanding, since they wouldn't need to build a C library first. But then there could be an issue in case a platform does have a C library in Freestanding mode, and libc++ tries to implement stuff like <stddef.h>
itself instead.
Overall, I think that providing an easy way to build LLVM libc for freestanding environments, and then making sure that libc++ works on top of that, would be a better alternative. Just my .02, curious to hear what folks think.
i've checked libstdc++. they haven't yet provided cerrno in freestanding.
$ ls
algorithm backward bitset compare cstdalign cstddef cxxabi.h expected generator limits numbers ranges source_location tuple typeinfo version
array bit cfloat concepts cstdarg cstdint debug ext initializer_list memory numeric ratio span type_traits utility x86_64-elf
atomic bits climits coroutine cstdbool cstdlib exception functional iterator new optional scoped_allocator string_view typeindex variant
My definition is the libcxx headers need an option to be built without libc. Not the runtime part.
C imports just define them as extern "C" functions.
For things like exceptions and RTTI, we have no way to support them, neither libstdc++ does it.
The errno.h can be forwarded to the compiler side.
But libcxxabi and other llvm components. No need more changes. Maybe the need to disable build if -DLIBCXX_ENABLE_FREESTANDING is defined to make the runtimes happier. But nothing more you can do.
also if the compiler passes -ffreestanding, it should treat as a freestanding toolchain too.
Some folks seem to equate freestanding with "not having a C library". That isn't how I see things -- Freestanding is for when there is no significant runtime support available (for example,
printf
isn't there). However, there's no reason why even a kernel environment wouldn't provide a<stddef.h>
header with stuff likesize_t
or even<string.h>
with an implementation ofmemcpy
. For me, freestanding is not a distinction between "there's a C library or there isn't" -- it's a distinction between "I have a lot of runtime support" and "I don't".
I largely agree with this take. I renamed the issue to say "libc" as opposed to "C standard library", since libc is an artefact of engineering processes, and the C standard library is what ships with a C implementation. libc++ is a part of "the implementation", as is libc, but they're separate due to a separation of concerns. I don't agree with the sentiment that freestanding is not having a C standard library.
I believe the only thing we'd achieve by making libc++ not require a C library in Freestanding mode is that it would be more user friendly for vendors to create a libc++ for Freestanding, since they wouldn't need to build a C library first. But then there could be an issue in case a platform does have a C library in Freestanding mode, and libc++ tries to implement stuff like
<stddef.h>
itself instead.Overall, I think that providing an easy way to build LLVM libc for freestanding environments, and then making sure that libc++ works on top of that, would be a better alternative. Just my .02, curious to hear what folks think.
If we go down this path, then perhaps we could have a config option such as RUNTIMES_BUILD_FREESTANDING
that builds both llvm-libc and libc++ in freestanding mode, and forces libc to be built prior to libc++.
I think clang++ -ffrestanding
is also a reasonable approach, which could define __FREESTANDING__
(that we then act upon). We could require the implementation to have a freestanding libc implementation in order for this to work. That would require some coordination with other parts of the implementation so that the headers are correct, and we might need something like libc_freestanding.a
, but I see a path forward there.
Agree with the above comments. The C language standard also defines a "freestanding" mode; C++ is only half the picture.
While "freestanding" was formerly colloquially defined as "no runtime libraries required", it's now a standardized concept, and certainly does require a set of library functions.
I think attempting to implement substitutes for things traditionally within C's purview in libc++ freestanding mode would be inappropriate, since approximately no users have only C++ code, and no C code. The right solution is for libc++ to build upon a freestanding-libc project. (And LLVM-libc would be a nice home for one such implementation!)
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf Section 4 says a freestanding program can assume the availability of: <float.h>, <iso646.h>, <limits.h>, <stdalign.h>, <stdarg.h>, <stdbit.h>, <stdbool.h>, <stddef.h>, <stdint.h>, and <stdnoreturn.h>
, possibly <fenv.h>
and <math.h>
(if the IEEE FP macros are defined), most parts of <string.h>
, and some parts of <stdlib.h>
.
I note that
@trcrsired
My definition is the libcxx headers need an option to be built without libc. Not the runtime part.
As mentioned by @jyknight, this doesn't work. Being freestanding and needing to build + link against a libc++.a
library are two independent things. Proper usage of libc++ requires linking against a built library (either libc++.so
or libc++.a
), regardless of whether we're in freestanding mode or not. For example, we have external instantiations of std::sort
in the built library and your code won't work if you try to use some forms of std::sort(...)
without linking against the built library.
Since we mostly seem to agree about the fact that removing the dependency on a C Standard Library is a non-goal, I am not certain what action item(s) this issue tracks. @cjdb Do you have something in mind when you mention removing the dependency on "libc" as opposed to a "C Standard Library"? I think we already don't have a dependency on any particular C library implementation -- we just need some implementation to be available.
Since we mostly seem to agree about the fact that removing the dependency on a C Standard Library is a non-goal, I am not certain what action item(s) this issue tracks. @cjdb Do you have something in mind when you mention removing the dependency on "libc" as opposed to a "C Standard Library"? I think we already don't have a dependency on any particular C library implementation -- we just need some implementation to be available.
Good question. The problem---as reported in Discord---led me to believe that libc++ looks for the existence of a libc, which you seem to agree is the case. The chatter from @jyknight to here has convinced me that we might not need to do much at all.
Before closing this issue, I'd like to discuss whether it would be good to get -ffreestanding
to define a __FREESTANDING__
macro that all implementations can use. That would give everyone a common vocab.
@trcrsired
My definition is the libcxx headers need an option to be built without libc. Not the runtime part.
As mentioned by @jyknight, this doesn't work. Being freestanding and needing to build + link against a
libc++.a
library are two independent things. Proper usage of libc++ requires linking against a built library (eitherlibc++.so
orlibc++.a
), regardless of whether we're in freestanding mode or not. For example, we have external instantiations ofstd::sort
in the built library and your code won't work if you try to use some forms ofstd::sort(...)
without linking against the built library.Since we mostly seem to agree about the fact that removing the dependency on a C Standard Library is a non-goal, I am not certain what action item(s) this issue tracks. @cjdb Do you have something in mind when you mention removing the dependency on "libc" as opposed to a "C Standard Library"? I think we already don't have a dependency on any particular C library implementation -- we just need some implementation to be available.
I have previously addressed the std::sort issue in libc++ by implementing a patch that disables it when operating in a freestanding environment, as it is a unique issue specific to libc++. Providing headers unconditionally is necessary to avoid circular dependency issues, such as how one could utilize freestanding C++ to implement a custom libc.
The coverage of libc poses a challenge, as it is an integral part of an operating system, and libcxx cannot feasibly support all existing operating systems. There are over 50 diverse libc implementations I knew, some of which are crafted in C++ (like cxxshim or serenity libc). Some the libcs are even unusable by a C++ compiler (like libcs in PDP11) since they were written in K&R C. Without the provision of headers, implementing hosted features becomes impractical. If you just provide headers, at least they have a chance to use C++ to rewrite some parts to make it work. C++ should be a self-host language because the language has the design goal of leaving no room for a lower-level language. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1105r0.html
You are not solving the issues, or people won't have hacks like cxxshim (https://github.com/managarm/cxxshim) which is totally non standardized, but they have to because even std::addressof, std::move are not provided. Rewriting things like std::move creates tons of issues for portability issues and defeating the purpose of standardization of language. (Ben Craig mentioned that before.) In fact, even non libc libraries have to write their own std::move, std::addressof to overcome these shortcomings. https://github.com/cppfastio/fast_io/blob/master/include/fast_io_core_impl/freestanding/addressof.h Like my library, i am super frustrated of reimplementing things C++ standard has but not provided in freestanding.
Also, for things like memcpy, there was a reason why back before C23, C did not make memcpy freestanding, because memcpy is very difficult to implement efficiently without knowing the environment. Even for x86_64, the fastest implementation of memcpy needs AVX instructions, but floating points registers are not allowed to use in the kernel due to fast context switch. Providing a real implementation is going to make situation worse. Same thing is true for many other C functions (strlen, memcmp). That is why i do think just providing the declaration in the header but not the real implementation in the .a would be a much better choice. It also allows a replacement implementation in some cases like how the API of Linux Kernel driver provides C functions like memcpy, strlen, but not using the ones from glibc.
It appears that the developers of libcxx may not have personally encountered these issues or possess a deep understanding of them. This is understandable, and that's why individuals with the requisite expertise can contribute to maintaining this aspect of the codebase.
Every time I talked about this issue, it seems that some of the libcxx devs behave quite angry for some reasons. I do not know why, probably because they do not work in those environments directly. However, now it is at least better since we can have discussion now.
"Freestanding" has an actual definition in the standards now, which I thought was what we were discussing.
But, I think you may be asking about something more minimal than the standardized "Freestanding" mode -- just the core language support functions. Such a mode may indeed be useful, but someone would have to define it, since it's not defined by the standard.
And libc++ already has work to do in order to implementing the standardized freestanding mode.
There are over 50 diverse libc implementations I knew, some of which are crafted in C++
Such as llvm's own libc or Android's Bionic libc
This is already possible today to implement a libc in other languages, such as C++, though doing so typically involves avoidance of C++'s std library.
This dir contains re-implementations of useful parts of the C++ standard library that are used in llvmlibc: https://github.com/llvm/llvm-project/tree/main/libc/src/__support/CPP
Notice how the codebase intentionally avoids the use of the C++ std library, while making use of C++ language features and providing its own implementation of similar-ish functionality to the std library when necessary.
That isn't how I see things -- Freestanding is for when there is no significant runtime support available (for example, printf isn't there). While "freestanding" was formerly colloquially defined as "no runtime libraries required", it's now a standardized concept, and certainly does require a set of library functions.
I find GCC's docs on this helpful.
The ISO C standard defines (in clause 4) two classes of conforming implementation. A conforming hosted implementation supports the whole standard including all the library facilities; a conforming freestanding implementation is only required to provide certain library facilities ... The standard also defines two environments for programs, a freestanding environment, required of all implementations and which may not have library facilities beyond those required of freestanding implementations, where the handling of program startup and termination are implementation-defined; and a hosted environment ... GCC requires the freestanding environment provide memcpy, memmove, memset and memcmp.
i've checked libstdc++. they haven't yet provided cerrno in freestanding.
Where are you running ls
(it's unclear from your comment). Are you pointing to some notion that libstdc++ has but libc++ does not?
Taking a step back, @trcrsired can you tell us a little more about your use case?
It sounds like you're trying to use the C++ standard library while compiling with -ffreestanding
? In that case, I'd imagine perhaps there are some parts of the standard library that are header-only and don't actually require linking against any library to provide implementations?
My definition is the libcxx headers need an option to be built without libc. Not the runtime part.
I suspect that the result would be an incomplete C++ standard library. How does iostream interact with the platform in that case, for example?
Doesn't libc++ currently depend on locale information from the libc? I'm trying to remember why DLIBCXX_HAS_MUSL_LIBC
is a thing.
I suspect that the result would be an incomplete C++ standard library. How does iostream interact with the platform in that case, for example?
Doesn't libc++ currently depend on locale information from the libc? I'm trying to remember why
DLIBCXX_HAS_MUSL_LIBC
is a thing.
We have flags to disable parts of the library that aren't supported by the environment. e.g. LIBCXX_ENABLE_LOCALIZATION
to disable locale dependencies. If you disable all the optional parts you have essentially a freestanding C++ library implementation assuming (almost?) only the C23 freestanding subset.
@cjdb
Good question. The problem---as reported in Discord---led me to believe that libc++ looks for the existence of a libc, which you seem to agree is the case. The chatter from @jyknight to here has convinced me that we might not need to do much at all.
We require a C Standard Library to exist underneath so we can #include_next
some of its headers, which are required to define things such as size_t
, memcpy
and some more. We don't actually require that much from such a library, but we do require a bit.
Before closing this issue, I'd like to discuss whether it would be good to get
-ffreestanding
to define a__FREESTANDING__
macro that all implementations can use. That would give everyone a common vocab.
This is already the case, although it's done a bit differently. Clang defines __STDC_HOSTED__
to 1
all the time, but if you pass -ffreestanding
then it defines it to 0
instead. That is how we set the _LIBCPP_FREESTANDING
macro in __config
, although that macro is barely used if at all.
@trcrsired
It appears that the developers of libcxx may not have personally encountered these issues or possess a deep understanding of them. This is understandable, and that's why individuals with the requisite expertise can contribute to maintaining this aspect of the codebase.
Every time I talked about this issue, it seems that some of the libcxx devs behave quite angry for some reasons. I do not know why, probably because they do not work in those environments directly. However, now it is at least better since we can have discussion now.
I have personally done the work to set up libc++ on various "unusual" platforms, all of which don't implement a full C Standard Library. I have set it up on platforms where there is no underlying operating system, including a kernel environment. I think I am quite familiar with the problems you are mentioning, however we seem to disagree on the way to get there. You want to avoid shipping a libc++.a
library, and I think that's completely misguided, since the existence of such a library is both orthogonal to being freestanding and is necessary for libc++ to function properly. You want to just "declare" the C library functions like memcpy
, and I think that's just a hack that will move compilation errors to link time.
We do want to support freestanding, and in fact I remember having various discussions with @philnik777 where we discussed ways of getting there. However, here and in the past you've been pushing for your specific way of getting there, and so far most of us have thought that it wasn't the right way, hence the push back. I don't think anybody's angry (at least I am not), but we're also not going to do something wrong just because someone really really wants us to.
Concretely, I see the following possibilities in the domain of "C / C++ library intersection" w.r.t. freestanding:
If you see other options, please let me know. IMO, option (1) is vastly superior because we don't want to be in the business of implementing a C Standard Library, other folks already do it way better than we could.
The other aspect of the Freestanding question (and IMO the only aspect that requires serious discussion and design), is how to enable only the parts that we want in Freestanding mode. Today, libc++ has various "knobs" that allow carving out parts of the library. For example, we have notions for _LIBCPP_HAS_NO_LOCALIZATION
, _LIBCPP_HAS_NO_RANDOM_DEVICE
, _LIBCPP_HAS_NO_FILESYSTEM
, etc. An interesting question is whether Freestanding can be expressed as an intersection of these "carve-outs", or whether we need something else.
So, for example, is it sufficient to define Freestanding as _LIBCPP_HAS_NO_LOCALIZATION + _LIBCPP_HAS_NO_FILESYSTEM + ...
? If not, are we just missing a few knobs for that to be the case? If that's the case, then we have a pretty easy path towards supporting Freestanding. Otherwise, if there's too much of an impedance mismatch between the two, we may have to #ifdef _LIBCPP_FREESTANDING
around the code base, and we'll have to think about how to structure our code for that to be workable. We'll also have to think about how to deal with it in the test suite so that we can have a conformance test suite that works for freestanding environments.
If we had such #ifdef
s, for example, I'm really not certain what other work would be required for libc++ to "just work" in freestanding environments. I don't think there's much that would be required. On the testing side, we'd want to make sure that we don't require more than a Freestanding C Standard Library by probably building our own C Library or using a flavor of llvm-libc in the CI configuration where we test Freestanding, but that's it.
Every time I talked about this issue, it seems that some of the libcxx devs behave quite angry for some reasons. I do not know why, probably because they do not work in those environments directly. However, now it is at least better since we can have discussion now.
I don't know who you're talking about here, but I guess it's me, since I interacted quite a bit with you. I have never been angry. However, I've been very annoyed because you never even tried to argue why your approach is the best. We've been arguing in circles forever with zero progress, since you never engaged in any arguments of mine and never provided ones for your view point. You basically just said "it has to be done this way".
The other aspect of the Freestanding question (and IMO the only aspect that requires serious discussion and design), is how to enable only the parts that we want in Freestanding mode. Today, libc++ has various "knobs" that allow carving out parts of the library. For example, we have notions for
_LIBCPP_HAS_NO_LOCALIZATION
,_LIBCPP_HAS_NO_RANDOM_DEVICE
,_LIBCPP_HAS_NO_FILESYSTEM
, etc. An interesting question is whether Freestanding can be expressed as an intersection of these "carve-outs", or whether we need something else.So, for example, is it sufficient to define Freestanding as
_LIBCPP_HAS_NO_LOCALIZATION + _LIBCPP_HAS_NO_FILESYSTEM + ...
? If not, are we just missing a few knobs for that to be the case? If that's the case, then we have a pretty easy path towards supporting Freestanding. Otherwise, if there's too much of an impedance mismatch between the two, we may have to#ifdef _LIBCPP_FREESTANDING
around the code base, and we'll have to think about how to structure our code for that to be workable. We'll also have to think about how to deal with it in the test suite so that we can have a conformance test suite that works for freestanding environments.If we had such
#ifdef
s, for example, I'm really not certain what other work would be required for libc++ to "just work" in freestanding environments. I don't think there's much that would be required. On the testing side, we'd want to make sure that we don't require more than a Freestanding C Standard Library by probably building our own C Library or using a flavor of llvm-libc in the CI configuration where we test Freestanding, but that's it.
I've started a survey of all the symbols* we define with all the optional parts disabled. From what I can tell for now is that there are a few groups that aren't really handled currently:
@cjdb
Good question. The problem---as reported in Discord---led me to believe that libc++ looks for the existence of a libc, which you seem to agree is the case. The chatter from @jyknight to here has convinced me that we might not need to do much at all.
We require a C Standard Library to exist underneath so we can
#include_next
some of its headers, which are required to define things such assize_t
,memcpy
and some more. We don't actually require that much from such a library, but we do require a bit.Before closing this issue, I'd like to discuss whether it would be good to get
-ffreestanding
to define a__FREESTANDING__
macro that all implementations can use. That would give everyone a common vocab.This is already the case, although it's done a bit differently. Clang defines
__STDC_HOSTED__
to1
all the time, but if you pass-ffreestanding
then it defines it to0
instead. That is how we set the_LIBCPP_FREESTANDING
macro in__config
, although that macro is barely used if at all.
Excellent, thanks for clearing that up @ldionne! I don' think there's much else to do besides ensuring that we're meeting the minimum freestanding requirements at the very least (if we can achieve more: great).
@trcrsired without having a clearer idea of your use-case, it's very difficult to understand your position. Could you please answer @nickdesaulniers' questions before continuing?
I would be tempted to close this issue in favour of a general "implement Freestanding in libc++" issue, since that would define the task more precisely. Does anyone think this issue needs to stay open?
I'm happy with that direction. It doesn't prematurely end the discussion, and refocuses it so that we can identify issues and move forward on a design. My only request is that we consider whether it should be a GitHub issue or a Discourse forum post (no need to discuss that).
I went for an issue for now -- once we have a solution in mind to propose, we should probably go through a Discourse RFC first, but I think the right medium to track this work item at this point is a Github issue. https://github.com/llvm/llvm-project/issues/78350
Also, the valuable discussion here isn't lost since the two issues are linked.
In order to do this, we need to: