Created
November 15, 2019 21:42
-
-
Save Matthewacon/75d029a7fa72d0f965f69f267f00b019 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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