pnoltes commented on PR #441:
URL: https://github.com/apache/celix/pull/441#issuecomment-1241134232

   > > In practice this means that every bundle activator library (and ideally 
every private bundle library) should have a unique SONAME and although in 
theory this is doable ("just configure the SONAME"), I think that in many cases 
this adds too much complexity.
   > 
   > Bundle activator library can have the same SONAME, provided that we 
install them into different location of the global cache, and provide the full 
path to `dlopen`. `man 2 dlopen` says:
   > 
   > > If filename contains a slash ("/"), then it is interpreted as a 
(relative or absolute) pathname.  Otherwise, the dynamic linker searches for 
the object as follows (LD_LIBRARY_PATH thing)
   > 
   > I worked out a minimal example and tested it on my Ubuntu machine:
   > 
   > ```cmake
   >    # CMakeLists.txt
   >    cmake_minimum_required(VERSION 3.23)
   >    project(hello_solib C)
   >    
   >    set(CMAKE_C_STANDARD 99)
   >    
   >    
   >    add_library(hello_impl1 SHARED hello1.c)
   >    set_target_properties(hello_impl1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY 
${CMAKE_CURRENT_BINARY_DIR}/impl1)
   >    set_target_properties(hello_impl1 PROPERTIES OUTPUT_NAME hello)
   >    add_library(hello_impl2 SHARED hello2.c)
   >    set_target_properties(hello_impl2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY 
${CMAKE_CURRENT_BINARY_DIR}/impl2)
   >    set_target_properties(hello_impl2 PROPERTIES OUTPUT_NAME hello)
   >    
   >    add_executable(hello_solib main.c)
   >    target_compile_definitions(hello_solib PRIVATE 
HELLO1_LOC="$<TARGET_[FILE:hello_impl1](file:///hello_impl1)>")
   >    target_compile_definitions(hello_solib PRIVATE 
HELLO2_LOC="$<TARGET_[FILE:hello_impl2](file:///hello_impl2)>")
   >    target_link_libraries(hello_solib PRIVATE dl)
   >    
   > ```
   > 
   >  ```c
   > //main.c
   > #include "hello.h"
   > #include <assert.h>
   > #include <dlfcn.h>
   > #include <stdio.h>
   > 
   > int main() {
   >     int (*funcp1)(void);
   >     int (*funcp2)(void);
   >     void *handle1;
   >     void *handle2;
   >     printf("impl1=%s\n", HELLO1_LOC);
   >     handle1 = dlopen(HELLO1_LOC, RTLD_LAZY);
   >     assert(handle1 != NULL);
   >     printf("impl2=%s\n", HELLO2_LOC);
   >     handle2 = dlopen(HELLO2_LOC, RTLD_LAZY);
   >     assert(handle2 != NULL);
   >     *(void **)(&funcp2) = dlsym(handle2, "hello");
   >     funcp2();
   >     *(void **)(&funcp1) = dlsym(handle1, "hello");
   >     funcp1();
   >     dlclose(handle2);
   >     dlclose(handle1);
   >     return 0;
   > }
   > ```
   > 
   >  ```c
   > //hello.h
   > #ifndef HELLO_SOLIB_HELLO_H
   > #define HELLO_SOLIB_HELLO_H
   > #ifdef __cplusplus
   > extern "C" {
   > #endif
   > 
   > int hello(void);
   > 
   > #ifdef __cplusplus
   > }
   > #endif
   > #endif //HELLO_SOLIB_HELLO_H
   > ```
   > 
   >  ```c
   > //hello1.c
   > #include "hello.h"
   > #include <stdio.h>
   > 
   > int hello(void) {
   >     printf("hello1\n");
   > }
   > ```
   > 
   >  ```c
   > //hello2.c
   > #include "hello.h"
   > #include <stdio.h>
   > 
   > int hello(void) {
   >     printf("hello2\n");
   > }
   > ```
   > 
   > Running the binary produces the following console output:
   > 
   > ```
   > /home/peng/Downloads/hello_solib/cmake-build-debug/hello_solib
   > impl1=/home/peng/Downloads/hello_solib/cmake-build-debug/impl1/libhello.so
   > impl2=/home/peng/Downloads/hello_solib/cmake-build-debug/impl2/libhello.so
   > hello2
   > hello1
   > 
   > Process finished with exit code 0
   > ```
   
   Nice, this is new for (even though it is documenten in the man dlopen ..). I 
also gonna try this on a Mac and then think about how we can use this.
   
   > 
   >  > The challenge I see here is the same as with importing/exporting 
libraries from bundles: If there are libraries which are different, but have 
the same SONAME header, a call to dlopen will reuse a (transitive) library with 
the same SONAME instead of loading a new lib. This is even true if DL_LOCAL is 
used.
   > 
   > At first, I thought the importing/exporting problem touches the intrinsic 
difference between JAVA and C++, and thus is impossible to solve. It turns out 
that I was wrong.
   > 
   > Instead of `dlopen/dlmopen`, we should really manipulate `ld.so`. 
According to `man ld.so`:
   > 
   > > When  resolving  shared  object  dependencies,  the  dynamic linker 
first inspects each dependency string to see if it contains a slash (this can 
occur if a shared object pathname containing slashes was specified at link 
time).
   > 
   > IIRC, shared object's dependency is encoded in the NEEDED entry in ELF 
dynamic section:
   > 
   > ```
   > peng@hackerlife:~/Downloads/hello_solib/cmake-build-debug/impl1$ readelf 
-d libhello.so
   > 
   > Dynamic section at offset 0x2e10 contains 25 entries:
   >   Tag        Type                         Name/Value
   >  0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
   >  0x000000000000000e (SONAME)             Library soname: [libhello.so]
   > ```
   > 
   > If we could modify NEEDED entries of installed shared object at runtime, 
use relative/absolute path instead, `ld.so` will be guided to load whatever we 
want. And it turns out that we can do that!
   > 
   > https://github.com/NixOS/patchelf
   > 
   > ```
   > $ patchelf --remove-needed libhello.so.1 hello
   > $ patchelf --add-needed ./libhello.so.1 hello
   > ```
   > 
   > What we need to do is to implement `patchelf` inside the Celix framework. 
Then IMPORT/EXPORT in the manifest become the definitive source of shared 
object interdependence. The framework does the hard work of wiring shared 
objects together at appropriate stage (e.g. when bundle get resolved?).
   > 
   > If we do implement this, I bet every C/C++ programmer will be shocked.
   > 
   > Currently SONAME + strict semantic version scheme should work properly in 
most corporate working environment, though it may be difficult to enforce in 
the open source world. Besides, conan makes integration test against various 
version of dependencies relatively easier. I suggest we finish the cache work 
first.
   > 
   > WDYT?
   
   Nice, yes this can work. Around 2011-2013 we did some experiment with 
updating the NEEDED and SONAME flags and in that experiment it did work. But 
updating the NEEDED/SONAME entry is difficult of the string size needs to 
increased, so for the experiment we only updated NEEDED to something small 
(libfoo->ab1, libbar->ab2, etc). 
   If I remember correctly we did not follow up this approach, because we had 
bigger things to tackle. But I agree if we can tackle this we are creating 
something "magical" for the C/C++ word :).
   
   This should indeed be done in the resolving of a bundle and ideally be done 
by a resolver. There is a resolver in Celix, but that is outdated and not 
something I have 
touched(https://github.com/apache/celix/blame/master/libs/framework/src/resolver.c).
 But If we refactor this, it should be able to work out which libs are EXPORTED 
and IMPORTED and define a wire (e.g. how the NEEDED (and maybe SONAME) should 
be updated) between the export and import libs. 
   
   Also note that patchelf is GPL and if I am correct not useable for a ASF 
project. 
   
   
   
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscr...@celix.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to