How can I link a program to two libraries with the same function name but different implementations?

Viewed 330

I'm writing a program using libraries foo1.a and foo2.a.

Inside foo2.a, it uses foo3.a, which implements a function funcfoo. But foo1.a contains the same function that implements its own funcfoo. My main program wants to use funcfoo from foo1.a, and some other functions from foo2.a, while also makes sure that foo2.a only uses funcfoo from foo3.a.

Is there anyway I can enforce this to happen?

2 Answers

Short answer : you can't

Long answer : you still can't because name (like function name as well as global variable or enum name ... any name) have to be unique across your binary[1]

but

there is maybe action you can do.

  1. If you own the source code of any library involved in this mess, make a complete new library with the same stuff but renamed.

Basically, i say "make a new major version of you library, since you will change the function name present in the library, thus breaking the retro-compatibility". If you library is "foo1.a" containing the function "min", then make a "foo2.a" with "foo2_min" function. It's usually a good pratice to prefix/suffix your internal function (be it in a library or directly in your binary) with something. Like if your company name is "My Little Pony", "MLP_" sound a good prefix (do a google search, just in case). That's what user694733 said in the comment.

If you're using an IDE, then it should be easy and quick to do so with the "rename refactoring" feature. I advise to do the most renaming possible in order to avoid further scenario like the one your stuck with now.

  1. You don't have the source code, but the library licence allow you to modify it.

If it's format is open (like a good old so), you can use objcopy. I read it can do that, but I never do it myself so .... good luck

If it's a close format, either you have the documentation about it and you have to do a lot of work, or you're completly stuck.

  1. A third party own the code source, and/or the library licence don't allow you to modify it

If it's a third party library, maybe your company has subscribed to his support, so you better contact them directly.

.

As far as I know, there is no possibility to "encapsulate" a library into a "spacename" nativly in C.

I hope this answer an help, and I hope it's accurate and complete.

[1] Well, this is not completly rigth as you can create a local variable with a global variable's name, and this will compile and run. In the local context, it's the local variable that will be used. gcc can warn about this kind of scenario with -Wshadow option. But unless you're doing some shady hack, this situation is usually something that you want to avoid.

Most linkers obey the order of the libraries as you provide them on the command line. If you link your program first with "foo1.a", it will resolve the references of funcfoo in your program with the implemention of "foo1.a". Place "foo2.a" second, which will leave an open reference to funcfoo. With "foo3.a" placed third, this will be resolved with the second implementation.

EDIT 1:

I'm afraid that I was wrong. A quick check (just before going to sleep) revealed that when "foo2.a" is linked, the reference to funcfoo is resolved with the implementation of the already loaded "foo1.a". :-(

I will do some more research, but please don't hold your breath.

EDIT 2:

OK, it took some time, but with the help of "objcopy" is works as proposed. You can use its option --redefine-sym old=new to "rename" symbols even in a library.

I have prepared a small example to follow the steps as a prove of concept. In the real project, the libraries are already built, so just the last commands of the shown list are needed.

Let's start with the main source:

#include "foo1.h"
#include "foo2.h"

int main(void) {
  funcfoo();
  funcbar();
  return 0;
}

It includes the following header files, first "foo1.h" and second "foo2.h":

#ifndef FOO1_H
#define FOO1_H
void funcfoo(void);
#endif
#ifndef FOO2_H
#define FOO2_H
void funcbar(void);
#endif

Of course there are implementations of both, again first "foo1.c" and second "foo2.c":

#include <stdio.h>

#include "foo1.h"

void funcfoo(void) {
  puts("funcfoo() in foo1");
}
#include "foo2.h"
#include "foo3.h"

void funcbar(void) {
  funcfoo();
}

The third library "foo3" implements the same function as "foo1", first the header file and then the implementation file:

#ifndef FOO3_H
#define FOO3_H
void funcfoo(void);
#endif
#include <stdio.h>

#include "foo3.h"

void funcfoo(void) {
  puts("funcfoo() in foo3");
}

These are the commands to build the application:

gcc -c -Wall -Wextra foo1.c -o foo1.o
ar cr libfoo1.a foo1.o

gcc -c -Wall -Wextra foo2.c -o foo2.o
ar cr libfoo2.a foo2.o

gcc -c -Wall -Wextra foo3.c -o foo3.o
ar cr libfoo3.a foo3.o

# In the real prject, only the following steps are needed:
gcc -c -Wall -Wextra main.c -o main.o
objcopy --redefine-sym funcfoo=funcfoo2 libfoo2.a libfoo2n.a
objcopy --redefine-sym funcfoo=funcfoo2 libfoo3.a libfoo3n.a
gcc main.o -L. -lfoo1 -lfoo2n -lfoo3n -o app

As you can see, the tool "objcopy" of the common "binutils" is used to redefine the name of the conflicting function from funcfoo to funcfoo2. I also let it create a new output library to save the original.

And when run, the application prints:

funcfoo() in foo1
funcfoo() in foo3
Related