From 393ba546711c03190e5b7f78bfb5f84df078c070 Mon Sep 17 00:00:00 2001 From: Hsin-Yi Chen Date: Tue, 4 Oct 2022 18:23:11 +0800 Subject: [PATCH] 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 --- .../src/repr/abi_diff_helpers.cpp | 11 +- .../src/repr/ir_diff_representation.cpp | 99 ++++++- .../src/repr/ir_diff_representation.h | 2 + .../struct_extensions/include/base.h | 22 +- .../struct_extensions/include/extensions.h | 29 +- .../liballowed_struct_extensions.so.lsdump | 275 +++++++++++++++++- .../arm64/libstruct_extensions.so.lsdump | 217 +++++++++++++- 7 files changed, 639 insertions(+), 16 deletions(-) diff --git a/vndk/tools/header-checker/src/repr/abi_diff_helpers.cpp b/vndk/tools/header-checker/src/repr/abi_diff_helpers.cpp index a121c3780..26586dd03 100644 --- a/vndk/tools/header-checker/src/repr/abi_diff_helpers.cpp +++ b/vndk/tools/header-checker/src/repr/abi_diff_helpers.cpp @@ -246,18 +246,13 @@ bool AbiDiffHelper::CompareVTables( old_record->GetVTableLayout().GetVTableComponents(); const std::vector &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; } diff --git a/vndk/tools/header-checker/src/repr/ir_diff_representation.cpp b/vndk/tools/header-checker/src/repr/ir_diff_representation.cpp index a55829b87..a33c60863 100644 --- a/vndk/tools/header-checker/src/repr/ir_diff_representation.cpp +++ b/vndk/tools/header-checker/src/repr/ir_diff_representation.cpp @@ -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 &old_components = + old_layout_.GetVTableComponents(); + const std::vector &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 diff --git a/vndk/tools/header-checker/src/repr/ir_diff_representation.h b/vndk/tools/header-checker/src/repr/ir_diff_representation.h index d4d4b74eb..699170876 100644 --- a/vndk/tools/header-checker/src/repr/ir_diff_representation.h +++ b/vndk/tools/header-checker/src/repr/ir_diff_representation.h @@ -102,6 +102,8 @@ class VTableLayoutDiffIR { return new_layout_; } + bool IsExtended() const; + protected: const VTableLayoutIR &old_layout_; const VTableLayoutIR &new_layout_; diff --git a/vndk/tools/header-checker/tests/integration/struct_extensions/include/base.h b/vndk/tools/header-checker/tests/integration/struct_extensions/include/base.h index c20c82334..15d3672e9 100644 --- a/vndk/tools/header-checker/tests/integration/struct_extensions/include/base.h +++ b/vndk/tools/header-checker/tests/integration/struct_extensions/include/base.h @@ -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 &); diff --git a/vndk/tools/header-checker/tests/integration/struct_extensions/include/extensions.h b/vndk/tools/header-checker/tests/integration/struct_extensions/include/extensions.h index 345687b86..481af6682 100644 --- a/vndk/tools/header-checker/tests/integration/struct_extensions/include/extensions.h +++ b/vndk/tools/header-checker/tests/integration/struct_extensions/include/extensions.h @@ -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 &); diff --git a/vndk/tools/header-checker/tests/reference_dumps/arm64/liballowed_struct_extensions.so.lsdump b/vndk/tools/header-checker/tests/reference_dumps/arm64/liballowed_struct_extensions.so.lsdump index 75ac88791..4cb6da1ff 100644 --- a/vndk/tools/header-checker/tests/reference_dumps/arm64/liballowed_struct_extensions.so.lsdump +++ b/vndk/tools/header-checker/tests/reference_dumps/arm64/liballowed_struct_extensions.so.lsdump @@ -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" : diff --git a/vndk/tools/header-checker/tests/reference_dumps/arm64/libstruct_extensions.so.lsdump b/vndk/tools/header-checker/tests/reference_dumps/arm64/libstruct_extensions.so.lsdump index 35e0b016c..3afef988d 100644 --- a/vndk/tools/header-checker/tests/reference_dumps/arm64/libstruct_extensions.so.lsdump +++ b/vndk/tools/header-checker/tests/reference_dumps/arm64/libstruct_extensions.so.lsdump @@ -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,