I used IDA7 to decompile some binary and got something like this.
Pseudocode:
int A::start(){
int (__fastcall *v3)(B *const); // x2
unsigned int fd; // w0
int (__fastcall *v5)(A *const, int); // x3
int v6; // w20
v3 = *(this->_vptr.B + 6); //pointing to getFD (the sixth function of ref_B)
if ( v3 != B::getFD )
{
fd = v3(&this->B);
v5 = *(this->_vptr.B + 7);
if ( v5 == B::listen )
goto LABEL_4;
LABEL_9:
v6 = v5(this, fb);
if ( v6 )
goto LABEL_10;
goto LABEL_4;
}
fd = this->mFd;
v5 = *(this->_vptr.B + 7);
if ( v5 != B::listen ) //major problem here, A doesn't overwrite listen,
//so "v5 != B::listen" never true,
//so we can't just place this->listen(fb) here
goto LABEL_9;
LABEL_4:
if ( evutil_socketpair(1, 1, 0, fd) ) // this one comes from event2
{
fd = -1LL;
return -1;
}
LABEL_10:
return 0;
}
And Constructor of A looks like:
Pseudocode:
void A::A(A *const this)
{
B::B(&this->B);
this->_vptr.B = ref_B;
}
ref_B looks like:
.data.rel.ro:000000555644D8D8 DCQ _ZTI1A ; `typeinfo for'A
.data.rel.ro:000000555644D8E0 ref_B DCQ _ZThn8_N1AD1Ev ; `non-virtual thunk to'A::~A()
.data.rel.ro:000000555644D8E8 DCQ _ZThn8_N1AD0Ev ; `non-virtual thunk to'A::~A()
.data.rel.ro:000000555644D8F0 DCQ _ZThn8_N1A4openE8TYPE ; `non-virtual thunk to'A::open(TYPE)
.data.rel.ro:000000555644D8F8 DCQ _ZThn8_N1A5closeEv ; `non-virtual thunk to'A::close(void)
.data.rel.ro:000000555644D900 DCQ _ZN1B9get******Ev ; B::get******(void)
.data.rel.ro:000000555644D908 DCQ _ZN1B9write****EPKcm ; B::write****(char const*,ulong)
.data.rel.ro:000000555644D910 DCQ _ZN1B5getFDEv ; B::getFD(void)
.data.rel.ro:000000555644D918 DCQ _ZN1B6listenEi ; `non-virtual thunk to'B::listen(int)
struct(class) A looks like:
struct __cppobj A : B
{
pthread_mutex_t mMutex;
};
struct(class) B looks like:
struct B
{
int (**_vptr.B)(...);//this is generated by IDA7, and I think
// it's pretty much equal to _vptr_B (so dose all '_vptr.B' above)
int mFd;
};
My problem is what kind of source code yields code like A::start. I double checked the assemble code of A::start, and I'm quite sure IDA did the right Pseudocode translation.
I tried to understand the code's behaviour. It looks like A::start wants to judge whether getFD from it's vtable is from ref_B.(if Some class X derive from A and overwrite getFD, but not overwrite start, then calling x.start will calling its getFD instead of using mFd from class B). But I just don't know how to achieve the source code. Will some one so kind to help me out?
I think the code you highlighted is just the compiler optimistically inlining a virtual function with a slow path to fall back to.
The original source code probably is just
fd = getFD();
where getFD
is declared in B
as:
virtual int getFD() { return this->mFd; }
The compiler cannot know whether A
in turn is not subclassed with an override for getFD
, so it must keep the slower virtual call as a backup.