libc: Defining Standard Types For Your Platform

Our next step on the libc bringup adventure is an important one: we need to define the standard types. In order to implement the standard types in an extensible manner, we need to support different type definitions on different platforms.

Today we'll look into the implementation of two headers: stddef.h and stdint.h. Next week we will tackle more platform-specific headers, such as limits.h and endian.h

stddef.h

If you look into your system's stddef.h header, or even musl libc's implementation, you will see a large number of types. Any system-wide types are typically thrown into stddef.h to provide convenient global access.

We are going to skip most of those types and focus only on this small set:

  • size_t
  • ssize_t
  • NULL
  • offsetof
  • max_align_t
  • ptrdiff_t

Feel free to follow Apple and musl's lead and add in other system-wide types to your stddef.h implementatation.

stddef.h

First we'll take a look at the implementation specifics for the stddef.h. Following the implementation overview we'll look at the completed header.

NULL

NULL is a very important definition. In C, it is defined as (void*)0, but for C++ we'll need to make sure it is only represented as 0:

#ifdef __cplusplus
#define NULL 0L
#else
//not C++
#define NULL ((void *) 0)
#endif

offsetof

offsetof is a very useful macro. If you have a recent C compiler, this instruction is likely a builtin. If not we still provide the macro implementation of the function.

#if __GNUC__ > 3
#define offsetof(type, member) __builtin_offsetof(type, member)
#else
#define offsetof(type, member) ((size_t)( (char *)&(((type *)0)->member) - (char *)0 ))
#endif

max_align_t

max_align_t is a new implementation, requiring C11/C++11.

#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
    || (defined(__cplusplus) && __cplusplus >= 201103L)
typedef long double max_align_t;
#endif

ptrdiff_t

ptrdiff_t is one of the types that can have different definitions depending on your target architecture.

For architecture-specific definitions, we will create standalone header files that will be included in our primary stddef.h header. This allows us to switch which architecture headers to include at compile time.

#ifndef __PTRDIFF_T_H_
#define __PTRDIFF_T_H_

typedef long ptrdiff_t;

#endif // __PTRDIFF_T_H_

size_t

size_t and ssize_t are also types that requires a platform-specific definitions. We'll create another standalone header and implement those types:

#ifndef __SIZE_T_H_
#define __SIZE_T_H_

typedef unsigned long size_t;

typedef long ssize_t;

#endif // __SIZE_T_H_

Completed Header

Now that we've looked at each of the individual types, let's put them all together into a comprehensive header. Don't forget that you can add other system-wide type definitions in stddef.h.

Note the following lines:

#include <_types/_size_t.h>
#include <_types/_ptrdiff_t.h>

These headers are where we put our definitions for size_t, ssize_t, and ptrdiff_t. The _types folder referenced is included for each architecture that I support.

#ifndef __STDDEF_H_
#define __STDDEF_H_

#include <_types/_size_t.h>
#include <_types/_ptrdiff_t.h>

#pragma mark - NULL -

#ifdef __cplusplus
#define NULL 0L
#else
//not C++
#define NULL ((void *) 0)
#endif

#pragma mark - offsetof -

#if __GNUC__ > 3
#define offsetof(type, member) __builtin_offsetof(type, member)
#else
#define offsetof(type, member) ((size_t)( (char *)&(((type *)0)->member) - (char *)0 ))
#endif

#pragma mark - max_align_t -

#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
    || (defined(__cplusplus) && __cplusplus >= 201103L)
typedef long double max_align_t;
#endif

#endif //__STDDEF_H_

stdint.h

stddef.h was a pretty straightforward implementation. stdint.h requires more platform-specific definitions. Let's take a look at those first.

Platform-specific standard types

Similar to size_t and ptrdiff_t, I define the standard types in the arch-specific _types folder.

For the sake of readabiltiy, I have grouped all of my type definitions for x86_64 into one block. Each of these definitions is a different header in my repository. You can use the combined or split version - there's no hard rule here!

typedef signed char int8_t;
typedef    short int16_t;
typedef    int    int32_t;
typedef    long long int64_t;
typedef long int intmax_t;
typedef long intptr_t;

typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef long unsigned int uintmax_t;
typedef unsigned long uintptr_t;

Completed Header

For the main header implementation, I leveraged Apple's Open Source libc implementation of stdint.h. For brevity, I will not include the full file. Please refer to the Apple implementation, or check my implementation on embedded-resources.

I want to highlight the section below. Note that each of the types is included from the arch-specific location:

/* 7.18.1.1 Exact-width integer types */
#include <_types/_int8_t.h>
#include <_types/_int16_t.h>
#include <_types/_int32_t.h>
#include <_types/_int64_t.h>
#include <_types/_uint8_t.h>
#include <_types/_uint16_t.h>
#include <_types/_uint32_t.h>
#include <_types/_uint64_t.h>

/* 7.18.1.2 Minimum-width integer types */
typedef int8_t           int_least8_t;
typedef int16_t         int_least16_t;
typedef int32_t         int_least32_t;
typedef int64_t         int_least64_t;
typedef uint8_t         uint_least8_t;
typedef uint16_t       uint_least16_t;
typedef uint32_t       uint_least32_t;
typedef uint64_t       uint_least64_t;


/* 7.18.1.3 Fastest-width integer types */
typedef int8_t            int_fast8_t;
typedef int16_t          int_fast16_t;
typedef int32_t          int_fast32_t;
typedef int64_t          int_fast64_t;
typedef uint8_t          uint_fast8_t;
typedef uint16_t        uint_fast16_t;
typedef uint32_t        uint_fast32_t;
typedef uint64_t        uint_fast64_t;


/* 7.18.1.4 Integer types capable of holding object pointers */
#include <_types/_intptr_t.h>
#include <_types/_uintptr_t.h>

/* 7.18.1.5 Greatest-width integer types */
#include <_types/_intmax_t.h>
#include <_types/_uintmax_t.h>

Putting it All Together

Please see embedded-reserouces for completed stddef.h and stdint.h implementations. I have also included example headers for x86_64.