diff --git a/src/ndb/NDBMetadata.cc b/src/ndb/NDBMetadata.cc index fe10bc7..493a0d6 100644 --- a/src/ndb/NDBMetadata.cc +++ b/src/ndb/NDBMetadata.cc @@ -5,15 +5,44 @@ #include "util.h" #include "NDBMetadata.h" -/* Volume/Content objects appear to be only 8 bytes. They - hold a pointer to a much larger ContentPrivate/VolumePrivate - object (VolumePrivate is at least 400 bytes). - Setting 16 bytes just to be safe here. */ -#define VOLUME_SIZE 16 - #define NDB_RESOLVE_ATTR(attr, required) if (!resolve_attr(attr, "ATTRIBUTE_" #attr, required)) return namespace NDB { +void (*Volume__Volume)(Volume* _this); + +NDBVolume::NDBVolume() { + initResult = SymbolError; + resolveSymbolRTLD("_ZTV6Volume", nh_symoutptr(Volume_vtable)); + NDB_ASSERT_RET(Volume_vtable, "Volume vtable not found"); + resolveSymbolRTLD("_ZTV7Content", nh_symoutptr(Content_vtable)); + NDB_ASSERT_RET(Content_vtable, "Content vtable not found"); + initResult = InitError; + Volume__Volume((Volume*)&vol); + NDB_ASSERT_RET(vol.vptr != nullptr && vol.vol_private != nullptr, "Volume not constructed correctly"); + initResult = Ok; + return; +} + +/* Volume contains a large heap allocated VolumePrivate member variable that needs to be cleaned up + Thankfully it has a virtual destructor. */ +NDBVolume::~NDBVolume() { + // Don't try and call the destructor if we never got a valid object + NDB_ASSERT_RET(initResult == Ok, "Volume was not initialised."); + NDB_ASSERT_RET(vol.vptr != nullptr && vol.vol_private != nullptr, "vptr or VolumePrivate not set on Volume."); + + // Shamelessly stolen from NickelMenu + #define vtable_target(x) reinterpret_cast(reinterpret_cast(x)+8) + nh_log("Value of Content vtable: %p", vtable_target(Content_vtable)); + nh_log(" Value of Volume vtable: %p", vtable_target(Volume_vtable)); + nh_log(" Value of current vptr: %p", vol.vptr); + NDB_ASSERT_RET(vol.vptr == vtable_target(Volume_vtable), "unexpected vtable layout (expected class to start with a pointer to 8 bytes into the vtable)"); + + vol.vptr->volume_dstor(&vol); + nh_log("Virtual Volume Destructor invoked."); + return; +} + + NDBMetadata::~NDBMetadata() {} @@ -52,17 +81,17 @@ NDBMetadata::NDBMetadata(QObject* parent) : QObject(parent) { nh_log("DB name is %s", dbName->toUtf8().constData()); resolveSymbolRTLD("_ZN13VolumeManager7getByIdERK7QStringS2_", nh_symoutptr(symbols.VolumeManager__getById)); - resolveSymbolRTLD("_ZN6VolumeC1Ev", nh_symoutptr(symbols.Volume__Volume)); + resolveSymbolRTLD("_ZN13VolumeManager7forEachERK7QStringRKSt8functionIFvRK6VolumeEE", nh_symoutptr(symbols.VolumeManager__forEach)); + resolveSymbolRTLD("_ZN6VolumeC1Ev", nh_symoutptr(Volume__Volume)); resolveSymbolRTLD("_ZNK6Volume7isValidEv", nh_symoutptr(symbols.Volume__isValid)); resolveSymbolRTLD("_ZNK6Volume11getDbValuesEv", nh_symoutptr(symbols.Volume__getDbValues)); - resolveSymbolRTLD("_ZN13VolumeManager7forEachERK7QStringRKSt8functionIFvRK6VolumeEE", nh_symoutptr(symbols.Volume__forEach)); resolveSymbolRTLD("_ZN6Volume12setAttributeERK7QStringRK8QVariant", nh_symoutptr(symbols.Volume__setAttribute)); resolveSymbolRTLD("_ZN6Volume4saveERK6Device", nh_symoutptr(symbols.Volume__save)); if (!symbols.VolumeManager__getById || - !symbols.Volume__Volume || + !symbols.VolumeManager__forEach || + !Volume__Volume || !symbols.Volume__getDbValues || !symbols.Volume__isValid || - !symbols.Volume__forEach || !symbols.Volume__setAttribute || !symbols.Volume__save) { initResult = SymbolError; @@ -106,9 +135,9 @@ Volume* NDBMetadata::getByID(Volume* vol, QString const& id) { } QVariantMap NDBMetadata::getMetadata(QString const& cID) { - uint8_t va[VOLUME_SIZE] = {0}; - symbols.Volume__Volume(va); - Volume* v = getByID((Volume*)va, cID); + NDBVolume vol; + NDB_ASSERT(QVariantMap(), vol.initResult == Ok, "Unable to construct Volume"); + Volume* v = getByID(vol.getVolume(), cID); return getMetadata(v); } @@ -123,7 +152,7 @@ QVariantMap NDBMetadata::getMetadata(Volume* v) { QStringList NDBMetadata::getBookList(std::function filter) { NDB_DEBUG("calling Volume::forEach()"); QStringList bookList = {}; - symbols.Volume__forEach(*dbName, [&](Volume *v) { + symbols.VolumeManager__forEach(*dbName, [&](Volume *v) { if (volIsValid(v) && filter(v)) { QVariantMap values = getMetadata(v); QString cID = values[CONTENT_ID].toString(); @@ -155,9 +184,8 @@ QStringList NDBMetadata::getBookListSideloaded() { } Result NDBMetadata::setMetadata(QString const& cID, QVariantMap md) { - uint8_t va[VOLUME_SIZE] = {0}; - symbols.Volume__Volume(va); - Volume* v = getByID((Volume*)va, cID); + NDBVolume vol; + Volume* v = getByID(vol.getVolume(), cID); NDB_ASSERT(NullError, v, "Error getting Volume for %s", cID.toUtf8().constData()); NDB_ASSERT(VolumeError, volIsValid(v), "Volume is not valid for %s", cID.toUtf8().constData()); for (auto i = md.constBegin(); i != md.constEnd(); ++i) { diff --git a/src/ndb/NDBMetadata.h b/src/ndb/NDBMetadata.h index 3dd700b..f0276a0 100644 --- a/src/ndb/NDBMetadata.h +++ b/src/ndb/NDBMetadata.h @@ -13,8 +13,37 @@ typedef void Volume; // Inherits Content typedef QObject VolumeManager; // The VolumeManager is a QObject though typedef void Device; +/* Volume/Content objects appear to be only 8 bytes. They + hold a pointer to a much larger ContentPrivate/VolumePrivate + object (VolumePrivate is at least 400 bytes). + Setting 16 bytes just to be safe here. */ +#define VOLUME_SIZE 16 + namespace NDB { +typedef void(*dstor_ptr_t)(void*); + +struct VolVtable { + dstor_ptr_t volume_dstor; +}; + +class NDBVolume { + public: + enum Result initResult; + Volume* getVolume() { return (Volume*)&vol; } + + NDBVolume(); + ~NDBVolume(); + private: + // Volume contains a vptr and a VolumePrivate ptr + struct { + VolVtable* vptr = nullptr; + void* vol_private = nullptr; + } vol; + void* Volume_vtable; + void* Content_vtable; +}; + class NDBMetadata : public QObject { Q_OBJECT @@ -48,8 +77,7 @@ class NDBMetadata : public QObject { // Getting Volume's VolumeManager* (*VolumeManager__sharedInstance)(); Volume* (*VolumeManager__getById)(Volume* vol, QString const& id, QString const& dbName); - void (*Volume__Volume)(Volume* _this); - void (*Volume__forEach)(QString const& dbName, std::function f); + void (*VolumeManager__forEach)(QString const& dbName, std::function f); int (*Volume__isValid)(Volume* _this); QVariantMap (*Volume__getDbValues)(Volume* volume); int (*Volume__save)(Volume* _this, Device* device); diff --git a/src/ndb/util.h b/src/ndb/util.h index a0aaa1b..fa68d04 100644 --- a/src/ndb/util.h +++ b/src/ndb/util.h @@ -12,6 +12,7 @@ namespace NDB { #endif #define NDB_ASSERT(ret, cond, fmt, ...) if (!(cond)) { nh_log(fmt, ##__VA_ARGS__); return (ret); } +#define NDB_ASSERT_RET(cond, fmt, ...) if (!(cond)) { nh_log(fmt, ##__VA_ARGS__); return; } #define NDB_ASSERT_RES(res, func) (res) = (func); if ((res) != Ok) { return (res); } // like NDB_ASSERT, but sends an appropriate d-bus error on the bus before returning