16

I am a bit puzzled on how and why this code works as it does. I have not actually encountered this in any project I've worked on, and I have not even thought of doing it myself.

override_getline.c:

#include <stdio.h>

#define OVERRIDE_GETLINE

#ifdef OVERRIDE_GETLINE
ssize_t getline(char **lineptr, size_t *n, FILE *stream)
{
    printf("getline &lineptr=%p &n=%p &stream=%p\n", lineptr, n, stream);
    return -1; // note: errno has undefined value
}
#endif

main.c:

#include <stdio.h>

int main()
{
    char *buf = NULL;
    size_t len = 0;
    printf("Hello World! %zd\n", getline(&buf, &len, stdin));
    return 0;
}

And finally, example compile and run command:

gcc main.c override_getline.c && ./a.out

With the OVERRIDE_GETLINE define, the custom function gets called, and if it is commented out, normal library function gets called, and both work as expected.

Questions

  1. What is the correct term for this? "Overriding", "shadowing", something else?

  2. Is this gcc-specific, or POSIX, or ANSI C, or even undefined in all?

  3. Does it make any difference if function is ANSI C function or (like here) a POSIX function?

  4. Where does the overriding function get called? By other .o files in the same linking, at least, and I presume .a files added to link command too. How about static or dynamic libs added with -l command line option of linker?

  5. If it is possible, how do I call the library version of getline from the overriden getline?

hyde
  • 60,639
  • 21
  • 115
  • 176

4 Answers4

18

The linker will search the files you provide on the command line first for symbols, before it searches in libraries. This means that as soon as it sees that getline has been defined, it will no longer look for another getline symbol. This is how linkers works on all platforms.

This of course has implications for your fifth point, in that there is no possibility to call the "original" getline, as your function is the original from the point of view of the linker.

For the fifth point, you may want to look at e.g. this old answer.

Community
  • 1
  • 1
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • No way to call the original ? How about using `dlopen` + `dlsym` ? – cnicutar Sep 26 '13 at 08:33
  • 1
    @cnicutar Well, no *easy* way. The easiest might be using the `--wrap` argument to the linker, on which I just updated my answer with a link on how to do it. – Some programmer dude Sep 26 '13 at 08:35
  • 1
    Compiling with `gcc -Wl,--wrap=getline main.c override_getline.c` after renaming function to `__wrap_getline`, and calling `__real_getline` from it after the debug print worked beautifully (main.c still calling plain `getline`)! – hyde Sep 26 '13 at 08:42
  • As for a standard compiler-agnostic approach: couldn't you create wrapper functions with different names (prefixed) that invoke the original symbols, link this against the original library into an object file and then create wrappers with original names that invoke the already linked prefixed function names? Haven't tried this out though. – filip Oct 17 '16 at 09:12
4

There's no standard way to have two functions of the same name in your program, but with some UNIX-like implementations (notably GNU libc) you might be able to get away with this:

#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>

ssize_t getline(char **lineptr, size_t *n, FILE *stream)
{
   ssize_t (*realfunc)(char**, size_t *, FILE*) =
       (ssize_t(*)(char**, size_t *, FILE*))(dlsym (RTLD_NEXT, "getline"));
   return realfunc(lineptr, n, stream);
}

You will need to link with -ldl for this.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
1

What is happening here is that you are relying on the behaviour of the linker. The linker finds your implementation of getline before it sees the version in the standard library, so it links to your routine. So in effect you are overriding the function via the mechanism of link order. Of course other linkers may behave differently, and I believe the gcc linker may even complain about duplicate symbols if you specify appropriate command line switches.

In order to be able to call both your custom routine and the library routine you would typically resort to macros, e.g.

#ifdef OVERRIDE_GETLINE
#define GETLINE(l, n, s) my_getline(l, n, s)
#else
#define GETLINE(l, n, s) getline(l, n, s)
#endif

#ifdef OVERRIDE_GETLINE
ssize_t my_getline(char **lineptr, size_t *n, FILE *stream)
{
   // ...
   return getline(lineptr, n, stream);
}
#endif

Note that this requires your code to call getline as GETLINE, which is rather ugly.

Paul R
  • 208,748
  • 37
  • 389
  • 560
  • If I define `getline` twice in the application code (different source files), then I get linker *error*. Here there's not even a warning. I'll see if I can find switches which will throw a warning with the question code. – hyde Sep 26 '13 at 08:36
  • I think this may be due to the difference between *object files* and *libraries* and the way they area treated by the linker. – Paul R Sep 26 '13 at 08:40
0

What you see is expected behaviour if you linking with shared libraries. Linker will just assign it to your function, as it was first. It will also be correctly called from any other external libraries functions, - because linker will make your function exportable when it will scan linking libraries.

But - if you, say, have no external libraries that links to your function (so it isn't marked exportable, and isn't inserted to symbol table), and then dlopen() some library that want to use it during runtime - it will not find required function. Furthermore, if you first dlopen(RTLD_NOW|RTLD_GLOBAL) original library, every subsequent dlopen()'d library will use this library code, not yours. Your code (or any libraries that you've linked with during compilation phase, not runtime) will still stick with your function, no matter what.

keltar
  • 17,711
  • 2
  • 37
  • 42