I'm expanding a bit on one of my comments so the idea is properly formatted and described. This might not be doable in your scenario.
To simplify, we have libx1
which exposes two functions: libx_api
and libx1_api
. This version of the library is used by libx1_user
which exposes a single API: libx1_user
which uses both of the libx1
APIs.
We also have libx2
which exposes two functions: libx_api
and libx2_api
.
We have a program (prog
) that depends on libx1_user
and libx2
.
My suggestion is to wrap libx2
in a dynamic library libx2w
which exposes the same APIs as libx2
, only with a slight change in name: w_libx_api
and w_libx2_api
(only the functions that have common names between libx1
and libx2
need this change, but I added the w_
prefix to both functions for consistency).
Now, the code of prog
is changed so that every place in which a libx2
version of an API needs to be used it will actually use the wrappers for libx2w
. This should be a change that is easy to automate.
Example with code (I will omit the contents of the headers, there's nothing special there, just function prototypes):
libx1.c
#include <stdio.h>
#include "libx1.h"
void libx_api()
{
printf("In libx_api() from libx1\n");
}
void libx1_api()
{
printf("In libx1_api()\n");
}
libx2.c
#include <stdio.h>
#include "libx2.h"
void libx_api()
{
printf("In libx_api() from libx2\n");
}
void libx2_api()
{
printf("In libx2_api()\n");
}
libx1_user.c
#include <stdio.h>
#include "libx1_user.h"
#include "libx1.h"
void libx1_user()
{
printf("Entering libx1_user()\n");
libx_api();
libx1_api();
printf("Exiting libx1_user()\n");
}
libx2_wrapper.c
#include <stdio.h>
#include "libx2wrapper.h"
#include "libx2.h"
void w_libx_api()
{
libx_api();
}
void w_libx2_api()
{
libx2_api();
}
program.c
#include <stdio.h>
#include "libx1_user.h"
#include "libx2wrapper.h"
int main()
{
printf("Using libx1_user()\n");
libx1_user();
printf("Done!\n");
printf("Using libx2 wrappers\n");
w_libx_api(); // this was previously libx_api()
w_libx2_api(); // this was previously libx2_api()
printf("Done!\n");
return 0;
}
Sample CMakeLists.txt for compiling and linking
I'm more familiar with CMake than make so I found it easier to write the corresponding CMakeLists.txt instead of a Makefile.
project(prog)
add_library(x1 STATIC libx1.c)
add_library(x1_user STATIC libx1_user.c)
target_link_libraries(x1_user PRIVATE x1)
add_library(x2 STATIC libx2.c)
add_library(x2w SHARED libx2wrapper.c)
target_link_libraries(x2w PRIVATE x2)
add_executable(prog program.c)
target_link_libraries(prog PRIVATE x2w x1_user)
Running the resulting program
$ ./prog
Using libx1_user()
Entering libx1_user()
In libx_api() from libx1
In libx1_api()
Exiting libx1_user()
Done!
Using libx2 wrappers
In libx_api() from libx1
In libx2_api()
Done!
nm output
$ nm prog
0000000000003d98 d _DYNAMIC
0000000000003fa8 d _GLOBAL_OFFSET_TABLE_
0000000000002000 R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
000000000000224c r __FRAME_END__
0000000000002088 r __GNU_EH_FRAME_HDR
0000000000004010 d __TMC_END__
0000000000004010 B __bss_start
w __cxa_finalize@@GLIBC_2.2.5
0000000000004000 D __data_start
0000000000001140 t __do_global_dtors_aux
0000000000003d90 d __do_global_dtors_aux_fini_array_entry
0000000000004008 d __dso_handle
0000000000003d88 d __frame_dummy_init_array_entry
w __gmon_start__
0000000000003d90 d __init_array_end
0000000000003d88 d __init_array_start
00000000000012c0 T __libc_csu_fini
0000000000001250 T __libc_csu_init
U __libc_start_main@@GLIBC_2.2.5
0000000000004010 D _edata
0000000000004018 B _end
00000000000012c8 t _fini
0000000000001000 t _init
00000000000010a0 T _start
0000000000004010 b completed.8060
0000000000004000 W data_start
00000000000010d0 t deregister_tm_clones
0000000000001180 t frame_dummy
0000000000001234 T libx1_api
00000000000011e6 T libx1_user
000000000000121d T libx_api
0000000000001189 T main
U puts@@GLIBC_2.2.5
0000000000001100 t register_tm_clones
U w_libx2_api
U w_libx_api