Skip to content

Instantly share code, notes, and snippets.

@Matthewacon
Created November 15, 2019 21:42
Show Gist options
  • Save Matthewacon/75d029a7fa72d0f965f69f267f00b019 to your computer and use it in GitHub Desktop.
Save Matthewacon/75d029a7fa72d0f965f69f267f00b019 to your computer and use it in GitHub Desktop.
#include <cstdio>
#include <cstdint>
#include <cstring>
#include <initializer_list>
#include <stdexcept>
#include "cx.h"
struct member_ptr_align_t {
void *ptr, *offset;
} __attribute__((packed));
template<typename S = void>
class Bruh;
template<>
class Bruh<void> {
protected:
struct PseudoVtable final {
private:
unsigned int size;
member_ptr_align_t * entries;
void cleanup() {
size = 0;
delete[] entries;
}
public:
PseudoVtable(std::initializer_list<member_ptr_align_t> entries) noexcept :
size(entries.size()),
entries(new member_ptr_align_t[size])
{
int i = -1;
for (auto& entry : entries) {
this->entries[++i] = entry;
}
}
PseudoVtable(const PseudoVtable& vtable) noexcept :
size(vtable.size),
entries(new member_ptr_align_t[size])
{
memcpy(entries, vtable.entries, size * sizeof(member_ptr_align_t));
}
PseudoVtable(const PseudoVtable&& vtable) noexcept :
size(vtable.size),
entries(new member_ptr_align_t[size])
{
memcpy(entries, vtable.entries, size * sizeof(member_ptr_align_t));
}
~PseudoVtable() {
delete[] entries;
}
member_ptr_align_t& operator[](unsigned int index) {
if (index < size) {
return entries[index];
}
throw std::runtime_error("Index out of bounds!");
}
const member_ptr_align_t& operator[](unsigned int index) const {
return const_cast<PseudoVtable&>(*this)[index];
}
PseudoVtable operator=(const PseudoVtable& vtable) {
size = vtable.size;
delete[] entries;
entries = new member_ptr_align_t[size];
memcpy(entries, vtable.entries, size * sizeof(member_ptr_align_t));
return *this;
}
PseudoVtable operator=(const PseudoVtable&& vtable) {
printf("Move assigned!\n");
size = vtable.size;
delete[] entries;
entries = new member_ptr_align_t[size];
memcpy(entries, vtable.entries, size * sizeof(member_ptr_align_t));
return *this;
}
};
public:
const PseudoVtable templateOverrides;
Bruh(const PseudoVtable vtable) noexcept : templateOverrides(vtable)
{}
virtual void doSomething() {
printf("Bruh<>!\n");
}
template<typename... Args>
int myVirtualTemplateMember(Args... args) const {
auto ptr = const_cast<Bruh<>*>(this);
auto align = templateOverrides[0];
//shift the object pointer to point to the derived class
auto inst = reinterpret_cast<CX::Dummy<> *>(union_cast<intptr_t>(ptr) + (intptr_t)align.offset);
//reset the offset to 0x0 before casting back to member pointer type
align.offset = nullptr;
auto mem_ptr = union_cast<int (CX::Dummy<>::*)(Args...)>(align);
return (inst->*mem_ptr)(args...);
}
};
template<typename S>
class Bruh : public Bruh<void> {
public:
Bruh() noexcept;
void doSomething() override {
printf("Bruh<S>!\n");
}
//Default implementation
template<typename... Args>
int myVirtualTemplateMember(Args... args) const {
return 64;
}
};
//this would be defined in the header preceeding the unit containing `Derived`, so the static
//assertion can use the unqualified type without the definition
template<typename S>
Bruh<S>::Bruh() noexcept :
Bruh<void>::Bruh([&]() -> PseudoVtable {
//will produce &S::template myVirtualTemplateMember<> if it is defined in S
//or &Bruh<S>::template myVirtualTemplateMember<> if it is not
auto align = union_cast<member_ptr_align_t>(&S::template myVirtualTemplateMember<>);
//templates are all global functions, non-static member or otherwise, so the vtable offset
//will always be 0x0. we reuse this value for the vtable offset for the derived object from
//dynamically casted base. it must be reset to 0x0 before invoking the member template in
//the derived class
align.offset = (void *)((intptr_t)(S *)this - (intptr_t)(Bruh<> *)this);
return {align};
}())
{
static_assert(__is_base_of(Bruh<S>, S));
}
class Derived : public Bruh<Derived> {
public:
Derived() : Bruh()
{}
void doSomething() override {
printf("Derived!\n");
}
template<typename... Args>
int myVirtualTemplateMember(Args... args) const {
return 128;
}
};
int main() {
auto
derived = union_cast<member_ptr_align_t>(&Derived::template myVirtualTemplateMember<>),
base = union_cast<member_ptr_align_t>(&Bruh<Derived>::template myVirtualTemplateMember<>),
bruh = union_cast<member_ptr_align_t>(&Bruh<>::template myVirtualTemplateMember<>);
printf("Derived: 0x%lx 0x%lx\n", (uintptr_t)derived.ptr, (uintptr_t)derived.offset);
printf("Bruh<Derived>: 0x%lx 0x%lx\n", (uintptr_t)base.ptr, (uintptr_t)base.offset);
printf("Bruh<>: 0x%lx 0x%lx\n", (uintptr_t)bruh.ptr, (uintptr_t)bruh.offset);
Derived d;
Bruh<>* b = &d;
member_ptr_align_t align_ptr = (b->templateOverrides)[0];
printf("0x%lx 0x%lx\n", (uintptr_t)align_ptr.ptr, (uintptr_t)align_ptr.offset);
printf("returned value: %d\n", b->myVirtualTemplateMember<>());
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment