Since the Standard mentions the details of errno.h first, 7.1.4 "Errors <errno.h>", then we'll start there. This post will look into how to implement a global variable, errno, some macros, and how to properly link object files together so that all of your symbols resolve correctly.

About errno.h

The standard states that errno.h provides several macros relating to the reporting of error conditions. Specifically, a "modifiable lvalue that has type int", and two macros: EDOM and ERANGE. So, what is a modifiable lvalue and what are macros?

Lvalues

The standard defines lvalues in section 6.2.2.1, "Lvalues and function designators". An lvalue is an expression that designates an object, with an object type or incomplete type other than void. The definition of a modifiable lvalue is a little more complicated: an lvalue that doesn't have an array type, an incomplete type, or a const-qualified type, and if it is a structure or union, it has no members that are of const-qualified type. Basically, an object which you can assign a value to. The term "lvalue" can be thought of as meaning "left value". In expressions like object = value;, the object is a modifiable lvalue; it's on the left and we are modifying it.

Macros

Macros are a bit simpler. Section 6.8.3 of the standard, "Macro replacement", states that the identifier immediately following the define is called the macro name. For example:

#define MY_MACRO    (10)

MY_MACRO is the macro name. Immediately following a macro of this form is the replacement-list followed by a new-line. This means that when the preprocessor runs through our C code, every instance of MY_MACRO will be replaced with the characters (10). This makes our code much more readable and allows us to avoid using "magic numbers" which are values which have no apparent meaning.

For example, if you are using strings within your program and you know that you only want to handle strings which are less than or equal to 30 characters in length, you could have something like:

if (30 >= strlen(myString))
{
    /* process string */
}

This is fine since you wrote it, but there is a good chance someone else may look through your code and when they come across this 30 they would wonder what meaning it has. It might be easy to infer than it's the max string length, but they would have to further investigate your code to be sure. Instead, you could define a macro for the max string length and then use it like so:

#define MAX_STR_LEN     (30)

...

if (MAX_STR_LEN >= strlen(myString))
{
    /* process string */
}

Now the next person can easily determine that you were checking to be sure the string was within your limits. In addition, it's likely that you would use that value several times within your program and at some point you might decide that you will now handle strings up to 50 characters in length. Instead of having to update every instance of 30 that corresponds to your string length, you only need to modify your macro and everything is automatically updated for you! This will help you avoid bugs since every relevant instance of your max string length is updated, rather than you having to find and replace them manually. Let the preprocessor do the work for you.

errno

So, errno is a modifiable lvalue of type int which should mean we can just define it as int errno;, right? Well, the standard explicitly states that errno does not have to be the identifier of an object and that it could expand to the result from a function call like *errno(). The way I'm choosing to implement it is with an object of type int, rather than with a function. The use case for having a function macro is that every thread of your program would have its own instance of errno. This prevents threads from stepping on each other's error conditions. However, multi-threading support is another beast so I'll just stick to getting things working on one-thread before moving on to more.

The idea behind errno is that the library will update errno when somethings goes wrong and return an indication to the caller that it should check errno. This means that our errno object needs to have global scope such that any part of the code can check it. The way we accomplish this with C is through the use of the extern storage-class specifier.

Declarations vs. definitions

We need to take a short detour on the difference between declarations and definitions. The standard provides some information about them in section 6.5, "Declaration". A declaration specifies the interpretation and attributes of a set of identifiers; i.e. the storage-class, type-specifier, type-qualifiers, and the name. A definition is what causes storage to be reserved for an object.

Some examples of declarations in C look like:

extern int a;

int
main(void);

Note that these are at file scope, and not within a function. This declares an object, a, with external linkage and of type int, in addition to a function of type int, named main which takes no parameters. This indicates to the compiler that these two objects will be defined elsewhere and not to reserve any storage for them since the storage will be reserved by other code.

Some examples of definitions in C look like:

int a;

int
main(void)
{
    int b;
    int c = 0;

    return c;
}

Each object here is defined and has space reserved for it: a, main, b, and c. The two questionable objects are a and b; we never assigned any value to them yet they are still definitions? With C, declarations at file scope, without external linkage (i.e. without the extern storage-class), also count as definitions. Likewise, all function local variable declarations are also definitions regardless of whether or not you assigned a value to them.

Implementing the errno object

Since we want multiple files to be able to access the errno object, we would need to place the line extern int errno; in every file that needs access to errno. The way we will accomplish this is by creating the header file, errno.h, and placing this declaration inside it. Now, any file wishing to access errno can just include errno.h and it will get access. This is only declaring the errno object, so we also need to provide a definition for errno so that space is reserved for it such that the library can actually assign it a value and other files can check that value. We will have a matching errno.c file with int errno; in it providing the definition for our errno object. It is unnecessary to initialize errno since it is an external variable. The standard dictates that objects with external linkage have static storage duration (6.1.2.4 "Storage duraction of objects"), that objects in static storage are initialized before program startup (5.1.2 "Execution environment"), and that the value they are initialized to is 0 if no value is provided (6.4.7 "Initialization").

So, errno.h will contain the declaration for errno:

extern int errno;

errno.c will contain the definition for errno:

int errno;

And files which want to access errno only need to include errno.h like so:

#include <errno.h>

EDOM and ERANGE

errno.h must also provide these two macros which indicate two different error conditions, defined in 7.5.1, "Treatment of error conditions". EDOM indicates a domain error which means that the input argument is outside the domain over which the function is defined; e.g. if your function accepts inputs from 0 to 5 and you pass 10 to it, you've caused a domain error. ERANGE indicates a range error which means that the result of a function cannot be represented as a double value; e.g. if you try to take the log of 0 then you cause a range error because log(0) is undefined and cannot be represented.

Since we're using macros for EDOM and ERANGE it doesn't really matter what value we give them since the macro name would be used to check errno. We will just use the values 1 and 2 the represent EDOM and ERANGE, respectively. Just like errno, we want other files to have access to EDOM and ERANGE; we'll place them in the errno.h file so that anyone accessing errno automatically gets access to these macros as well.

We'll add the following to errno.h:

#define EDOM    (1)
#define ERANGE  (2)

Header Guards

Since we've written our first header file, we also need to talk about header guards. When you #include a file, the contents of that file are inserted into the current file at the location of the #include directive. When multiple files include the same file, the contents get included multiple times and you can end up with multiple definition errors.

To prevent this, we use preprocessor directives to control when the contents of the header file get used. Header guards look like the following:

#ifndef _HEADER_H
#define _HEADER_H

/* contents of header */

#endif /* _HEADER_H */

This works by checking to see if the identifier _HEADER_H has not been defined yet. If it hasn't been defined, in the case of the first occurrence of a file including this header, then the code between the #ifndef and #endif is processed and the identifier _HEADER_H is defined. If the identifier _HEADER_H has been defined, in the case of any inclusion of the header after the first, the code inside the conditionals is not processed. Anytime you use a header, you should use header guards to prevent errors from occurring when the header needs to be included by multiple files.

Identifier name

The identifier we used, _HEADER_H, will usually correspond to the specific name of the file that the header guards are placed in. The reason that we prepend these with an underscore, _, is because tokens of this naming scheme are specifically reserved by the standard for use in the standard library. Section 7.1.3, "Reserved identifiers" states what names are reserved, and our identifier falls within "identifiers that begin with an underscore and either an uppercase letter or another underscore". Specifically for our errno.h file, our header guards will look like:

#ifndef _ERRNO_H
#define _ERRNO_H

/* contents of header */

#endif /* _ERRNO_H */

Implementation of errno.h

That finishes up all the pieces we need for errno.h. Combining all these things together, our implementation of errno.h is below (without comments):

#ifndef _ERRNO_H
#define _ERRNO_H

#define EDOM    (1)

#define ERANGE  (2)

extern int errno;

#endif /* _ERRNO_H */

Makefile changes

A few modifications to the Makefile were needed to facilitate building object files from C source files and to use our header directory instead of the standard system header directory.

Compiling C files into object files

This addition was pretty simple. We we're previously only assembling .s files, but now that we have some C code, we also want to build objects from .c files. We can add a new pattern substitution that appends more object names to the OBJECTS variable, like so:

OBJECTS +=  $(patsubst $(SRCDIR)/%.c,%.o,$(wildcard $(SRCDIR)/*.c))

Defining the system include directory

By default, gcc will use /usr/include as the location for finding system header files. System headers are wrapped with <> rather than "" when you #include them. Since we want to use the header files in our ./include directory, we need to tell gcc not to use files found in /usr/include. The -nostdinc flag tells gcc not to search the standard system directories for header files, exactly what we want! Now, we need to tell gcc where to search for system headers with the -isystem flag. You simple provide a directory path to this argument, so from the base of our directory structure we will use the ./include directory by adding -isystem ./include.

Compile for a specific standard

gcc also includes specific rules for compiling against a certain standard. Since we are writing this for ISO 9899:1990 we want to compile for that standard by passing the following flag: -std=iso9899:1990.

Testing errno.h

Since we implemented errno.h then we need to write a test to ensure that things work the way we want them to. This should be pretty simple because all we need to do is #include <errno.h> and ensure that we can use the errno object as well as the EDOM and ERANGE macros. The test I wrote looks like so:

#include <errno.h>

/*******************************************************************************
    errno_test
*//**
    @brief  Tests the functionality of errno.h

    @return Indication of success
    @retval 0   Successfully ran all tests
    @retval 1   Failed to run tests
*******************************************************************************/
int
errno_test(void)
{
    int ret = 0;

    /* Ensure errno starts out as 0 */
    if (0 == errno)
    {
        /* Test setting errno to all defined values */
        errno = EDOM;
        errno = ERANGE;

        /* Reset since no errors occurred */
        errno = 0;
    }
    else
    {
        ret = 1;
    }

    return ret;
}

Link order

Lastly, we need to slightly alter the order of our build command so that our symbols will resolve properly. Initially, we used the following recipe to link our test code against the library:

$(TSTNAME): $(LIBNAME) $(TSTOBJS)
    $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(patsubst $(LIBNAME,,$^))

When I compiled this way, I kept getting the following errors:

errno_test.o: In function `errno_test':
errno_test.c:(.text+0xd): undefined reference to `errno'
errno_test.c:(.text+0x17): undefined reference to `errno'
errno_test.c:(.text+0x21): undefined reference to `errno'
errno_test.c:(.text+0x2b): undefined reference to `errno'

The first thing I checked was that the library actually had the errno symbol and that it was global. You check this by dumping the symbol table with objdump -t:

$ objdump -t libwelibc.a

In archive libwelibc.a:

_start.o:     file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l    d  .text  0000000000000000 .text
0000000000000000 l    d  .data  0000000000000000 .data
0000000000000000 l    d  .bss   0000000000000000 .bss
0000000000000000 g       .text  0000000000000000 _start
0000000000000000         *UND*  0000000000000000 main



errno.o:     file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l    df *ABS*  0000000000000000 errno.c
0000000000000000 l    d  .text  0000000000000000 .text
0000000000000000 l    d  .data  0000000000000000 .data
0000000000000000 l    d  .bss   0000000000000000 .bss
0000000000000000 l    d  .note.GNU-stack        0000000000000000 .note.GNU-stack
0000000000000000 l    d  .comment       0000000000000000 .comment
0000000000000004       O *COM*  0000000000000004 errno

Hrm, we can see the last entry is errno and that it's placed in the .common section of the ELF (indicated by *COM*). The .common section holds uninitialized global variables and is where we would expect to see errno since we didn't specificically initialize it in errno.c. So, the symbol is there but the linker is having trouble resolving it.

When linking object files together, the order in which you list them makes a difference in how the symbols are resolved during linking. Eli Bendersky wrote a great article explaining this, Library order in static linking. It even has an example of this exact situation of linking against a library. The answer to the problem is to place the library after the other object files we are linking together. So, we just move the $(LDFLAGS) variable after the pattern substitution like so:

$(TSTNAME): $(LIBNAME) $(TSTOBJS)
    $(CC) $(CFLAGS) $(patsubst $(LIBNAME),,$^) $(LDFLAGS) -o $@

I also moved the output file to the end because that seems to make sense to me.

Conclusion

With all that, we now know what modifiable lvalues and macros are, how to use global variables with C, and how to link properly with our own system headers. We now have the error reporting facilities we need to move on with the library. The next piece we will implement is 7.1.5, "Limits <float.h> and <limits.h>".

comments powered by Disqus