Fixture 133
cpp multiple inheritance
C++ · 3 functions · 4 lanes · 8 of 12 function-lanes behave identically
2 of 4 lanes have a function that returns a different result after decompilation: clang-O0 (1/3), gcc-O0 (1/3).
Multiple inheritance forces this-pointer adjustment: a Derived* converted to its second base points partway into the object, so the compiler emits thunks that add a fixed offset before jumping to the real body. Casting between base pointers therefore changes the numeric address, and comparing two base pointers of one object is NOT a plain address comparison.
/* 133_cpp_multiple_inheritance.cpp
*
* Multiple inheritance forces `this`-pointer adjustment: a Derived* converted
* to its second base points partway into the object, so the compiler emits
* thunks that add a fixed offset before jumping to the real body. Casting
* between base pointers therefore changes the numeric address, and comparing
* two base pointers of one object is NOT a plain address comparison.
*/
#include <stdint.h>
namespace {
class Readable {
public:
virtual ~Readable() {}
virtual int32_t read(int32_t index) const { return index; }
};
class Writable {
public:
virtual ~Writable() {}
virtual int32_t write(int32_t index, int32_t value) { return index + value; }
virtual int32_t flushed() const { return 0; }
};
class File : public Readable, public Writable {
public:
int32_t stored;
File() : stored(0) {}
int32_t read(int32_t index) const override { return index * 2 + stored; }
int32_t write(int32_t index, int32_t value) override {
stored = index ^ value;
return stored;
}
int32_t flushed() const override { return stored + 1; }
};
} // namespace
extern "C" int32_t cpp_mi_dispatch(int32_t index, int32_t value) {
File file;
Readable *readable = &file;
Writable *writable = &file; /* adjusted: not the same address */
int32_t written = writable->write(index, value);
int32_t got = readable->read(index);
return written * 100 + got;
}
extern "C" int32_t cpp_mi_pointer_adjustment(int32_t seed) {
File file;
file.stored = seed;
const Readable *readable = &file;
const Writable *writable = &file;
/* The two base subobjects live at different offsets, so the raw addresses
* differ even though both denote the same complete object. */
const void *first = static_cast<const void *>(readable);
const void *second = static_cast<const void *>(writable);
return (first != second) ? 1 : 0;
}
extern "C" int32_t cpp_mi_cross_cast(int32_t index, int32_t value) {
File file;
Writable *writable = &file;
/* Cross-cast through the complete object, then dispatch on the far base. */
File *complete = static_cast<File *>(writable);
Readable *readable = complete;
writable->write(index, value);
return readable->read(index) + writable->flushed();
} Recovered C
Generated by glaurung decompile --style decbench at b47f6b43.
baseline.json records the result after recompiling the C and calling it beside the
original with seeded inputs.
clang -O0
1/3cpp_mi_cross_cast fail 42 lines
// glaurung: cpp_mi_cross_cast @ 0x1300
typedef struct File File;
typedef struct Readable Readable;
typedef struct Writable Writable;
struct File;
__attribute__((no_stack_protector)) int32_t cpp_mi_cross_cast(int32_t arg0, int32_t arg1) {
extern void _ZN12_GLOBAL__N_14FileC2Ev(struct File *);
extern void _ZN12_GLOBAL__N_14FileD2Ev(struct File *);
unsigned char writable[8];
unsigned char complete[24];
unsigned char readable[8];
unsigned char local_20[24];
long local_50;
long local_58;
int local_64;
int local_68;
int local_6c;
long var18;
long var24;
long var29;
long var6;
// x86-64 prologue: save rbp, frame 112 bytes
((void (*)(char *))_ZN12_GLOBAL__N_14FileC2Ev)((char *)(&local_20[0]));
local_50 = 0;
if ((&local_20[0] != 0)) {
local_50 = (long)((&local_20[0] + 8));
}
*(long *)(&writable[0]) = local_50;
var6 = *(long *)(&writable[0]);
local_58 = var6;
*(long *)(&complete[0]) = ((var6 != 0) ? (local_58 - 8) : 0);
*(long *)(&readable[0]) = *(long *)(&complete[0]);
var18 = ((long (*)(void))(*(long *)((*(long *)(*(long *)(&writable[0])) + 16))))();
var24 = ((long (*)(void))(*(long *)((*(long *)(*(long *)(&readable[0])) + 16))))();
local_64 = var24;
var29 = ((long (*)(void))(*(long *)((*(long *)(*(long *)(&writable[0])) + 24))))();
local_68 = var29;
local_6c = ((unsigned int)(local_64) + (unsigned int)(local_68));
((void (*)(char *))_ZN12_GLOBAL__N_14FileD2Ev)((char *)(&local_20[0]));
// x86-64 epilogue: restore rbp
return (unsigned int)(local_6c);
} cpp_mi_dispatch fail 37 lines
// glaurung: cpp_mi_dispatch @ 0x1120
typedef struct Readable Readable;
typedef struct Writable Writable;
struct File;
__attribute__((no_stack_protector)) int32_t cpp_mi_dispatch(int32_t arg0, int32_t arg1) {
extern void _ZN12_GLOBAL__N_14FileC2Ev(struct File *);
extern void _ZN12_GLOBAL__N_14FileD2Ev(struct File *);
unsigned char readable[8];
unsigned char writable[8];
int written;
int got;
unsigned char local_20[24];
long local_50;
int local_54;
int local_58;
int local_5c;
long var12;
long var19;
// x86-64 prologue: save rbp, frame 96 bytes
((void (*)(char *))_ZN12_GLOBAL__N_14FileC2Ev)((char *)(&local_20[0]));
*(long *)(&readable[0]) = (long)((long)(&local_20[0]));
local_50 = 0;
if ((&local_20[0] != 0)) {
local_50 = (long)((&local_20[0] + 8));
}
*(long *)(&writable[0]) = local_50;
var12 = ((long (*)(void))(*(long *)((*(long *)(*(long *)(&writable[0])) + 16))))();
local_54 = var12;
written = local_54;
var19 = ((long (*)(void))(*(long *)((*(long *)(*(long *)(&readable[0])) + 16))))();
local_58 = var19;
got = local_58;
local_5c = ((written * 100) + got);
((void (*)(char *))_ZN12_GLOBAL__N_14FileD2Ev)((char *)(&local_20[0]));
// x86-64 epilogue: restore rbp
return (unsigned int)(local_5c);
} cpp_mi_pointer_adjustment pass 30 lines
// glaurung: cpp_mi_pointer_adjustment @ 0x1270
typedef struct Readable Readable;
typedef struct Writable Writable;
struct File;
__attribute__((no_stack_protector)) int32_t cpp_mi_pointer_adjustment(int32_t arg0) {
extern void _ZN12_GLOBAL__N_14FileC2Ev(struct File *);
extern void _ZN12_GLOBAL__N_14FileD2Ev(struct File *);
unsigned char readable[8];
unsigned char writable[8];
unsigned char local_20[24];
long local_38;
long local_40;
long local_48;
int local_4c;
// x86-64 prologue: save rbp, frame 80 bytes
((void (*)(char *))_ZN12_GLOBAL__N_14FileC2Ev)((char *)(&local_20[0]));
*(int *)((&local_20[0] + 16)) = arg0;
*(long *)(&readable[0]) = (long)((long)(&local_20[0]));
local_48 = 0;
if ((&local_20[0] != 0)) {
local_48 = (long)((&local_20[0] + 8));
}
*(long *)(&writable[0]) = local_48;
local_38 = *(long *)(&readable[0]);
local_40 = *(long *)(&writable[0]);
local_4c = ((local_38 != local_40) ? 1 : 0);
((void (*)(char *))_ZN12_GLOBAL__N_14FileD2Ev)((char *)(&local_20[0]));
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4c);
} gcc -O0
1/3cpp_mi_cross_cast fail 48 lines
// glaurung: cpp_mi_cross_cast @ 0x14a3
typedef struct File File;
typedef struct Readable Readable;
typedef struct Writable Writable;
struct File;
int32_t cpp_mi_cross_cast(int32_t arg0, int32_t arg1) {
extern __attribute__((noreturn)) long _Unwind_Resume(long);
extern void _ZN12_GLOBAL__N_14FileC2Ev(const struct File *);
extern void _ZN12_GLOBAL__N_14FileD1Ev(const struct File *);
extern __attribute__((noreturn)) void __stack_chk_fail(void);
Writable * writable;
File * complete;
Readable * readable;
long local_18;
unsigned char local_30[24];
long ret;
long var15;
long var23;
long var25;
long var31;
long var32;
long var34;
long var39;
long var42;
// x86-64 prologue: save rbp, frame 8 bytes
local_18 = (long)(0x28);
((void (*)(char *))_ZN12_GLOBAL__N_14FileC2Ev)((char *)(&local_30[0]));
writable = (Writable *)((&local_30[0] + 8));
complete = ((writable != 0) ? (File *)(((long)writable - 8)) : (File *)(0));
readable = (Readable *)complete;
var15 = ((long (*)(char *, long, long))(*(long *)((*(long *)((long)writable) + 16))))((char *)((long)writable), (unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)));
var23 = ((long (*)(char *, long))(*(long *)((*(long *)((long)readable) + 16))))((char *)((long)readable), (unsigned long)((unsigned int)(arg0)));
var25 = (unsigned long)((unsigned int)(var23));
var31 = ((long (*)(char *))(*(long *)((*(long *)((long)writable) + 24))))((char *)((long)writable));
var32 = var31;
var34 = (unsigned long)((unsigned int)((var25 + var31)));
((void (*)(char *))_ZN12_GLOBAL__N_14FileD1Ev)((char *)(&local_30[0]));
ret = (unsigned long)((unsigned int)(var34));
if ((local_18 == 0x28)) {
// x86-64 epilogue: restore rbp
return ret;
}
__stack_chk_fail();
// __glaurung_eh_landing_1575
var39 = var32;
((void (*)(char *))_ZN12_GLOBAL__N_14FileD1Ev)((char *)(&local_30[0]));
var42 = _Unwind_Resume(var39);
} cpp_mi_dispatch fail 45 lines
// glaurung: cpp_mi_dispatch @ 0x1338
typedef struct Readable Readable;
typedef struct Writable Writable;
struct File;
int32_t cpp_mi_dispatch(int32_t arg0, int32_t arg1) {
extern __attribute__((noreturn)) long _Unwind_Resume(long);
extern void _ZN12_GLOBAL__N_14FileC2Ev(const struct File *);
extern void _ZN12_GLOBAL__N_14FileD1Ev(const struct File *);
extern __attribute__((noreturn)) void __stack_chk_fail(void);
Readable * readable;
Writable * writable;
int written;
int got;
long local_18;
unsigned char local_30[24];
long ret;
long var13;
long var21;
long var22;
long var26;
long var31;
long var34;
// x86-64 prologue: save rbp, frame 8 bytes
local_18 = (long)(0x28);
((void (*)(char *))_ZN12_GLOBAL__N_14FileC2Ev)((char *)(&local_30[0]));
readable = (Readable *)&local_30[0];
writable = (Writable *)((&local_30[0] + 8));
var13 = ((long (*)(char *, long, long))(*(long *)((*(long *)((long)writable) + 16))))((char *)((long)writable), (unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)));
written = var13;
var21 = ((long (*)(char *, long))(*(long *)((*(long *)((long)readable) + 16))))((char *)((long)readable), (unsigned long)((unsigned int)(arg0)));
var22 = var21;
got = var21;
var26 = (unsigned long)((unsigned int)((((unsigned long)((unsigned int)(written)) * 100) + (unsigned long)((unsigned int)(got)))));
((void (*)(char *))_ZN12_GLOBAL__N_14FileD1Ev)((char *)(&local_30[0]));
ret = (unsigned long)((unsigned int)(var26));
if ((local_18 == 0x28)) {
// x86-64 epilogue: restore rbp
return ret;
}
__stack_chk_fail();
// __glaurung_eh_landing_13e7
var31 = var22;
((void (*)(char *))_ZN12_GLOBAL__N_14FileD1Ev)((char *)(&local_30[0]));
var34 = _Unwind_Resume(var31);
} cpp_mi_pointer_adjustment pass 33 lines
// glaurung: cpp_mi_pointer_adjustment @ 0x1410
typedef struct Readable Readable;
typedef struct Writable Writable;
struct File;
int32_t cpp_mi_pointer_adjustment(int32_t arg0) {
extern void _ZN12_GLOBAL__N_14FileC2Ev(const struct File *);
extern void _ZN12_GLOBAL__N_14FileD1Ev(const struct File *);
extern __attribute__((noreturn)) void __stack_chk_fail(void);
const Readable * readable;
const Writable * writable;
char * first;
char * second;
long local_18;
unsigned char local_30[24];
long ret;
int var11;
// x86-64 prologue: save rbp, frame 8 bytes
local_18 = (long)(0x28);
((void (*)(char *))_ZN12_GLOBAL__N_14FileC2Ev)((char *)(&local_30[0]));
*(int *)((&local_30[0] + 16)) = arg0;
readable = (const Readable *)&local_30[0];
writable = (const Writable *)((&local_30[0] + 8));
first = (char *)readable;
second = (char *)writable;
var11 = ((first == second) ? 0 : 1);
((void (*)(char *))_ZN12_GLOBAL__N_14FileD1Ev)((char *)(&local_30[0]));
ret = (unsigned long)((unsigned int)(var11));
if ((local_18 != 0x28)) {
__stack_chk_fail();
}
// x86-64 epilogue: restore rbp
return ret;
} clang -O2
3/3cpp_mi_cross_cast pass 6 lines
// glaurung: cpp_mi_cross_cast @ 0x1120
int32_t cpp_mi_cross_cast(int32_t arg0, int32_t arg1) {
int var2;
var2 = ((unsigned int)((arg1 ^ arg0)) + arg0);
return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var2)) + (unsigned long)((unsigned int)(var2))))) + 1));
} cpp_mi_dispatch pass 7 lines
// glaurung: cpp_mi_dispatch @ 0x1100
int32_t cpp_mi_dispatch(int32_t arg0, int32_t arg1) {
int written;
int got;
written = (unsigned long)((unsigned int)((arg1 ^ arg0)));
return (unsigned int)(((written * 100) + (unsigned long)((unsigned int)((written + (arg0 * 2))))));
} cpp_mi_pointer_adjustment pass 4 lines
// glaurung: cpp_mi_pointer_adjustment @ 0x1110
int32_t cpp_mi_pointer_adjustment(int32_t arg0) {
return 1;
} gcc -O2
3/3cpp_mi_cross_cast pass 6 lines
// glaurung: cpp_mi_cross_cast @ 0x1120
int32_t cpp_mi_cross_cast(int32_t arg0, int32_t arg1) {
long var1;
var1 = (unsigned long)((unsigned int)((arg1 ^ arg0)));
return (unsigned int)(((var1 + (unsigned long)((unsigned int)((var1 + (arg0 * 2))))) + 1));
} cpp_mi_dispatch pass 7 lines
// glaurung: cpp_mi_dispatch @ 0x1100
int32_t cpp_mi_dispatch(int32_t arg0, int32_t arg1) {
int written;
int got;
written = (unsigned long)((unsigned int)((arg1 ^ arg0)));
return (unsigned int)(((unsigned long)((unsigned int)((written + (arg0 * 2)))) + (written * 100)));
} cpp_mi_pointer_adjustment pass 4 lines
// glaurung: cpp_mi_pointer_adjustment @ 0x1110
int32_t cpp_mi_pointer_adjustment(int32_t arg0) {
return 1;
}