Okay, so for those who are interested in the solution to the C++ dlopen problem. There are two “solutions”. I like neither of them but here you go:
- link the plugin with the
gold
linker and pass --weak-unresolved-symbols
. This will make everything work magically out-of-the-box at the cost of using a non-default linker. This solution comes from wayfire developer Scott Moreau. It can be done by putting in debian/rules
:export DEB_LDFLAGS_MAINT_APPEND = -fuse-ld=gold -Wl,--weak-unresolved-symbols
- the other option is to link
test.cc
with -rdynamic
. This was the flag I was missing. Without -rdynamic
it does not matter how many symbols I try to provide in test.cc
. The clue to provide this flag came from stackoverflow user Employed Russian. For those curios, here is a version of test.cc
that will just work if compiled with -rynamic
:
#include <wayfire/plugin.hpp>
#include <errno.h>
#include <dlfcn.h>
#include <memory>
#include <stdexcept>
#include <stdio.h>
#include <inttypes.h>
namespace nonstd {
template<class W> class observer_ptr {};
}
namespace wf {
class toplevel_t {};
class output_t {};
class pointf_t {};
class custom_data_t {};
class object_base_t {
virtual ~object_base_t() = default;
void erase_data(std::string);
void _fetch_data(std::string);
void _store_data(std::unique_ptr<custom_data_t>, std::string);
};
class toplevel_view_interface_t {
virtual ~toplevel_view_interface_t() = default;
void toplevel() const;
};
class view_interface_t {
virtual ~view_interface_t() = default;
void get_surface_root_node() const;
};
using wayfire_view = nonstd::observer_ptr<wf::view_interface_t>;
class view_matcher_t {
view_matcher_t(const std::string&);
~view_matcher_t();
void matches(wayfire_view);
};
class render_target_t {
void logic_scissor(wlr_box) const;
void get_orthographic_projection() const;
};
struct point_t {};
struct region_t {
region_t();
region_t(region_t const&);
region_t(region_t &&);
region_t(const wlr_box&);
region_t& operator=(const region_t&);
region_t& operator=(region_t&&);
region_t operator+(const point_t&) const;
region_t operator&(const region_t&) const;
region_t& operator|=(const wlr_box&);
void contains_point(const point_t&) const;
void contains_pointf(const pointf_t&) const;
void begin() const;
void end() const;
void clear();
void empty() const;
~region_t();
};
class compositor_core_t {
void get_all_views();
};
class wl_idle_call {
using callback_t = std::function<void ()>;
wl_idle_call();
~wl_idle_call();
void run_once(callback_t);
};
class output_workarea_manager_t {
void get_workarea();
};
class texture_t {
texture_t(unsigned int);
};
struct framebuffer_t {};
struct color_t {};
struct dimensions_t {};
using geometry_t = wlr_box;
geometry_t clamp(geometry_t, geometry_t);
void print_trace(bool);
void dimensions(wlr_box const&);
void get_core();
void find_view_for_toplevel(std::shared_ptr<wf::toplevel_t>);
void construct_box(wf::point_t const&, wf::dimensions_t const&);
template<bool A> class wl_timer {
using callback_t = std::function<std::conditional_t<A, bool, void>()>;
~wl_timer();
void is_connected();
void set_timeout(uint32_t timeout_ms, callback_t call);
};
template class wl_timer<true>;
template class wl_timer<false>;
namespace detail {
void option_wrapper_debug_message(const std::string&, const std::logic_error&);
void option_wrapper_debug_message(const std::string&, const std::runtime_error&);
};
namespace txn {
class transaction_object_t {
virtual ~transaction_object_t() = default;
};
using transaction_object_sptr = std::shared_ptr<transaction_object_t>;
class transaction_t {
void get_objects() const;
};
class transaction_manager_t {
void schedule_object(transaction_object_sptr);
};
};
namespace scene {
class node_t {
public:
node_t();
node_t(bool);
virtual ~node_t() = default;
void keyboard_refocus(wf::output_t*);
void to_local(wf::pointf_t const&);
void to_global(wf::pointf_t const&);
std::string stringify() const;
void optimize_update(unsigned int);
};
using node_ptr = std::shared_ptr<node_t>;
class floating_inner_node_t {
void set_children_list(std::vector<node_ptr>);
virtual ~floating_inner_node_t() = default;
};
void update(node_ptr changed_node, uint32_t flags);
};
namespace signal {
class connection_base_t {
void disconnect();
};
};
};
namespace glm {
enum qualifier {};
template<int A, typename B, qualifier C> class vec { virtual ~vec() = default; };
typedef vec<4, float, qualifier(0)> vec4;
template<int A, int B, typename C, qualifier D> class mat { virtual ~mat() = default; };
typedef mat<4, 4, float, qualifier(0)> mat4;
}
namespace OpenGL {
void render_rectangle(wf::geometry_t, wf::color_t, glm::mat4);
void render_texture(wf::texture_t, const wf::render_target_t&, const wf::geometry_t&, glm::vec4 color, uint32_t bits);
void render_begin(wf::framebuffer_t const&);
void render_begin();
void render_end();
};
wf::texture_t::texture_t(unsigned int) {};
void wf::construct_box(wf::point_t const&, wf::dimensions_t const&) {};
void wf::output_workarea_manager_t::get_workarea() {};
void wf::find_view_for_toplevel(std::shared_ptr<wf::toplevel_t>) {};
template<bool A> wf::wl_timer<A>::~wl_timer() {};
template<bool A> void wf::wl_timer<A>::is_connected() {};
template<bool A> void wf::wl_timer<A>::set_timeout(uint32_t, wf::wl_timer<A>::callback_t) {};
void wf::detail::option_wrapper_debug_message(const std::string&, const std::logic_error&) {};
void wf::detail::option_wrapper_debug_message(const std::string&, const std::runtime_error&) {};
void wf::get_core() {};
wf::wl_idle_call::wl_idle_call() {};
wf::wl_idle_call::~wl_idle_call() {};
void wf::wl_idle_call::run_once(wf::wl_idle_call::callback_t) {};
void operator+(wlr_box const&, wf::point_t const&) {};
void operator&(wlr_box const&, wf::point_t const&) {};
void wf::signal::connection_base_t::disconnect() {};
void wf::compositor_core_t::get_all_views() {};
void wlr_box_from_pixman_box(pixman_box32 const&) {};
wf::region_t::region_t() { };
wf::region_t::region_t(wf::region_t const&) { };
wf::region_t::region_t(wf::region_t &&) { };
wf::region_t::region_t(const wlr_box&) { };
wf::region_t& wf::region_t::operator=(const wf::region_t& v) { return *this; };
wf::region_t& wf::region_t::operator=(wf::region_t&& v) { return v; };
wf::region_t wf::region_t::operator+(const point_t& v) const { return wf::region_t(); };
wf::region_t wf::region_t::operator&(const region_t& v) const { return v; };
wf::region_t& wf::region_t::operator|=(const wlr_box& v) { return *this; };
wf::region_t::~region_t() { };
void wf::region_t::contains_point(const point_t& point) const {};
void wf::region_t::contains_pointf(const pointf_t& point) const {};
void wf::region_t::begin() const { };
void wf::region_t::end() const { };
void wf::region_t::clear() { };
void wf::region_t::empty() const { };
void OpenGL::render_texture(wf::texture_t, const wf::render_target_t&, const wf::geometry_t&, glm::vec4 color, uint32_t bits) {};
void OpenGL::render_rectangle(wlr_box, wf::color_t, glm::mat4) {};
void OpenGL::render_begin(wf::framebuffer_t const&) {};
void OpenGL::render_begin() {};
void OpenGL::render_end() {};
wf::geometry_t wf::clamp(wf::geometry_t, wf::geometry_t) { return wlr_box(); };
void wf::print_trace(bool) { };
void wf::dimensions(wlr_box const&) {};
void wf::render_target_t::logic_scissor(wlr_box) const {};
void wf::render_target_t::get_orthographic_projection() const {};
wf::view_matcher_t::view_matcher_t(const std::string&) {};
void wf::view_matcher_t::matches(wf::wayfire_view) {};
wf::view_matcher_t::~view_matcher_t() {};
void wf::plugin_interface_t::init() {};
void wf::plugin_interface_t::fini() {};
wf::scene::node_t::node_t() {};
wf::scene::node_t::node_t(bool) {};
void wf::scene::update(node_ptr changed_node, uint32_t flags) {};
void wf::scene::node_t::keyboard_refocus(wf::output_t*) {};
void wf::scene::node_t::to_local(wf::pointf_t const&) {};
void wf::scene::node_t::to_global(wf::pointf_t const&) {};
std::string wf::scene::node_t::stringify() const { return ""; };
void wf::scene::node_t::optimize_update(unsigned int) { };
void wf::view_interface_t::get_surface_root_node() const {};
void wf::txn::transaction_t::get_objects() const {};
void wf::txn::transaction_manager_t::schedule_object(wf::txn::transaction_object_sptr) {};
void gl_call(const char*, uint32_t, const char*) {};
void wf::toplevel_view_interface_t::toplevel() const {};
void wf::scene::floating_inner_node_t::set_children_list(std::vector<node_ptr>) {};
void wf::object_base_t::erase_data(std::string) {};
void wf::object_base_t::_fetch_data(std::string) {};
void wf::object_base_t::_store_data(std::unique_ptr<custom_data_t>, std::string) {};
// Create one instance to make sure the vtable is not optimized out.
static wf::object_base_t *ptr1 = new wf::object_base_t();
static wf::txn::transaction_object_t *ptr = new wf::txn::transaction_object_t();
static wf::scene::node_t *ptr2 = new wf::scene::node_t();
static wf::toplevel_view_interface_t *ptr3 = new wf::toplevel_view_interface_t();
static wf::scene::floating_inner_node_t *ptr4 = new wf::scene::floating_inner_node_t();
static wf::view_interface_t *ptr5 = new wf::view_interface_t();
int main(int argc, char* argv[]) {
void *handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL);
if (handle == NULL) {
fprintf(stderr, "dlopen failed: %s\n", dlerror());
return 1;
}
uint32_t (*version_func)() = (uint32_t (*)())dlsym(handle, "getWayfireVersion");
if (version_func == NULL) {
perror("dlsym failed");
dlclose(handle);
return 1;
}
int32_t plugin_abi_version = version_func();
dlclose(handle);
fprintf(stdout, "wayfire is: %" PRIu32 "\n", WAYFIRE_API_ABI_VERSION);
fprintf(stdout, "plugin is: %" PRIu32 "\n", plugin_abi_version);
if (WAYFIRE_API_ABI_VERSION != plugin_abi_version) {
fprintf(stderr, "API/ABI version mismatch:\n");
return 1;
}
return 0;
}