To avoid an A/B question: ultimately what I want to do is to find out whether a plugin binary for wayfire that I have (firedecor in my case) has been compiled with a new enough version of wayfire such that the API/ABI version supported by the plugin (as can be retrieved with its exported getWayfireVersion
) matches the version of today's wayfire or if the plugin has to be recompiled against the new wayfire version.
To find out the wayfire api/abi version against which my plugin shared library was compiled so that I can ultimately perform the desired comparison, I write a short C++ program. It dlopen
s the plugin shared library and attempts calling getWayfireVersion()
and compares its output against the value of WAYFIRE_API_ABI_VERSION
as retrieved by the current version of the included wayfire headers:
#include <wayfire/plugin.hpp>
#include <errno.h>
#include <dlfcn.h>
#include <stdio.h>
#include <inttypes.h>
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();
if (WAYFIRE_API_ABI_VERSION != plugin_abi_version) {
fprintf(stderr, "API/ABI version mismatch:\n");
fprintf(stderr, "wayfire is: %" PRIu32 "\n", WAYFIRE_API_ABI_VERSION);
fprintf(stderr, "plugin is: %" PRIu32 "\n", plugin_abi_version);
dlclose(handle);
return 1;
}
fprintf(stderr, "wayfire is: %" PRIu32 "\n", WAYFIRE_API_ABI_VERSION);
fprintf(stderr, "plugin is: %" PRIu32 "\n", plugin_abi_version);
dlclose(handle);
return 0;
}
I compile it:
$ g++ test.cc -o test -Wall -ldl $(pkg-config --cflags pixman-1) -DWLR_USE_UNSTABLE
But when I run it I get:
$ ./test /usr/lib/aarch64-linux-gnu/wayfire/libfiredecor.so
dlopen failed: /usr/lib/aarch64-linux-gnu/wayfire/libfiredecor.so: undefined symbol: _ZTIN2wf3txn20transaction_object_tE
Looking at libfiredecor.so
with nm
indeed reveals, that _ZTIN2wf3txn20transaction_object_tE
is undefined:
0000000000045b70 V _ZTIN2wf28view_geometry_changed_signalE
00000000000457a8 V _ZTIN2wf36view_decoration_state_updated_signalE
U _ZTIN2wf3txn20transaction_object_tE
00000000000457b8 V _ZTIN2wf3txn22new_transaction_signalE
0000000000045988 V _ZTIN2wf5scene17render_instance_tE
But since it's a plugin, that's okay because it's supposed to obtain the symbol from the executable loading it. Now my question: how do I modify my test.cc
or my g++
invocation such that the test
binary includes the typeinfo for wf::txn::transaction_object_t
?
Using dummy values should be enough because the getWayfireVersion
in my plugin is defined like this:
uint32_t getWayfireVersion() { return WAYFIRE_API_ABI_VERSION; }
Changing RTLD_NOW
to RTLD_LAZY
in dlopen
only has the effect that the error message does not show up when doing dlopen
but only later when calling the function.
I contacted the wayfire developers for their input but they are none the wiser: https://github.com/WayfireWM/wayfire/issues/2499
An alternative solution to this problem would be to hard-code the wayfire api/abi version when building the plugin and store the results in a file and query that file later and compare it with the current value of WAYFIRE_API_ABI_VERSION
. But since that solution is not quite optimal and since this has turned into a c++ question, I'm asking for help here. Maybe somebody knows how to make my test.cc
contain the (dummy) symbols expected by my plugin, thank you!
EDIT 1:
You can obtain your own version of libfiredecor.so by downloading and unpacking the Debian package like this:
$ curl http://ftp.debian.org/debian/pool/main/r/reform-firedecor/reform-firedecor_2023-10-23-5_amd64.deb | dpkg-deb --fsys-tarfile - | tar --to-stdout -x ./usr/lib/x86_64-linux-gnu/wayfire/libfiredecor.so > libfiredecor.so
EDIT 2:
The test.cc
program works successfully when compiling the plugin with gold
's --weak-unresolved-symbols
option. Is this the correct solution?
how do I modify my
test.cc
or myg++
invocation such that thetest
binary includes the typeinfo forwf::txn::transaction_object_t
You would add a definition of such class to your test.cc
. Something like:
namespace wf::txn {
class transaction_object_t {
virtual ~transaction_object_t() = default;
};
// Create one instance to make sure the vtable is not optimized out.
static transaction_object_t *ptr = new transaction_object_t();
}
In addition, you must link your test
with -rdynamic
flag.
You would have to do that for all undefined symbols in the plugin. The set may change between versions, so this doesn't sound like a viable long-term approach.
OTOH, if dlopen
fails due to unresolved symbol, than that's an indication that you have to rebuild the plugin anyway, so maybe that's good enough.