Date: Sat, 27 Jan 2024 00:58:58 +0000
On Fri, Jan 26, 2024 at 12:06 AM Thiago Macieira wrote:
>
> This linker change is DoA for ABI purposes.
Jarrad, I have implemented your "direct" member function pointers on
x86_64 with GNU compilers and build tools. I did so by just editing
the linker to provide a map of typeinfo's to vtables. I didn't need to
edit the compiler-proper.
I got my linker working just now. Here's the pull request comparison
against the trunk of 'binutils' which contains the GNU bfd linker:
https://github.com/bminor/binutils-gdb/compare/8409b75...healytpk:linker-vtable:e4adc46
In the coming days I'm gonna ask Arthur and Matt if I can stick it up
on GodBolt (or at least ask them how to host my own little mini
GodBolt on my own webspace).
Using my linker, I can build the following program which prints out:
Polymorphic Facilitator for Base = 0x562af0ed1c48
Polymorphic Facilitator for Derived = 0x562af0ed1c30
Hi, I'm Derived!
Hi, I'm Base!
Hi, I'm Derived!
Here's my program:
struct Base {
virtual void Speak(void) { std::cout << "Hi, I'm Base!\n"; }
};
struct Derived : Base {
void Speak(void) override { std::cout << "Hi, I'm Derived!\n"; }
};
int main(void)
{
std::cout << "Polymorphic Facilitator for Base = " <<
get_polymorphic_facilitator(typeid(Base )) << std::endl;
std::cout << "Polymorphic Facilitator for Derived = " <<
get_polymorphic_facilitator(typeid(Derived)) << std::endl;
Derived obj;
void (Base::*pmf1)(void) = &Base::Speak;
(obj.*pmf1)();
void (Base::*pmf2)(void) = std::devirtualise(pmf1,
std::get_polymorphic_facilitator(typeid(Base)));
(obj.*pmf2)();
void (Base::*pmf3)(void) = std::devirtualise(pmf1,
std::get_polymorphic_facilitator(typeid(Derived)));
(obj.*pmf3)();
}
And here's how I implemented 'std::devirtualise' and
'std::get_polymorphic_facilitator':
#include <cassert> // assert
#include <cstring> // memcpy
#include <algorithm> // lower_bound
#include <typeinfo> // type_info
extern uint32_t __map_typeinfo_vtable[4u];
namespace std {
void const *get_polymorphic_facilitator(std::type_info const &ti)
{
using std::uint32_t, std::pair;
uint32_t const size_in_bytes = __map_typeinfo_vtable[0u];
if ( 0u == size_in_bytes ) return nullptr;
uint32_t const offset = __map_typeinfo_vtable[1u];
char const *const start_of_section =
(char*)&__map_typeinfo_vtable - offset;
assert( (char*)&ti > (char*)start_of_section );
uint32_t const offset_of_typeinfo = (char*)&ti -
(char*)start_of_section;
pair<uint32_t,uint32_t> const *const p =
(pair<uint32_t,uint32_t> const *)&__map_typeinfo_vtable[2u];
pair<uint32_t,uint32_t> const *const q = p +
(size_in_bytes / (4u + 4u));
pair<uint32_t,uint32_t> const *const found =
std::lower_bound(
p,
q,
offset_of_typeinfo,
[](std::pair<uint32_t,uint32_t> const &a, uint32_t
const n){ return a.first < n; });
if ( q == found ) return nullptr;
return (char*)start_of_section + found->second + 0x10;
}
template<typename R, class Class, typename... Params>
R (Class::*devirtualise(R (Class::*pmf)(Params...), void const
*const p) noexcept)(Params... args)
{
auto n = *static_cast<std::uintptr_t*>(static_cast<void*>(&pmf));
if ( 0u == (n-- & 1u) ) return pmf; /* no need to devirtualise */
void const *const *pvtable = static_cast<void const *const*>(p);
std::memcpy(&pmf, pvtable + n, sizeof(void(*)(void)));
return pmf;
}
}
>
> This linker change is DoA for ABI purposes.
Jarrad, I have implemented your "direct" member function pointers on
x86_64 with GNU compilers and build tools. I did so by just editing
the linker to provide a map of typeinfo's to vtables. I didn't need to
edit the compiler-proper.
I got my linker working just now. Here's the pull request comparison
against the trunk of 'binutils' which contains the GNU bfd linker:
https://github.com/bminor/binutils-gdb/compare/8409b75...healytpk:linker-vtable:e4adc46
In the coming days I'm gonna ask Arthur and Matt if I can stick it up
on GodBolt (or at least ask them how to host my own little mini
GodBolt on my own webspace).
Using my linker, I can build the following program which prints out:
Polymorphic Facilitator for Base = 0x562af0ed1c48
Polymorphic Facilitator for Derived = 0x562af0ed1c30
Hi, I'm Derived!
Hi, I'm Base!
Hi, I'm Derived!
Here's my program:
struct Base {
virtual void Speak(void) { std::cout << "Hi, I'm Base!\n"; }
};
struct Derived : Base {
void Speak(void) override { std::cout << "Hi, I'm Derived!\n"; }
};
int main(void)
{
std::cout << "Polymorphic Facilitator for Base = " <<
get_polymorphic_facilitator(typeid(Base )) << std::endl;
std::cout << "Polymorphic Facilitator for Derived = " <<
get_polymorphic_facilitator(typeid(Derived)) << std::endl;
Derived obj;
void (Base::*pmf1)(void) = &Base::Speak;
(obj.*pmf1)();
void (Base::*pmf2)(void) = std::devirtualise(pmf1,
std::get_polymorphic_facilitator(typeid(Base)));
(obj.*pmf2)();
void (Base::*pmf3)(void) = std::devirtualise(pmf1,
std::get_polymorphic_facilitator(typeid(Derived)));
(obj.*pmf3)();
}
And here's how I implemented 'std::devirtualise' and
'std::get_polymorphic_facilitator':
#include <cassert> // assert
#include <cstring> // memcpy
#include <algorithm> // lower_bound
#include <typeinfo> // type_info
extern uint32_t __map_typeinfo_vtable[4u];
namespace std {
void const *get_polymorphic_facilitator(std::type_info const &ti)
{
using std::uint32_t, std::pair;
uint32_t const size_in_bytes = __map_typeinfo_vtable[0u];
if ( 0u == size_in_bytes ) return nullptr;
uint32_t const offset = __map_typeinfo_vtable[1u];
char const *const start_of_section =
(char*)&__map_typeinfo_vtable - offset;
assert( (char*)&ti > (char*)start_of_section );
uint32_t const offset_of_typeinfo = (char*)&ti -
(char*)start_of_section;
pair<uint32_t,uint32_t> const *const p =
(pair<uint32_t,uint32_t> const *)&__map_typeinfo_vtable[2u];
pair<uint32_t,uint32_t> const *const q = p +
(size_in_bytes / (4u + 4u));
pair<uint32_t,uint32_t> const *const found =
std::lower_bound(
p,
q,
offset_of_typeinfo,
[](std::pair<uint32_t,uint32_t> const &a, uint32_t
const n){ return a.first < n; });
if ( q == found ) return nullptr;
return (char*)start_of_section + found->second + 0x10;
}
template<typename R, class Class, typename... Params>
R (Class::*devirtualise(R (Class::*pmf)(Params...), void const
*const p) noexcept)(Params... args)
{
auto n = *static_cast<std::uintptr_t*>(static_cast<void*>(&pmf));
if ( 0u == (n-- & 1u) ) return pmf; /* no need to devirtualise */
void const *const *pvtable = static_cast<void const *const*>(p);
std::memcpy(&pmf, pvtable + n, sizeof(void(*)(void)));
return pmf;
}
}
Received on 2024-01-27 00:59:10