# C standard libraries on bare metal

I am mostly doing development on devices that have ported Linux so the standard C library provides lots of it's functionality through implementing system calls which have a standardised behaviour.

However for bare metal, there is no underlying OS. Is there a standard related to how a c library should be implemented or do you have to relearn peculiarity of a library implementations when you switch to new board which provides a different BSP?

-
Wrong site for your question. – ott-- Mar 21 at 22:34
I'm voting to close this question as off-topic because it belongs on Stack Overflow. – uint128_t Mar 22 at 0:50
Generally you do without. Why would you need such things without an operating system to support them? memcpy and such sure. File systems, not necessarily, although implemented fopen, close, etc is trivial against ram for example. printf() is very very very heavy, tons and tons of code required, do without. any I/O replace or do without. newlib is pretty extreme, but does help if you cant do without, but you have to implement the system on the backend anyway, so do you need the extra layer? – dwelch Mar 22 at 2:36
save bare metal for things that dont need system calls. – dwelch Mar 22 at 2:36
While this question is about software, it is very specific to embedded programming, which generally rejected by SO. Since we already have some good answers here, migration is not appropriate. – Dave Tweed Mar 22 at 12:34

Yes, there is a standard, simply the C standard library. The library functions do not require a "full blown" OS, or any OS at all, and there are a number of implementations out there tailored to "bare metal" code, Newlib perhaps being the best known.

Taking Newlib as an example, it requires you to write a small subset of core functions, mainly how files and memory allocation is handled in your system. If you're using a common target platform, chances are that someone already did this job for you.

If you're using linux (probably also OSX and maybe even cygwin/msys?) and type man strlen, it should have a section called something like CONFORMING TO, which would tell you that the implementation conforms to a specific standard. This way you can figure out if something you've been using is a standard function or if it depends on a specific OS.

-
i am curious as to how a stdlib implements stdio without being dependent on the OS. like fopen(), fclose(), fread(), fwrite(), putc() and getc()? and how does malloc() work without talking to the OS? – robert bristow-johnson Mar 21 at 22:13
With Newlib, there is a layer below it called "libgloss" which contains (or you write) a couple of dozen functions for your platform. For example, a getchar and putchar which know about your hardware's UART; then Newlib layers printf on top of these. File I/O will similarly rely on a few primitives. – Brian Drummond Mar 21 at 23:02
yeah, i didn't read pipe's 2nd paragraph carefully. besides dealing with stdin and stdout and stderr (which takes care of putchar() and getchar()) which directs I/O from/to a UART, if your platform has file storage, like with a flash, then you have to write glue for that also. and you have to have the means to malloc() and free(). i think if you take care of those issues, you can pretty much run portable C in your embedded target (no argv nor argc). – robert bristow-johnson Mar 21 at 23:27
newlib is pretty heavy duty, you are basically making your own OS to some extent, blurring the baremetal line... – dwelch Mar 22 at 1:47
@dwelch You're not making your own OS, you're making the C library. If you don't want that, then yeah, it's unnecessary large. – pipe Mar 22 at 13:15

Is there a standard related to how a c library should be implemented or do you have to relearn peculiarity of a library implementations when you switch to new board which provides a different BSP?

First off, the C standard defines something called a "freestanding" implementation, as opposed to a "hosted" implementation (which is what most of us are familiar with, the full range of C functions supported by the underlying OS).

A "freestanding" implementation needs to define only a subset of the C library headers, namely those that do not require support, or even the definition of functions (they merely do #defines and typedefs):

• <float.h>
• <iso646.h>
• <limits.h>
• <stdalign.h>
• <stdarg.h>
• <stdbool.h>
• <stddef.h>
• <stdint.h>
• <stdnoreturn.h>

When you're taking the next step toward a hosted implementation, you will find that there are only very few functions that really need to interface "the system" in any way, with the rest of the library being implementable on top of those "primitives". In implementing the PDCLib, I made some effort to isolate them in a separate subdirectory for easy identification when porting the lib to a new platform (examples for the Linux port in parenthesis):

• getenv() (extern char * * environ)
• system() (fork() / execve() / wait())
• malloc() and free() (brk() / sbrk())
• _Exit() (_exit())
• time() (not implemented yet)

And for <stdio.h> (arguably the most "OS-involved" of the C99 headers):

• some way to open a file (open())
• some way to close it (close())
• some way to remove it (unlink())
• some way to rename it (link() / unlink())
• some way to write to it (write())
• some way to read from it (read())
• some way to reposition within it (lseek())

Certain details of the library are optional, with the standard merely offering them to be implemented in a standard way but not making such an implementation a requirement.

• The time() function may legally just return (time_t)-1 if no time-keeping mechanics are available.

• The signal handlers described for <signal.h> need not be invoked by anything other than a call to raise(), there is no requirement that the system actually sends something like SIGSEGV to the application.

• The C11 header <threads.h>, which is (for obvious reasons) very dependent on the OS, need not be provided at all if the implementation defines __STDC_NO_THREADS__...

There are more examples, but I don't have them at hand right now.

The rest of the library can be implemented without any help from the environment.(*)

(*)Caveat: The PDCLib implementation is not complete yet, so I might have overlooked a thing or two. ;-)

-

Standard C is actually defined separate from the operating environment. No assumption is made about a host OS being present, and those parts that are host dependent are defined as such.

That is, the C Standard is already pretty bare metal.

Of course, those language parts we love so much, the libraries, are often where the core language pushes that host specific stuff. Hence, the typical "xxx-lib" cross compiler stuff found for many bare metal platform tools.

-

When you use it baremetal, you discover some unimplemented dependencies and have to handle them. All these dependencies are about tuning the internals according to your system's personality. For example when I tried to use sprintf() which uses malloc() inside. Malloc has "t_sbrk" function symbol as a hook in code, which has to be implemented by user to enforce the hardware consrains. Here I may implement it, or make my own malloc() if I believe I could do a better one for the embedded hardware, mainly for other uses, not only sprintf.

-
Why should sprintf need malloc()? – supercat Mar 22 at 20:37
I don't know. I think your point is the buffer it already has, isn't it? But even printf shouldn't need malloc. Maybe to dynamically allocate some internal variables, when the calculation of requested output is heavier than foresight of stacked allocation (dynamic variables of function)? I am sure that sprintf required malloc (arm-none-eabi-newlib). Now I experimented a simple program uses sprintf on computer (glibc). It never called malloc. Then used printf. It called malloc. Malloc was fake, always returning 0. But it worked fine. They were to print a string and a decimal variable. @supercat – luchador Mar 23 at 10:03
I've made a few versions of printf or similar methods myself, customized to support the formats my applications used. Decimal output requires a buffer long enough to hold the longest possible number, but otherwise the base routine accepts a structure that whose first member is an output function that accepts a pointer to that structure along with the data to be output. Such a design makes it possible to add printf variants that output to curses-style consoles, sockets, etc. I've never had a need for "malloc" in such a thing. – supercat Mar 23 at 14:38