Wrapping System Calls

Page Contents

References

Why Wrap System Calls?

The main reason I like to do this is to check error handling in my code. I will wrap system functions so that I can simulate various possible errors that may occur and check that these are gracefully.

For example, lets say some resources have been allocated and then a system call fails. Have I cleaned up correctly? Below I offet a contrived example...

char *some_function() {
    char *buffer = malloc(...);
    if (!buffer) {
        // handle error
    }

    // ...
    // ... Do lots of work and ALLOCATE MORE RESOURCES
    // ...

    FILE *fh = fopen(...)
    if (!fh) {
        free(buffer);
        buffer = NULL;
    }

    // ...
    // ...

    return buffer;
}

If, in the snippet above, fopen() fails, I should free the buffer. I can force the function to fail by wrapping it with my own function that will return NULL and set some desired errno and then check whether the function returns NULL. Note, for memory leak detection Valgrind or Dr. Memory would be the prefered options - this just checks the function logic flow.

How To Wrap System Calls

The GCC linker allows symbols to be linked to wrapper functions when the symbol in a translation unit is undefined. What this means is that when you compile your .c into a .o and the object file references fopen(), the symbol before linkage will be undefined. It is the linker's job to link this to the actual fopen() function. So, what the linker can do is link this to some wrapper function that you specify.

Lets say I write a test file and want to wrap fopen() to test the above function, which I helpfully called some_function(). I would write the following in my test file:

FILE *fh __wrap_fopen(...)
{
    errno = -E_SOME_CODE;
    return NULL;
}

And in my makefile I would write:

my_test: $(TEST_OBJS)
    $(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@ -Wl,--wrap=fopen

Maybe I only want fopen() to fail at certain points in my test. Then in my test file I would write:

static bool some_flag = false;

FILE *fh __wrap_fopen(...)
{
    if (some_flag) {
        errno = -E_SOME_CODE;
        return NULL;
    }
    else {
        return __real_fopen(...);
    }
}

void test_some_feature()
{
    some_function();   // My fopen wrap should work
    some_flag = true;
    some_function();   // Now I know my fopen wrap will fail and I can test condition is handled
}

Using this strategy I can excercise some difficult-to-test paths through my code.