Question

Consider I have the following function in a dynamic external library libExternal.dylib:

void print(char* str)
{
    // Changes the first char to 'a' and prints the string
    *str = 'a';
    printf("%s\n", str);
}

Next, I have an executable that loads this external library and calls the function (error checking omitted):

int main(int argc, const char * argv[])
{
    void* hLib = dlopen("libExternal.dylib", RTLD_LAZY | RTLD_LOCAL);

    typedef void(*printFunc)(const char*);
    printFunc func = (printFunc)dlsym(hLib, "print");

    std::string test = "hello";
    func(test.c_str());

    dlclose(hLib);

    return 0;
}

As you can see, the function defined in the library takes a char* as parameter. When using dlsym, I made it get a function that takes a const char*. And it works!

My question is, how is this possible? The dynamic loader ignores const types? I really couldn't find an answer anywhere, so please help me! :)

EDIT: I know this code is wrong, I'm just trying to understand how is this possible.

Was it helpful?

Solution

It works but it doesn't mean it's correct.

It doesn't ignore const types, you're casting the external function to a function which accepts const types:

typedef void(*printFunc)(const char*);
                         ^^^^^^^^^^^

printFunc func = (printFunc)dlsym(hLib, "print");
                 ^^^^^^^^^^^

And try to use right function signature to avoid undefined behavior due to modifying a const value.

OTHER TIPS

Passing const char * to foo(char *str) is undefined behavior (UB).

It could work, it might not. It certainly will not work on systems that prevent writing to const memory. Other systems are lenient and subsequent operations may / may not work as expected.

C11 draft 6.7.3. 6

Consider this code:

#include <stdio.h>
int
main(int argc, char **argv)
{
        char a[] = "foo";
        const char *b = a;
        char *c = (char *)b;
        *c = 'a';
        printf("%s\n", b);
        return 0;
}

That's on a high level equivalent to what you're doing internally. Theoretically char *c = (char *)b; *c = 'a'; is illegal, but in practice it happens to work in this particular case.

See const as a kind of contract between the writer of the API and the user of that API rather than something that is strictly enforced by the compiler and runtime. A secondary reason for const to exist is that it allows string literals to be put into read-only segments in programs opening up for many useful optimizations (like deduplication of strings), but I'd argue that primarily const is a just reminder for the programmer.

This is why in large projects I've seen adding const to function arguments is sometimes called "const poisoning". You poison some string or struct that is being passed around many different layers of APIs to make a promise that it isn't modified anywhere through the layers. The act of adding const by itself is very valuable to discover where unintended modifications happen. You can always get rid of the const quite easily and break the contract, but by not doing that you make it easier to read and debug your program. I've even done some experiments and compilers don't do optimizations that would be reasonable to do if the compiler expected const to be respected.

Sometimes it's even necessary to cast away const for completely legitimate reasons. For example when you have something like this:

struct foo {
    const char *string;
    int dynamic;
};
void
foo_dynamic(struct foo *f, int x)
{
    f->dynamic = 1;
    f->string = malloc(16);
    snprintf(&s->string, 16, "%d", x);
}
void
foo_static(struct foo *f, const char *x)
{
    f->dynamic = 0;
    f->string = x;
}
void
foo_free(struct foo *f)
{
    if (f->dynamic)
        free((void *)f->string);
    free(f);
}

In this case we make the promise in our API that we won't change the contents of what foo->string points to during the life time of foo, but we still need to be able to free it if we allocated it ourselves. A language fundamentalistlawyer might say that this is undefined behavior (it is) and there are solutions to achieve the same thing (there are), but this is pretty common in practice.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top