I have multiple pre-compiled dynamic libraries that use the same `extern "C"` methods names. The methods can behave differently for each dynamic libraries. Ultimately these methods will be accessed in a SystemVerilog simulation via DPI (Direct Program Interface). When trying to link all the libraries to my simulator, I noticed visibility of the methods is depended on link order. This blocks me from accessing the desired method of the same name if it is not the first libraries. One solution would have been to make each extern C method to have a unique name. But this fails in my case for two reasons: 1) I do not own the sources code of the dynamic libraries. I can make requests, but there is no guarantee if or when it will be fulfill. 2) It would add a lot of verbosity and static code to my SystemVerilog code as each component would need to figure out which DPI method it needs to access. It also doesn't scale well if a libraries are added or removed. My solution is to create my own dynamic library that uses `dlopen()` and `dlsym()` (from `dlfcn.h`) to dynamically access methods from the pre-compiled dynamic libraries. Fortunately, all the pre-compiled libraries flavors use the same root class. They also contain a publicly accessible variable identifying the compiled flavor. I can use this identifier to decide which library to dynamically reference. I cannot share the real code. Bellow is a runable proof of concept. I am getting the desired output. It has been a few years since I have done this kind of codding with C++, so I'm hoping I not missing something. my_model.h ```lang-cpp #include <stdio.h> #include <iostream> #include <unordered_map> #define CONCAT_(A, B) A ## B #define CONCAT(A, B) CONCAT_(A, B) #define EXPORTED __attribute__((visibility("default"))) #ifndef FLAVOR #define FLAVOR PASSION #endif namespace my_ns{ enum Flavor { PASSION=0, ORANGE=1, GUAVA=2, POG=-1 }; class my_model { public: my_model(int cfg); virtual ~my_model() { close(); } virtual void close() { um.clear(); } virtual int get_info(std::string key, int* value) { return -1; } virtual int set_info(std::string key, int value) { return -1; } virtual int del_info(std::string key) { return -1; } const Flavor flavor = FLAVOR; protected: std::unordered_map<std::string,int> um; }; #if FLAVOR>=0 class CONCAT(my_model_,FLAVOR) : public my_model { public: CONCAT(my_model_,FLAVOR)(int cfg) : my_model(cfg) {} virtual int get_info(std::string key, int* value); virtual int set_info(std::string key, int value); virtual int del_info(std::string key); }; #endif extern "C" { int EXPORTED create_model(void** model_h, int flavor, int cfg); int EXPORTED delete_model(void** model_h); int EXPORTED get_info(const void* model_h, const char* key, int* value); int EXPORTED set_info(const void* model_h, const char* key, int value); int EXPORTED del_info(const void* model_h, const char* key); } } ``` my_model.cpp ```lang-cpp #include "my_model.h" namespace my_ns { my_model::my_model(int cfg) { um["cfg"] = cfg; um["flavor"] = static_cast<int>(FLAVOR); printf("Info: cfg:%0d FLAVOR:%0d (%s:%s:%d)\n", um["cfg"],FLAVOR,__func__,__FILE__,__LINE__); } int CONCAT(my_model_,FLAVOR)::get_info(std::string key, int* value) { printf("Info: %s(%s,%%d) : %s:%d\n", __func__,key.c_str(), __FILE__,__LINE__); if (um.count(key)==0) return -1; *value = um[key] + static_cast<int>(FLAVOR); return 0; } int CONCAT(my_model_,FLAVOR)::set_info(std::string key, int value) { printf("Info: %s(%s,%0d) : %s:%d\n", __func__,key.c_str(),value, __FILE__,__LINE__); um[key] = value * um["cfg"] * um["cfg"]; return 0; } int CONCAT(my_model_,FLAVOR)::del_info(std::string key) { printf("Info: %s(%s):%s:%d)\n", __func__,key.c_str(), __FILE__,__LINE__); if (um.count(key)==0) return -1; um.erase(key); return 0; } int create_model(void** model_h, int flavor, int cfg) { printf("Info: cfg:%0d FLAVOR:%d (%s:%s:%d)\n", cfg, FLAVOR, __func__,__FILE__,__LINE__); *model_h = (void*) new CONCAT(my_model_,FLAVOR)(cfg); return (!*model_h ? -1 : 0); } int delete_model(void** model_h) { CONCAT(my_model_,FLAVOR)* _model = (CONCAT(my_model_,FLAVOR)*) *model_h; if (!_model) { fprintf(stderr, "Error: NULL model_h"); return -1; } delete _model; *model_h = (void*) NULL; return 0; } int get_info(const void* model_h, const char* key, int* value) { CONCAT(my_model_,FLAVOR)* _model = (CONCAT(my_model_,FLAVOR)*) model_h; if (!_model) { fprintf(stderr, "Error: NULL model_h"); return -1; } return _model->get_info(std::string(key), value); } int set_info(const void* model_h, const char* key, int value) { CONCAT(my_model_,FLAVOR)* _model = (CONCAT(my_model_,FLAVOR)*) model_h; if (!_model) { fprintf(stderr, "Error: NULL model_h"); return -1; } return _model->set_info(std::string(key), value); } int del_info(const void* model_h, const char* key) { CONCAT(my_model_,FLAVOR)* _model = (CONCAT(my_model_,FLAVOR)*) model_h; if (!_model) { fprintf(stderr, "Error: NULL model_h"); return -1; } return _model->del_info(std::string(key)); } } ``` my_top.cpp ```lang-cpp #include "my_model.h" #include <dlfcn.h> namespace my_ns { my_model::my_model(int cfg) {} const char lib[3][16] = {"./libPassion.so","./libOrange.so","./libGuava.so"}; void *my_so[3]; int link_so(Flavor flavor) { if (my_so[flavor] == NULL) { printf("linking %s (%s:%s:%d)\n", lib[flavor], __func__,__FILE__,__LINE__); my_so[flavor] = dlopen(lib[flavor], RTLD_NOW); } if (!my_so[flavor]) { /* fail to load the library */ fprintf(stderr, "Error: %s\n", dlerror()); return -1; } return 0; } int create_model(void** model_h, int flavor, int cfg) { link_so(static_cast<Flavor>(flavor)); int (*dlsym_create_model)(void** model_h, int flavor, int cfg); *(void**)(&dlsym_create_model) = dlsym(my_so[flavor], "create_model"); return dlsym_create_model(model_h, flavor, cfg); } int delete_model(void** model_h) { my_model* _model = (my_model*) model_h; link_so(_model->flavor); int (*dlsym_delete_model)(void** model_h); *(void**)(&dlsym_delete_model) = dlsym(my_so[_model->flavor], "delete_model"); return dlsym_delete_model(model_h); } int get_info(const void* model_h, const char* key, int* value) { my_model* _model = (my_model*) model_h; link_so(_model->flavor); int (*dlsym_get_info)(const void* model_h, const char* key, int* value); *(void**)(&dlsym_get_info) = dlsym(my_so[_model->flavor], "get_info"); return dlsym_get_info(model_h, key, value); } int set_info(const void* model_h, const char* key, int value) { my_model* _model = (my_model*) model_h; link_so(_model->flavor); int (*dlsym_set_info)(const void* model_h, const char* key, int value); *(void**)(&dlsym_set_info) = dlsym(my_so[_model->flavor], "set_info"); return dlsym_set_info(model_h, key, value); } int del_info(const void* model_h, const char* key) { my_model* _model = (my_model*) model_h; link_so(_model->flavor); int (*dlsym_del_info)(const void* model_h, const char* key); *(void**)(&dlsym_del_info) = dlsym(my_so[_model->flavor], "del_info"); return dlsym_del_info(model_h, key); } } ``` my_dpi.sv ```lang-systemverilog package my_dpi_pkg; import "DPI-C" function int create_model( output chandle handle, input int flavor, cfg ); import "DPI-C" function int delete_model( inout chandle handle ); import "DPI-C" function int get_info( input chandle handle, input string key, output int value ); import "DPI-C" function int set_info( input chandle handle, input string key, input int value ); import "DPI-C" function int del_info( input chandle handle, input string key ); endpackage : my_dpi_pkg module tb; import my_dpi_pkg::*; initial begin chandle passion_h,orange_h, guava_h; string str; int flavor,val; $display("Create"); assert(create_model(passion_h, 0, 10)==0); assert(create_model( orange_h, 1, 16)==0); assert(create_model( guava_h, 2, 8)==0); $display("\nInfo via set_info()"); assert(set_info(passion_h, "alpha", 'd13)==0); assert(set_info( orange_h, "beta", 'h13)==0); assert(set_info( guava_h, "gamma", 'o13)==0); $display("\nInfo via get_info()"); assert(get_info(passion_h,"flavor", flavor)==0); assert(get_info(passion_h,"alpha", val)==0); $display("passion_h flavor:%0d, alpha:'d%0d",flavor,val); assert(get_info(orange_h,"flavor", flavor)==0); assert(get_info(orange_h,"beta", val)==0); $display("orange_h flavor:%0d, beta:'h%0h",flavor,val); assert(get_info(guava_h,"flavor", flavor)==0); assert(get_info(guava_h,"gamma", val)==0); $display("guava_h flavor:%0d, gamma:'o%0o",flavor,val); assert(get_info(guava_h,"cfg", flavor)==0); val = 0; assert(get_info(guava_h,"alpha", val)==-1); $display("guava_h cfg:%0d, alpha:'d%0d",flavor,val); $display("Delete"); assert(delete_model(passion_h)==0); assert(delete_model(orange_h)==0); assert(delete_model(guava_h)==0); $finish(0); end endmodule ``` Commands to build and run: ```lang-bash g++ -fvisibility=hidden -fvisibility-inlines-hidden -s -shared -fPIC -std=gnu++11 -DFLAVOR=PASSION -o libPassion.so my_model.h my_model.cpp -Wall -g || exit 1 g++ -fvisibility=hidden -fvisibility-inlines-hidden -s -shared -fPIC -std=gnu++11 -DFLAVOR=ORANGE -o libOrange.so my_model.h my_model.cpp -Wall -g || exit 1 g++ -fvisibility=hidden -fvisibility-inlines-hidden -s -shared -fPIC -std=gnu++11 -DFLAVOR=GUAVA -o libGuava.so my_model.h my_model.cpp -Wall -g || exit 1 g++ -fvisibility=hidden -fvisibility-inlines-hidden -s -shared -fPIC -std=gnu++11 -DFLAVOR=POG -o libPOG.so my_model.h my_top.cpp -Wall -g <sv_simulator> -<dpi_keyword> libPOG.so my_dpi.sv ``` Output: ```lang-none Create linking ./libPassion.so (link_so:my_top.cpp:11) Info: cfg:10 FLAVOR:0 (create_model:my_model.cpp:29) Info: cfg:10 FLAVOR:0 (my_model:my_model.cpp:7) linking ./libOrange.so (link_so:my_top.cpp:11) Info: cfg:16 FLAVOR:1 (create_model:my_model.cpp:29) Info: cfg:16 FLAVOR:1 (my_model:my_model.cpp:7) linking ./libGuava.so (link_so:my_top.cpp:11) Info: cfg:8 FLAVOR:2 (create_model:my_model.cpp:29) Info: cfg:8 FLAVOR:2 (my_model:my_model.cpp:7) Info via set_info() Info: set_info(alpha,13) : my_model.cpp:17 Info: set_info(beta,19) : my_model.cpp:17 Info: set_info(gamma,11) : my_model.cpp:17 Info via get_info() Info: get_info(flavor,%d) : my_model.cpp:11 Info: get_info(alpha,%d) : my_model.cpp:11 passion_h flavor:0, alpha:'d1300 Info: get_info(flavor,%d) : my_model.cpp:11 Info: get_info(beta,%d) : my_model.cpp:11 orange_h flavor:2, beta:'h1301 Info: get_info(flavor,%d) : my_model.cpp:11 Info: get_info(gamma,%d) : my_model.cpp:11 guava_h flavor:4, gamma:'o1302 Info: get_info(cfg,%d) : my_model.cpp:11 Info: get_info(alpha,%d) : my_model.cpp:11 guava_h cfg:10, alpha:'d0 Delete ./my_dpi.sv:42 $finish(0); ```