Allow extending vtables

This commit adds VTableLayoutDiffIR::IsExtended() that determines
whether the difference is a pure extension, i.e., appending virtual
functions to vtables.

Test: ./test.py
Bug: 248418092
Change-Id: I339713c5fff1dfa50dc7875272c0e3a59f858f57
This commit is contained in:
Hsin-Yi Chen
2022-10-04 18:23:11 +08:00
parent e6b57e4a05
commit 393ba54671
7 changed files with 639 additions and 16 deletions

View File

@@ -246,18 +246,13 @@ bool AbiDiffHelper::CompareVTables(
old_record->GetVTableLayout().GetVTableComponents();
const std::vector<VTableComponentIR> &new_components =
new_record->GetVTableLayout().GetVTableComponents();
if (old_components.size() > new_components.size()) {
// Something in the vtable got deleted.
if (old_components.size() != new_components.size()) {
return false;
}
uint32_t i = 0;
while (i < old_components.size()) {
auto &old_element = old_components.at(i);
auto &new_element = new_components.at(i);
if (!CompareVTableComponents(old_element, new_element)) {
for (size_t i = 0; i < old_components.size(); i++) {
if (!CompareVTableComponents(old_components[i], new_components[i])) {
return false;
}
i++;
}
return true;
}

View File

@@ -17,6 +17,99 @@
namespace header_checker {
namespace repr {
static inline bool IsVOffset(VTableComponentIR::Kind kind) {
return kind == VTableComponentIR::VBaseOffset ||
kind == VTableComponentIR::VCallOffset;
}
static inline bool IsFunctionPointer(VTableComponentIR::Kind kind) {
return kind == VTableComponentIR::FunctionPointer ||
kind == VTableComponentIR::CompleteDtorPointer ||
kind == VTableComponentIR::DeletingDtorPointer;
}
// A Vtable consists of one or more sub-vtables. Each sub-vtable is a sequence
// of components in the following order:
// Zero or more VCallOffset or VBaseOffset.
// One OffsetToTop.
// One RTTI.
// Zero or more FunctionPointer, CompleteDtorPointer, or DeletingDtorPointer.
//
// An object's vtable pointer points to the next component of the RTTI
// component. Hence, new components can be appended or prepended to sub-vtables
// without breaking compatibility.
bool VTableLayoutDiffIR::IsExtended() const {
const std::vector<VTableComponentIR> &old_components =
old_layout_.GetVTableComponents();
const std::vector<VTableComponentIR> &new_components =
new_layout_.GetVTableComponents();
const auto old_end = old_components.end();
const auto new_end = new_components.end();
auto old_it = old_components.begin();
auto new_it = new_components.begin();
bool is_extended = false;
while (old_it != old_end) {
const auto old_begin = old_it;
const auto new_begin = new_it;
// Iterate VCallOffset and VBaseOffset.
while (old_it != old_end && IsVOffset(old_it->GetKind())) {
old_it++;
}
while (new_it != new_end && IsVOffset(new_it->GetKind())) {
new_it++;
}
// Compare VCallOffset and VBaseOffset.
auto old_back_it = old_it;
auto new_back_it = new_it;
while (old_back_it != old_begin) {
if (new_back_it == new_begin) {
return false;
}
old_back_it--;
new_back_it--;
if (old_back_it->GetKind() != new_back_it->GetKind()) {
return false;
}
}
// The new sub-vtable has additional VOffsets at the beginning.
if (new_back_it != new_begin) {
is_extended = true;
}
// Compare OffsetToTop.
if (old_it == old_end || new_it == new_end ||
old_it->GetKind() != VTableComponentIR::OffsetToTop ||
new_it->GetKind() != VTableComponentIR::OffsetToTop) {
return false;
}
old_it++;
new_it++;
// Compare RTTI.
if (old_it == old_end || new_it == new_end ||
old_it->GetKind() != VTableComponentIR::RTTI ||
new_it->GetKind() != VTableComponentIR::RTTI ||
old_it->GetName() != new_it->GetName()) {
return false;
}
old_it++;
new_it++;
// Compare function pointers.
while (old_it != old_end && IsFunctionPointer(old_it->GetKind())) {
if (new_it == new_end || old_it->GetKind() != new_it->GetKind() ||
old_it->GetName() != new_it->GetName()) {
return false;
}
old_it++;
new_it++;
}
// The new sub-vtable has additional function pointers at the end.
while (new_it != new_end && IsFunctionPointer(new_it->GetKind())) {
is_extended = true;
new_it++;
}
}
return new_it == new_end ? is_extended : false;
}
bool RecordTypeDiffIR::IsExtended() const {
bool is_extended = false;
if (type_diff_ != nullptr) {
@@ -43,8 +136,10 @@ bool RecordTypeDiffIR::IsExtended() const {
return false;
}
if (vtable_diffs_ != nullptr) {
// TODO(b/248418092): Compare vtables.
return false;
if (!vtable_diffs_->IsExtended()) {
return false;
}
is_extended = true;
}
// This function skips comparing the access specifiers of field_diffs_
// because AbiDiffHelper::CompareCommonRecordFields does not report

View File

@@ -102,6 +102,8 @@ class VTableLayoutDiffIR {
return new_layout_;
}
bool IsExtended() const;
protected:
const VTableLayoutIR &old_layout_;
const VTableLayoutIR &new_layout_;

View File

@@ -11,4 +11,24 @@ struct Struct2 {
} member;
};
Struct1 &PassByReference(Struct1 &, Struct2 &);
struct Vtable1 {
int member_1;
virtual ~Vtable1();
virtual void function_1() = 0;
};
struct Vtable2 {
int member_2;
virtual void function_2() = 0;
};
struct Vtable3 : virtual public Vtable1, virtual public Vtable2 {
int member_3;
virtual ~Vtable3();
virtual void function_3();
};
Vtable3 &PassByReference(Struct1 &, Struct2 &);

View File

@@ -14,4 +14,31 @@ struct Struct2 {
} member;
};
Struct1 &PassByReference(Struct1 &, Struct2 &);
struct Vtable1 {
int member_1;
int added_member_1;
virtual ~Vtable1();
virtual void function_1() = 0;
virtual void added_function_1() = 0;
};
struct Vtable2 {
int member_2;
int added_member_2;
virtual void function_2();
virtual void added_function_2() = 0;
virtual ~Vtable2();
};
struct Vtable3 : virtual public Vtable1, virtual public Vtable2 {
int member_3;
int added_member_3;
virtual ~Vtable3();
virtual void function_3();
virtual void added_function_3();
};
Vtable3 &PassByReference(Struct1 &, Struct2 &);

View File

@@ -30,6 +30,12 @@
"referenced_type" : "_ZTIs",
"self_type" : "_ZTIs",
"size" : 2
},
{
"linker_set_key" : "_ZTIv",
"name" : "void",
"referenced_type" : "_ZTIv",
"self_type" : "_ZTIv"
}
],
"elf_functions" :
@@ -55,7 +61,7 @@
"referenced_type" : "_ZTIR7Struct2"
}
],
"return_type" : "_ZTIR7Struct1",
"return_type" : "_ZTIR7Vtable3",
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/extensions.h"
}
],
@@ -79,9 +85,47 @@
"self_type" : "_ZTIR7Struct2",
"size" : 8,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/extensions.h"
},
{
"alignment" : 8,
"linker_set_key" : "_ZTIR7Vtable3",
"name" : "Vtable3 &",
"referenced_type" : "_ZTI7Vtable3",
"self_type" : "_ZTIR7Vtable3",
"size" : 8,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/extensions.h"
}
],
"pointer_types" :
[
{
"alignment" : 8,
"linker_set_key" : "_ZTIP7Vtable1",
"name" : "Vtable1 *",
"referenced_type" : "_ZTI7Vtable1",
"self_type" : "_ZTIP7Vtable1",
"size" : 8,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/extensions.h"
},
{
"alignment" : 8,
"linker_set_key" : "_ZTIP7Vtable2",
"name" : "Vtable2 *",
"referenced_type" : "_ZTI7Vtable2",
"self_type" : "_ZTIP7Vtable2",
"size" : 8,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/extensions.h"
},
{
"alignment" : 8,
"linker_set_key" : "_ZTIP7Vtable3",
"name" : "Vtable3 *",
"referenced_type" : "_ZTI7Vtable3",
"self_type" : "_ZTIP7Vtable3",
"size" : 8,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/extensions.h"
}
],
"pointer_types" : [],
"qualified_types" : [],
"record_types" :
[
@@ -132,6 +176,233 @@
"size" : 8,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/extensions.h"
},
{
"alignment" : 8,
"fields" :
[
{
"field_name" : "member_1",
"field_offset" : 64,
"referenced_type" : "_ZTIi"
},
{
"field_name" : "added_member_1",
"field_offset" : 96,
"referenced_type" : "_ZTIi"
}
],
"linker_set_key" : "_ZTI7Vtable1",
"name" : "Vtable1",
"referenced_type" : "_ZTI7Vtable1",
"self_type" : "_ZTI7Vtable1",
"size" : 16,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/extensions.h",
"vtable_components" :
[
{
"kind" : "offset_to_top"
},
{
"kind" : "rtti",
"mangled_component_name" : "_ZTI7Vtable1"
},
{
"kind" : "complete_dtor_pointer",
"mangled_component_name" : "_ZN7Vtable1D1Ev"
},
{
"kind" : "deleting_dtor_pointer",
"mangled_component_name" : "_ZN7Vtable1D0Ev"
},
{
"is_pure" : true,
"mangled_component_name" : "_ZN7Vtable110function_1Ev"
},
{
"is_pure" : true,
"mangled_component_name" : "_ZN7Vtable116added_function_1Ev"
}
]
},
{
"alignment" : 8,
"fields" :
[
{
"field_name" : "member_2",
"field_offset" : 64,
"referenced_type" : "_ZTIi"
},
{
"field_name" : "added_member_2",
"field_offset" : 96,
"referenced_type" : "_ZTIi"
}
],
"linker_set_key" : "_ZTI7Vtable2",
"name" : "Vtable2",
"referenced_type" : "_ZTI7Vtable2",
"self_type" : "_ZTI7Vtable2",
"size" : 16,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/extensions.h",
"vtable_components" :
[
{
"kind" : "offset_to_top"
},
{
"kind" : "rtti",
"mangled_component_name" : "_ZTI7Vtable2"
},
{
"mangled_component_name" : "_ZN7Vtable210function_2Ev"
},
{
"is_pure" : true,
"mangled_component_name" : "_ZN7Vtable216added_function_2Ev"
},
{
"kind" : "complete_dtor_pointer",
"mangled_component_name" : "_ZN7Vtable2D1Ev"
},
{
"kind" : "deleting_dtor_pointer",
"mangled_component_name" : "_ZN7Vtable2D0Ev"
}
]
},
{
"alignment" : 8,
"base_specifiers" :
[
{
"is_virtual" : true,
"referenced_type" : "_ZTI7Vtable1"
},
{
"is_virtual" : true,
"referenced_type" : "_ZTI7Vtable2"
}
],
"fields" :
[
{
"field_name" : "member_3",
"field_offset" : 64,
"referenced_type" : "_ZTIi"
},
{
"field_name" : "added_member_3",
"field_offset" : 96,
"referenced_type" : "_ZTIi"
}
],
"linker_set_key" : "_ZTI7Vtable3",
"name" : "Vtable3",
"referenced_type" : "_ZTI7Vtable3",
"self_type" : "_ZTI7Vtable3",
"size" : 48,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/extensions.h",
"vtable_components" :
[
{
"component_value" : 32,
"kind" : "vbase_offset"
},
{
"component_value" : 16,
"kind" : "vbase_offset"
},
{
"kind" : "offset_to_top"
},
{
"kind" : "rtti",
"mangled_component_name" : "_ZTI7Vtable3"
},
{
"kind" : "complete_dtor_pointer",
"mangled_component_name" : "_ZN7Vtable3D1Ev"
},
{
"kind" : "deleting_dtor_pointer",
"mangled_component_name" : "_ZN7Vtable3D0Ev"
},
{
"mangled_component_name" : "_ZN7Vtable310function_3Ev"
},
{
"mangled_component_name" : "_ZN7Vtable316added_function_3Ev"
},
{
"kind" : "vcall_offset"
},
{
"kind" : "vcall_offset"
},
{
"component_value" : -16,
"kind" : "vcall_offset"
},
{
"component_value" : -16,
"kind" : "offset_to_top"
},
{
"kind" : "rtti",
"mangled_component_name" : "_ZTI7Vtable3"
},
{
"kind" : "complete_dtor_pointer",
"mangled_component_name" : "_ZTv0_n24_N7Vtable3D1Ev"
},
{
"kind" : "deleting_dtor_pointer",
"mangled_component_name" : "_ZTv0_n24_N7Vtable3D0Ev"
},
{
"is_pure" : true,
"mangled_component_name" : "_ZN7Vtable110function_1Ev"
},
{
"is_pure" : true,
"mangled_component_name" : "_ZN7Vtable116added_function_1Ev"
},
{
"component_value" : -32,
"kind" : "vcall_offset"
},
{
"kind" : "vcall_offset"
},
{
"kind" : "vcall_offset"
},
{
"component_value" : -32,
"kind" : "offset_to_top"
},
{
"kind" : "rtti",
"mangled_component_name" : "_ZTI7Vtable3"
},
{
"mangled_component_name" : "_ZN7Vtable210function_2Ev"
},
{
"is_pure" : true,
"mangled_component_name" : "_ZN7Vtable216added_function_2Ev"
},
{
"kind" : "complete_dtor_pointer",
"mangled_component_name" : "_ZTv0_n40_N7Vtable3D1Ev"
},
{
"kind" : "deleting_dtor_pointer",
"mangled_component_name" : "_ZTv0_n40_N7Vtable3D0Ev"
}
]
},
{
"alignment" : 4,
"fields" :

View File

@@ -19,6 +19,12 @@
"referenced_type" : "_ZTIs",
"self_type" : "_ZTIs",
"size" : 2
},
{
"linker_set_key" : "_ZTIv",
"name" : "void",
"referenced_type" : "_ZTIv",
"self_type" : "_ZTIv"
}
],
"elf_functions" :
@@ -44,7 +50,7 @@
"referenced_type" : "_ZTIR7Struct2"
}
],
"return_type" : "_ZTIR7Struct1",
"return_type" : "_ZTIR7Vtable3",
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/base.h"
}
],
@@ -68,9 +74,47 @@
"self_type" : "_ZTIR7Struct2",
"size" : 8,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/base.h"
},
{
"alignment" : 8,
"linker_set_key" : "_ZTIR7Vtable3",
"name" : "Vtable3 &",
"referenced_type" : "_ZTI7Vtable3",
"self_type" : "_ZTIR7Vtable3",
"size" : 8,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/base.h"
}
],
"pointer_types" :
[
{
"alignment" : 8,
"linker_set_key" : "_ZTIP7Vtable1",
"name" : "Vtable1 *",
"referenced_type" : "_ZTI7Vtable1",
"self_type" : "_ZTIP7Vtable1",
"size" : 8,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/base.h"
},
{
"alignment" : 8,
"linker_set_key" : "_ZTIP7Vtable2",
"name" : "Vtable2 *",
"referenced_type" : "_ZTI7Vtable2",
"self_type" : "_ZTIP7Vtable2",
"size" : 8,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/base.h"
},
{
"alignment" : 8,
"linker_set_key" : "_ZTIP7Vtable3",
"name" : "Vtable3 *",
"referenced_type" : "_ZTI7Vtable3",
"self_type" : "_ZTIP7Vtable3",
"size" : 8,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/base.h"
}
],
"pointer_types" : [],
"qualified_types" : [],
"record_types" :
[
@@ -114,6 +158,175 @@
"size" : 4,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/base.h"
},
{
"alignment" : 8,
"fields" :
[
{
"field_name" : "member_1",
"field_offset" : 64,
"referenced_type" : "_ZTIi"
}
],
"linker_set_key" : "_ZTI7Vtable1",
"name" : "Vtable1",
"referenced_type" : "_ZTI7Vtable1",
"self_type" : "_ZTI7Vtable1",
"size" : 16,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/base.h",
"vtable_components" :
[
{
"kind" : "offset_to_top"
},
{
"kind" : "rtti",
"mangled_component_name" : "_ZTI7Vtable1"
},
{
"kind" : "complete_dtor_pointer",
"mangled_component_name" : "_ZN7Vtable1D1Ev"
},
{
"kind" : "deleting_dtor_pointer",
"mangled_component_name" : "_ZN7Vtable1D0Ev"
},
{
"is_pure" : true,
"mangled_component_name" : "_ZN7Vtable110function_1Ev"
}
]
},
{
"alignment" : 8,
"fields" :
[
{
"field_name" : "member_2",
"field_offset" : 64,
"referenced_type" : "_ZTIi"
}
],
"linker_set_key" : "_ZTI7Vtable2",
"name" : "Vtable2",
"referenced_type" : "_ZTI7Vtable2",
"self_type" : "_ZTI7Vtable2",
"size" : 16,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/base.h",
"vtable_components" :
[
{
"kind" : "offset_to_top"
},
{
"kind" : "rtti",
"mangled_component_name" : "_ZTI7Vtable2"
},
{
"is_pure" : true,
"mangled_component_name" : "_ZN7Vtable210function_2Ev"
}
]
},
{
"alignment" : 8,
"base_specifiers" :
[
{
"is_virtual" : true,
"referenced_type" : "_ZTI7Vtable1"
},
{
"is_virtual" : true,
"referenced_type" : "_ZTI7Vtable2"
}
],
"fields" :
[
{
"field_name" : "member_3",
"field_offset" : 64,
"referenced_type" : "_ZTIi"
}
],
"linker_set_key" : "_ZTI7Vtable3",
"name" : "Vtable3",
"referenced_type" : "_ZTI7Vtable3",
"self_type" : "_ZTI7Vtable3",
"size" : 48,
"source_file" : "development/vndk/tools/header-checker/tests/integration/struct_extensions/include/base.h",
"vtable_components" :
[
{
"component_value" : 32,
"kind" : "vbase_offset"
},
{
"component_value" : 16,
"kind" : "vbase_offset"
},
{
"kind" : "offset_to_top"
},
{
"kind" : "rtti",
"mangled_component_name" : "_ZTI7Vtable3"
},
{
"kind" : "complete_dtor_pointer",
"mangled_component_name" : "_ZN7Vtable3D1Ev"
},
{
"kind" : "deleting_dtor_pointer",
"mangled_component_name" : "_ZN7Vtable3D0Ev"
},
{
"mangled_component_name" : "_ZN7Vtable310function_3Ev"
},
{
"kind" : "vcall_offset"
},
{
"component_value" : -16,
"kind" : "vcall_offset"
},
{
"component_value" : -16,
"kind" : "offset_to_top"
},
{
"kind" : "rtti",
"mangled_component_name" : "_ZTI7Vtable3"
},
{
"kind" : "complete_dtor_pointer",
"mangled_component_name" : "_ZTv0_n24_N7Vtable3D1Ev"
},
{
"kind" : "deleting_dtor_pointer",
"mangled_component_name" : "_ZTv0_n24_N7Vtable3D0Ev"
},
{
"is_pure" : true,
"mangled_component_name" : "_ZN7Vtable110function_1Ev"
},
{
"kind" : "vcall_offset"
},
{
"component_value" : -32,
"kind" : "offset_to_top"
},
{
"kind" : "rtti",
"mangled_component_name" : "_ZTI7Vtable3"
},
{
"is_pure" : true,
"mangled_component_name" : "_ZN7Vtable210function_2Ev"
}
]
},
{
"access" : "protected",
"alignment" : 4,