24

The question I have is mostly related to section four, paragraph six.

The two forms of conforming implementation are hosted and freestanding. A conforming hosted implementation shall accept any strictly conforming program.

As I understand, this constitutes the typical application environment, with filesystems, allocated memory and threads...

A conforming freestanding implementation shall accept any strictly conforming program in which the use of the features specified in the library clause (clause 7) is confined to the contents of the standard headers <float.h>, <iso646.h>, <limits.h>, <stdalign.h>, <stdarg.h>, <stdbool.h>, <stddef.h>, <stdint.h>, and <stdnoreturn.h>.

... and this constitutes the typical kernel and/or embedded, bare minimum environment that doesn't have standard filesystems, allocated memory or threads (among other things).

A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any strictly conforming program.

It seems as though this gives a hosted implementation the freedom to call itself a hosted or freestanding implementation, and when it comes to filesystems, allocated memory or threads (among other things), these can fall under the extension category so that it can merely implement an interface that returns a value indicating errors every time. Just to name a few:

  • fopen, fgets and malloc can return NULL
  • fprintf, fscanf, fputc and fgetc can return EOF
  • thrd_create can return thrd_error (indicating that "the request could not be honored")

This implies that the distinction section four, paragraph six gives is virtually meaningless. Are there any requirements guaranteeing some actual level of functionality for these functions in hosted and freestanding implementations? For example, is it required that those functions above actually be able to return something other than their corresponding failure values?

autistic
  • 1
  • 3
  • 35
  • 80
  • 1
    Consider if the standard didn't mention anything about 'freestanding' vs. 'hosted' and required every conforming implementation to support the full standard library. You would still have the loophole that you mention - an implementation could implement much of the library as stubs that return errors. The distinction between freestanding & hosted is not necessary to have the problem that you mention. And the problem still wouldn't be a real world concern (as far as the standard document is concerned - it might be a concern for users as far as quality of implementation). – Michael Burr Jun 14 '15 at 05:18
  • 7
    Anybody that writes a C compiler for an embedded system is free to implement the headers that are not listed the way they want. A rather sane approach is to simply omit functions like fopen() if the system doesn't have a file system, faking it doesn't help anybody. – Hans Passant Jun 14 '15 at 06:45
  • @MichaelBurr: The standard requires some semantics for the standard libraries. So, once you call an implementation "hosted" it actually would have to provide not only dummy functions. Therefore the differentiation: "freestanding" is free to provide a library which actually implements such stubs (whatever that might be good for), but must not call it "standard compliant" nor it self "hosted". – too honest for this site Jun 14 '15 at 12:04
  • @Olaf Can you find a citation supporting what you say from the standard? That's kinda what I expect, since I cited the standard *in* the question... All I can see is that many functions may fail *for obscure reasons*. For example, ["The malloc function returns either a null pointer or a pointer to the allocated space."](http://www.iso-9899.info/n1570.html#7.22.3.4), indicating that an actual hosted implementation may indeed implement `malloc` as `void *malloc(size_t) { return NULL; }` – autistic Jun 14 '15 at 12:33
  • Read the [previous sentence](http://port70.net/~nsz/c/c11/n1570.html#7.22.3.4p2). A function always returning `NULL` obviously would not fullfill that. (it actually would not make much sense to provide such a function anyway. However, That is getting nowhere. Just call your implementation and environment "freestanding", provide just the few headers and as libraries whatever you want to. – too honest for this site Jun 14 '15 at 12:42
  • @Olaf If the standard works the way you think it does, why does `malloc(SIZE_MAX)` most commonly return `NULL`? ["The `thrd_create` function returns ... `thrd_error` if the request could not be honored."](http://www.iso-9899.info/n1570.html#7.26.5.1)... Even basic functions such as `rand` have obscure pits such as ["The rand function returns a pseudo-random integer."](http://www.iso-9899.info/n1570.html#7.22.2.1) (where the distribution of the pseudo-random integers isn't mentioned once prior to that), implying that somebody might consider `int rand() { return 0; }` to be appropriate. – autistic Jun 14 '15 at 12:49

4 Answers4

24

The cited paragraph already states it quite well.

A hosted execution environment is also a freestanding, but not vice versa. A compiler need only provide a freestanding implementation. gcc, for example, is strictly speaking freestanding only, as the standard library is not included. However, it assumes it is available when compiling for a hosted environment (the default), assuming the lib is available on the system (like glibc). See here

Simply put, freestanding is just the language. It is not required to support any libraries and just a few headers (mostly for common types and implementation specific stuff like numerical limits, etc.). This implies the standard library need not exist - nor do the corresponding headers. Reason is a freestanding environment most likely will not have such facilities like files, display, etc. It is used for kernels, bare-metal embedded, etc.

Note that gcc for instance will, if compiling for a hosted environment (-fhosted), assume functions used in the standard library have the corresponding meaning and might apply very aggressive optimizations (it has many of those functions built-in). For freestanding, it actually does not, so you can use a function strcmp for instance with completely different semantics. However, it assumes the mem...-functions to exist, as these are used for normal code, e.g. struct assignment.

So, when compiling for bare-metal without a standard library (or a non-standard standard library), you must use -ffreestanding.

If a hosted implementation calls itself freestanding, it is obviously not a hosted implementation anymore. Once it calls itself hosted, however, it has to provide all facilities required by the standard and is not allowed to just implement dummies, but has to provide the semantics as defined in the standard.

Just to state that clear: The cited section allows a freestanding environment to omit all functions of the library, except for the few listed headers. So you are free to supply any other library and use the same names, but do anything you like. As that would be not the standard library, there is no need for compliance.

5.1.2.1 further states that "Any library facilities available to a freestanding program, other than the minimal set required by clause 4, are implementation-defined.". That does support my statement. Sidenote: It also does not require main() as program entry point.

Cheiron
  • 3,620
  • 4
  • 32
  • 63
too honest for this site
  • 12,050
  • 4
  • 30
  • 52
  • 1
    My question was "Are there any requirements guaranteeing some actual level of functionality for these functions in freestanding implementations? For example, is it required that those functions above actually be able to return something other than their corresponding failure values?". Your answer seems to imply that the answer is "yes". Is there any specific section of a standard document that you can cite? – autistic Jun 14 '15 at 03:00
  • My answer is that a freestanding environment does not even have to supply these functions as defined by the standard - except for the headers explicitly listed. It is just the language, no libs. Maybe 5.1.2.1 states that even more clearly, as 4.6 is mostly about the compiler. Read the (new) last paragraph of my answer. – too honest for this site Jun 14 '15 at 03:19
  • This is a good answer to what the question was, but only half of an answer to what I intended the question to be... so you get a +1 from me for the meantime. I've added in the bold parts to the concluding questions: Are there any requirements guaranteeing some actual level of functionality for these functions in **hosted and** freestanding implementations? For example, is it required that those functions above actually be able to return something other than their corresponding failure values? – autistic Jun 14 '15 at 04:11
  • For hosted environment, you have to provide the semantics as defined by the standard (except for the optinal parts). So you are not allowed to just implement stubs (why should you actually?). But if you revert to a freestanding environment, you actually can unless you call that "standard library". However, there is no statement in the standard you must not use the same names/signature for your stubs, so if you fake the standard library with stubs returning error/fail/etc. these stubs will actually link to any program written for a hosted environment as-if they where the standard. – too honest for this site Jun 14 '15 at 11:53
  • What part of the standard states that `void *malloc(size_t size) { return NULL; }` in a hosted environment is invalid? I'm waiting to be "tutored"... – autistic Jul 09 '15 at 02:45
  • 2
    @Seb: Maybe the same part that would forbid an implementation from behaving as though it requires 50 petabytes of stack space--outputting "Stack overflow" and exiting if that's unavailable--unless given a very particular source file which exercises all implementation limits and then outputs the message "Stack overflow". The authors of the Standard recognized that an implementation could be simultaneously conforming but useless, but expected that implementers would use judgment and try to make their implementations useful. – supercat Apr 04 '17 at 22:33
  • The answer reads "gcc, for example, is strictly speaking freestanding only, as the standard library is not included". Shouldn't it say instead: "gcc, for example, is strictly speaking freestanding only, as the hosted standard library is not included"? The way you put it, gcc is freestanding because there is no standard library, but I thought that even a freestanding impelentation has to provide the freestanding part of the library at least. Or are, by definition, the headers needed not part of the library but of the language itselfe, as I would understand the first sencence of 3th paragraph? – Eric Nov 20 '20 at 18:58
  • @Eric Late I answer, but I do, however, just out of memory, if you want to get that exactly, please read the standard yourself, it is not that complicated. Strictly by the standard, the mandatory (freestanding) headers are part of the standard libray, just because they are listed there. However, technically, they just provide implementation-specific information, so I indeed see them as part of the language itself. The names they define could as well have been made identifiers. ... – too honest for this site Mar 06 '21 at 16:37
  • @Eric ... Interestingly, ´bool´ is currently mapped by ´stdbool.h´ to the ´_Bool´ keyword, but the standard also mentiones a future revision to make it a keyword by itself (along with ´true´ and ´false´). FYI: gcc already defines built-in names for the ´stdint.h´ constants (and some more), so that header just defines the standard macros as aliases to these built-ins. In my personal opinion, all this is just to maintain a (imo) very questionable backwards compatibility with legacy code which (miss)uses these names elsewise. – too honest for this site Mar 06 '21 at 16:42
2

The hosted respectively freestanding implementations which the standard defines are minimal definitions. Both variants are the smallest common denominator of what can reasonably be implemented on a wide range of actual systems.

The rationale for defining such minimal, doable sets is to specify how a program targeting either one of the two flavors must look in order to compile and run with the expected result on ideally all implementations conforming to the respective flavor.

A program strictly conforming to the (i.e., to either) standard is maximally portable.

It goes without saying that actually existing implementations, both freestanding and hosted, typically provide a plethora of extensions, which is fine as far as the standard is concerned — with the one caveat that the extensions must not invalidate a strictly conforming program.

Back to your question: The distinction which the standard makes — the theory — is clear. Actually existing implementations — the practice — fall in a wide spectrum between and beyond the standard's minimal requirements but are bound to the "must not change the behavior" clause regarding strictly conforming programs (targeting hosted or freestanding implementations and not relying on any extensions).

By the way, your examples regarding standard library dummy functions is correct: For freestanding implementations these are just a funny case of allowed extensions: No strictly conforming program targeting freestanding environments would call them anyway. As part of a hosted environment they would be conforming but useless. (One can actually run into situations like that when certain system resources are exhausted, or they could be test stub implementations.)

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
1

There are many kinds of C implementations, targeting many kinds of different execution platforms, many of which can provide a variety of useful features and guarantees that others cannot. The authors of the Standard decided that in most cases it should be sufficiently obvious what kinds of features and guarantees should be provided by implementations targeting various platforms and application fields, and how they should be provided, that there would be no need to have a standard concern itself with such details. On the other hand, the number of applications that would require things like file I/O and the number of platforms that could provide them were sufficient to justify recognizing as "special" those implementations which included such features.

In general, implementations which are intended designed for freestanding use will be usable on platforms that would be unable to usefully handle a hosted implementation. While the Standard imposes some requirements beyond what would be practical on some of the smaller C platforms, some almost-conforming implementations of C can be quite usefully employed on processors with only enough storage to hold 256 instructions and 16 bytes' worth of variables. If something like a digital kitchen thermometer/timer gadget doesn't have a file system or console, why should it waste storage on things like descriptors for stdout?

In addition, because the Standard defines no standard means by which freestanding applications can perform I/O, and because different platforms handle I/O differently, almost freestanding applications will be targeted for a particular target platform or range of platforms. A hosted implementation which doesn't expose the natural features or guarantees the underlying platform would provide could be useful for running programs that don't require such features or guarantees. It's not possible for an embedded program to do much of anything without using platform-specific features and guarantees, however, and thus a freestanding implementation which didn't allow a programmer access to such things would be unable to do much. Quality implementations should allow programmers to use any features or guarantees that may help them accomplish what they need to do, though some may require use of compilation options to ensure that they don't do anything wacky. For some reason, it has become fashionable to regard a decision by the Standards Committee that there might be some implementations and application fields where the value of a feature or guarantee wouldn't justify the cost, as an indication that programmers should not expect implementations to provide a feature or guarantee which would be useful in low-level programming and which the platform would provide as essentially zero cost.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • With regards to that last point about features which would be useful in low-level programming, are you referring to features such as VLAs, and more controversially `gets`? – autistic Apr 15 '17 at 00:56
  • @Seb: I was referring to things like the ability to perform chunking and partial writes to do things like operate upon two 16-bit values as a 32-bit chunk or set the bottom 16 bits of a 32-bit value without a read-modify-write. As it is, the Standard doesn't even require that implementations make it possible to take storage that had been used as one type and use it as an array of another type initially populated with unknown bit patterns (since reading data that had been written as another type could trigger arbitrary behavior even if code is prepared to handle any possible bit pattern) – supercat Apr 15 '17 at 01:04
  • @Seb: A guarantee that reading as uint32_t storage that had previously been written with some other type could have no consequence beyond yielding a meaningless number would seldom cost *anything*, and would allow some kinds of sparse-table code to be more efficient than would otherwise be possible, but the Standard doesn't require implementations to provide even a guarantee as loose as that. – supercat Apr 15 '17 at 01:06
  • Oh? Even if X is `char` and Y is `unsigned char`? – autistic Apr 15 '17 at 01:07
  • 1
    @Seb: The Standard has special rules for pointers of character type, but I'm not sure what X and Y you're referring to. Some people think that a programmer with an aligned pointer who wants to interpret the data there as a uint32_t should do four separate byte operations and be thankful that the compiler is able to figure out that it can substitute a 32-bit operation, but I think it's crazy to think that low-level programmers should write a bunch of code they don't want in the hopes that the optimizer will replace it with what they wanted in the first place. – supercat Apr 15 '17 at 15:03
  • @Seb: What would be helpful I think would be if the authors of the Standard explicitly distinguished between behaviors that should be supported by default by all implementations, and those which should be supported by *recognizable categories* of implementations. If an implementation is designed for high-end number crunching on machines with enough storage that they don't need to recycle it for different types, a requirement that any memory which has ever been used as a particular type will never be used as anything else would allow some kinds of optimization that would not otherwise... – supercat Apr 15 '17 at 15:30
  • ...be possible, but would of course make many kinds of memory management totally impossible. If the authors of the Standard were to say that implementations may impose such requirements *iff* a program contains a certain directive, but also say that implementations may only define a certain macro a particular way if they define aliasing behaviors beyond those presently required, that would make the language more suitable for *both* high-end number crunching and for systems programming. The idea of trying to define a single "compromise" set of semantics needless weakens the language... – supercat Apr 15 '17 at 15:33
  • ...for both purposes. – supercat Apr 15 '17 at 15:33
  • X and Y are placeholders for the two types you referred to. This special character rule (it's not just for pointers) invalidates your assertion. C is by definition high-level, so when you talk about low-level programming in C, well... you're wrong, sorry. As a result of being high-level, with undefined behaviours that might correspond to low-level actions, you can hide your UB behind the abstraction of a function-like macro and default to the portable behaviour for example. That is, if your compiler can't perform this optimisation for you, which in this day and age is crazy, ... – autistic Apr 15 '17 at 18:02
  • ... as when you think of the problems compilers are solving these days (e.g. dead code elimination, loop hoisting) this optimisation is trivial. Can you find a C++ compiler? If so, again, this optimisation should be trivial for any reasonable C compiler. Don't mock the language for the lack of features on a particular implementation, right? Nice answer btw. – autistic Apr 15 '17 at 18:08
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/141791/discussion-between-supercat-and-seb). – supercat Apr 15 '17 at 19:50
  • You do that. I'm happy to move on, having gleaned what I think is sufficient to understand your answer. Any more and I might explode all over some Easter bunnies ;) – autistic Apr 17 '17 at 05:54
0

There are some free standing environments that cannot become hosted environment. Two known conditions that prevent a free standing environment from becoming hosted environment are:

sizeof(char) == sizeof(int);
sizeof(size_t) < sizeof(int signed);
// SIZE_MAX < INT_MAX

Hosted environment explicitly requires

sizeof(char) < sizeof(int);
// (int)EOF != (char)EOF;
sizeof(int unsigned) <= sizeof(size_t);
// UINT_MAX <= SIZE_MAX

If sizeof(char) == sizeof(int), EOF is undefined.

If argc > SIZE_MAX, you cannot call main.

Abraham Le
  • 114
  • 3