diff options
author | 2018-10-30 10:02:20 +0000 | |
---|---|---|
committer | 2018-10-30 10:02:20 +0000 | |
commit | f82eb6e0309a138ea11e309ad125675efc44125b (patch) | |
tree | 6a66d8dc3262e963ac5559721a231df4ea1103d7 | |
parent | dc0156dd2e76d518bf1432ae95785ddff9144b78 (diff) | |
parent | 20c765f645fa9be77e045463c5064d41211a2815 (diff) |
Merge "Revert^4: Add dex item for hiddenapi flags"
-rw-r--r-- | dex2oat/linker/oat_writer.cc | 9 | ||||
-rw-r--r-- | dexdump/dexdump.cc | 4 | ||||
-rw-r--r-- | dexlayout/compact_dex_writer.cc | 1 | ||||
-rw-r--r-- | dexlayout/dex_ir.h | 55 | ||||
-rw-r--r-- | dexlayout/dex_ir_builder.cc | 49 | ||||
-rw-r--r-- | dexlayout/dex_writer.cc | 57 | ||||
-rw-r--r-- | dexlayout/dex_writer.h | 1 | ||||
-rw-r--r-- | dexlayout/dexlayout.cc | 44 | ||||
-rw-r--r-- | dexlayout/dexlayout.h | 14 | ||||
-rw-r--r-- | dexlist/dexlist.cc | 2 | ||||
-rw-r--r-- | libdexfile/dex/class_accessor-inl.h | 80 | ||||
-rw-r--r-- | libdexfile/dex/class_accessor.h | 60 | ||||
-rw-r--r-- | libdexfile/dex/dex_file.cc | 31 | ||||
-rw-r--r-- | libdexfile/dex/dex_file.h | 44 | ||||
-rw-r--r-- | libdexfile/dex/dex_file_verifier.cc | 118 | ||||
-rw-r--r-- | libdexfile/dex/dex_file_verifier.h | 1 | ||||
-rw-r--r-- | libdexfile/dex/hidden_api_access_flags.h | 93 | ||||
-rw-r--r-- | libdexfile/dex/modifiers.h | 5 | ||||
-rw-r--r-- | oatdump/oatdump.cc | 5 | ||||
-rw-r--r-- | openjdkjvmti/fixed_up_dex_file.cc | 4 | ||||
-rw-r--r-- | runtime/class_linker.cc | 13 | ||||
-rwxr-xr-x | test/etc/default-build | 3 | ||||
-rw-r--r-- | tools/hiddenapi/hiddenapi.cc | 403 | ||||
-rw-r--r-- | tools/hiddenapi/hiddenapi_test.cc | 24 |
24 files changed, 867 insertions, 253 deletions
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 23c486d05b..01c24fc197 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -3401,11 +3401,6 @@ bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex } bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_file) { - // Open dex files and write them into `out`. - // Note that we only verify dex files which do not belong to the boot class path. - // This is because those have been processed by `hiddenapi` and would not pass - // some of the checks. No guarantees are lost, however, as `hiddenapi` verifies - // the dex files prior to processing. TimingLogger::ScopedTiming split("Dex Layout", timings_); std::string error_msg; std::string location(oat_dex_file->GetLocation()); @@ -3426,7 +3421,7 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil dex_file = dex_file_loader.Open(location, zip_entry->GetCrc32(), std::move(mem_map), - /* verify */ !GetCompilerOptions().IsBootImage(), + /* verify */ true, /* verify_checksum */ true, &error_msg); } else if (oat_dex_file->source_.IsRawFile()) { @@ -3438,7 +3433,7 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil } TimingLogger::ScopedTiming extract("Open", timings_); dex_file = dex_file_loader.OpenDex(dup_fd, location, - /* verify */ !GetCompilerOptions().IsBootImage(), + /* verify */ true, /* verify_checksum */ true, /* mmap_shared */ false, &error_msg); diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 6b2a1b9a70..2b59342158 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -1208,7 +1208,7 @@ static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags, */ static void dumpMethod(const ClassAccessor::Method& method, int i) { // Bail for anything private if export only requested. - const uint32_t flags = method.GetRawAccessFlags(); + const uint32_t flags = method.GetAccessFlags(); if (gOptions.exportsOnly && (flags & (kAccPublic | kAccProtected)) == 0) { return; } @@ -1319,7 +1319,7 @@ static void dumpMethod(const ClassAccessor::Method& method, int i) { */ static void dumpField(const ClassAccessor::Field& field, int i, const u1** data = nullptr) { // Bail for anything private if export only requested. - const uint32_t flags = field.GetRawAccessFlags(); + const uint32_t flags = field.GetAccessFlags(); if (gOptions.exportsOnly && (flags & (kAccPublic | kAccProtected)) == 0) { return; } diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc index a04cfb65f9..a7dad0762d 100644 --- a/dexlayout/compact_dex_writer.cc +++ b/dexlayout/compact_dex_writer.cc @@ -441,6 +441,7 @@ bool CompactDexWriter::Write(DexContainer* output, std::string* error_msg) { WriteTypeLists(data_stream); WriteClassDatas(data_stream); WriteStringDatas(data_stream); + WriteHiddenapiClassData(data_stream); // Write delayed id sections that depend on data sections. { diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index 598f7df778..20ebc1733c 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -21,11 +21,11 @@ #include <stdint.h> -#include <map> #include <vector> #include "base/iteration_range.h" #include "base/leb128.h" +#include "base/safe_map.h" #include "base/stl_util.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" @@ -50,6 +50,7 @@ class EncodedValue; class FieldId; class FieldItem; class Header; +class HiddenapiClassData; class MapList; class MapItem; class MethodHandleItem; @@ -101,6 +102,7 @@ class AbstractDispatcher { virtual void Dispatch(AnnotationSetItem* annotation_set_item) = 0; virtual void Dispatch(AnnotationSetRefList* annotation_set_ref_list) = 0; virtual void Dispatch(AnnotationsDirectoryItem* annotations_directory_item) = 0; + virtual void Dispatch(HiddenapiClassData* hiddenapi_class_data) = 0; virtual void Dispatch(MapList* map_list) = 0; virtual void Dispatch(MapItem* map_item) = 0; @@ -216,6 +218,7 @@ class CollectionBase { uint32_t GetOffset() const { return offset_; } void SetOffset(uint32_t new_offset) { offset_ = new_offset; } virtual uint32_t Size() const = 0; + bool Empty() const { return Size() == 0u; } private: // Start out unassigned. @@ -476,6 +479,12 @@ class Header : public Item { const CollectionVector<AnnotationsDirectoryItem>& AnnotationsDirectoryItems() const { return annotations_directory_items_; } + IndexedCollectionVector<HiddenapiClassData>& HiddenapiClassDatas() { + return hiddenapi_class_datas_; + } + const IndexedCollectionVector<HiddenapiClassData>& HiddenapiClassDatas() const { + return hiddenapi_class_datas_; + } CollectionVector<DebugInfoItem>& DebugInfoItems() { return debug_info_items_; } const CollectionVector<DebugInfoItem>& DebugInfoItems() const { return debug_info_items_; } CollectionVector<CodeItem>& CodeItems() { return code_items_; } @@ -553,6 +562,7 @@ class Header : public Item { IndexedCollectionVector<AnnotationSetItem> annotation_set_items_; IndexedCollectionVector<AnnotationSetRefList> annotation_set_ref_lists_; IndexedCollectionVector<AnnotationsDirectoryItem> annotations_directory_items_; + IndexedCollectionVector<HiddenapiClassData> hiddenapi_class_datas_; // The order of the vectors controls the layout of the output file by index order, to change the // layout just sort the vector. Note that you may only change the order of the non indexed vectors // below. Indexed vectors are accessed by indices in other places, changing the sorting order will @@ -1264,6 +1274,49 @@ class MethodHandleItem : public IndexedItem { DISALLOW_COPY_AND_ASSIGN(MethodHandleItem); }; +using HiddenapiFlagsMap = SafeMap<const Item*, uint32_t>; + +class HiddenapiClassData : public IndexedItem { + public: + HiddenapiClassData(const ClassDef* class_def, std::unique_ptr<HiddenapiFlagsMap> flags) + : class_def_(class_def), flags_(std::move(flags)) { } + ~HiddenapiClassData() override { } + + const ClassDef* GetClassDef() const { return class_def_; } + + uint32_t GetFlags(const Item* field_or_method_item) const { + return (flags_ == nullptr) ? 0u : flags_->Get(field_or_method_item); + } + + static uint32_t GetFlags(Header* header, ClassDef* class_def, const Item* field_or_method_item) { + DCHECK(header != nullptr); + DCHECK(class_def != nullptr); + return (header->HiddenapiClassDatas().Empty()) + ? 0u + : header->HiddenapiClassDatas()[class_def->GetIndex()]->GetFlags(field_or_method_item); + } + + uint32_t ItemSize() const { + uint32_t size = 0u; + bool has_non_zero_entries = false; + if (flags_ != nullptr) { + for (const auto& entry : *flags_) { + size += UnsignedLeb128Size(entry.second); + has_non_zero_entries |= (entry.second != 0u); + } + } + return has_non_zero_entries ? size : 0u; + } + + void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); } + + private: + const ClassDef* class_def_; + std::unique_ptr<HiddenapiFlagsMap> flags_; + + DISALLOW_COPY_AND_ASSIGN(HiddenapiClassData); +}; + // TODO(sehr): implement MapList. class MapList : public Item { public: diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc index 7789c5f77d..92e438c8a5 100644 --- a/dexlayout/dex_ir_builder.cc +++ b/dexlayout/dex_ir_builder.cc @@ -171,6 +171,7 @@ class BuilderMaps { void AddAnnotationsFromMapListSection(const DexFile& dex_file, uint32_t start_offset, uint32_t count); + void AddHiddenapiClassDataFromMapListSection(const DexFile& dex_file, uint32_t offset); void CheckAndSetRemainingOffsets(const DexFile& dex_file, const Options& options); @@ -409,6 +410,10 @@ void BuilderMaps::CheckAndSetRemainingOffsets(const DexFile& dex_file, const Opt case DexFile::kDexTypeAnnotationsDirectoryItem: header_->AnnotationsDirectoryItems().SetOffset(item->offset_); break; + case DexFile::kDexTypeHiddenapiClassData: + header_->HiddenapiClassDatas().SetOffset(item->offset_); + AddHiddenapiClassDataFromMapListSection(dex_file, item->offset_); + break; default: LOG(ERROR) << "Unknown map list item type."; } @@ -625,6 +630,44 @@ void BuilderMaps::AddAnnotationsFromMapListSection(const DexFile& dex_file, } } +void BuilderMaps::AddHiddenapiClassDataFromMapListSection(const DexFile& dex_file, + uint32_t offset) { + const DexFile::HiddenapiClassData* hiddenapi_class_data = + dex_file.GetHiddenapiClassDataAtOffset(offset); + DCHECK(hiddenapi_class_data == dex_file.GetHiddenapiClassData()); + + for (auto& class_def : header_->ClassDefs()) { + uint32_t index = class_def->GetIndex(); + ClassData* class_data = class_def->GetClassData(); + const uint8_t* ptr = hiddenapi_class_data->GetFlagsPointer(index); + + std::unique_ptr<HiddenapiFlagsMap> flags = nullptr; + if (ptr != nullptr) { + DCHECK(class_data != nullptr); + flags = std::make_unique<HiddenapiFlagsMap>(); + for (const dex_ir::FieldItem& field : *class_data->StaticFields()) { + flags->emplace(&field, DecodeUnsignedLeb128(&ptr)); + } + for (const dex_ir::FieldItem& field : *class_data->InstanceFields()) { + flags->emplace(&field, DecodeUnsignedLeb128(&ptr)); + } + for (const dex_ir::MethodItem& method : *class_data->DirectMethods()) { + flags->emplace(&method, DecodeUnsignedLeb128(&ptr)); + } + for (const dex_ir::MethodItem& method : *class_data->VirtualMethods()) { + flags->emplace(&method, DecodeUnsignedLeb128(&ptr)); + } + } + + CreateAndAddIndexedItem(header_->HiddenapiClassDatas(), + header_->HiddenapiClassDatas().GetOffset() + + hiddenapi_class_data->flags_offset_[index], + index, + class_def.get(), + std::move(flags)); + } +} + AnnotationItem* BuilderMaps::CreateAnnotationItem(const DexFile& dex_file, const DexFile::AnnotationItem* annotation) { const uint8_t* const start_data = reinterpret_cast<const uint8_t*>(annotation); @@ -909,13 +952,13 @@ ClassData* BuilderMaps::CreateClassData(const DexFile& dex_file, FieldItemVector* static_fields = new FieldItemVector(); for (const ClassAccessor::Field& field : accessor.GetStaticFields()) { FieldId* field_item = header_->FieldIds()[field.GetIndex()]; - uint32_t access_flags = field.GetRawAccessFlags(); + uint32_t access_flags = field.GetAccessFlags(); static_fields->emplace_back(access_flags, field_item); } FieldItemVector* instance_fields = new FieldItemVector(); for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) { FieldId* field_item = header_->FieldIds()[field.GetIndex()]; - uint32_t access_flags = field.GetRawAccessFlags(); + uint32_t access_flags = field.GetAccessFlags(); instance_fields->emplace_back(access_flags, field_item); } // Direct methods. @@ -1181,7 +1224,7 @@ void BuilderMaps::ReadEncodedValue(const DexFile& dex_file, MethodItem BuilderMaps::GenerateMethodItem(const DexFile& dex_file, const ClassAccessor::Method& method) { MethodId* method_id = header_->MethodIds()[method.GetIndex()]; - uint32_t access_flags = method.GetRawAccessFlags(); + uint32_t access_flags = method.GetAccessFlags(); const DexFile::CodeItem* disk_code_item = method.GetCodeItem(); // Temporary hack to prevent incorrectly deduping code items if they have the same offset since // they may have different debug info streams. diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc index 365171b855..ef6ccf9304 100644 --- a/dexlayout/dex_writer.cc +++ b/dexlayout/dex_writer.cc @@ -462,6 +462,59 @@ void DexWriter::WriteAnnotationsDirectories(Stream* stream) { } } +void DexWriter::WriteHiddenapiClassData(Stream* stream) { + if (header_->HiddenapiClassDatas().Empty()) { + return; + } + DCHECK_EQ(header_->HiddenapiClassDatas().Size(), header_->ClassDefs().Size()); + + stream->AlignTo(SectionAlignment(DexFile::kDexTypeHiddenapiClassData)); + const uint32_t start = stream->Tell(); + + // Compute offsets for each class def and write the header. + // data_header[0]: total size of the section + // data_header[i + 1]: offset of class def[i] from the beginning of the section, + // or zero if no data + std::vector<uint32_t> data_header(header_->ClassDefs().Size() + 1, 0); + data_header[0] = sizeof(uint32_t) * (header_->ClassDefs().Size() + 1); + for (uint32_t i = 0; i < header_->ClassDefs().Size(); ++i) { + uint32_t item_size = header_->HiddenapiClassDatas()[i]->ItemSize(); + data_header[i + 1] = item_size == 0u ? 0 : data_header[0]; + data_header[0] += item_size; + } + stream->Write(data_header.data(), sizeof(uint32_t) * data_header.size()); + + // Write class data streams. + for (uint32_t i = 0; i < header_->ClassDefs().Size(); ++i) { + dex_ir::ClassDef* class_def = header_->ClassDefs()[i]; + const auto& item = header_->HiddenapiClassDatas()[i]; + DCHECK(item->GetClassDef() == class_def); + + if (data_header[i + 1] != 0u) { + dex_ir::ClassData* class_data = class_def->GetClassData(); + DCHECK(class_data != nullptr); + DCHECK_EQ(data_header[i + 1], stream->Tell() - start); + for (const dex_ir::FieldItem& field : *class_data->StaticFields()) { + stream->WriteUleb128(item->GetFlags(&field)); + } + for (const dex_ir::FieldItem& field : *class_data->InstanceFields()) { + stream->WriteUleb128(item->GetFlags(&field)); + } + for (const dex_ir::MethodItem& method : *class_data->DirectMethods()) { + stream->WriteUleb128(item->GetFlags(&method)); + } + for (const dex_ir::MethodItem& method : *class_data->VirtualMethods()) { + stream->WriteUleb128(item->GetFlags(&method)); + } + } + } + DCHECK_EQ(stream->Tell() - start, data_header[0]); + + if (compute_offsets_ && start != stream->Tell()) { + header_->HiddenapiClassDatas().SetOffset(start); + } +} + void DexWriter::WriteDebugInfoItem(Stream* stream, dex_ir::DebugInfoItem* debug_info) { stream->AlignTo(SectionAlignment(DexFile::kDexTypeDebugInfoItem)); ProcessOffset(stream, debug_info); @@ -730,6 +783,9 @@ void DexWriter::GenerateAndWriteMapItems(Stream* stream) { queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationsDirectoryItem, header_->AnnotationsDirectoryItems().Size(), header_->AnnotationsDirectoryItems().GetOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeHiddenapiClassData, + header_->HiddenapiClassDatas().Empty() ? 0u : 1u, + header_->HiddenapiClassDatas().GetOffset())); WriteMapItems(stream, &queue); } @@ -829,6 +885,7 @@ bool DexWriter::Write(DexContainer* output, std::string* error_msg) { WriteTypeLists(stream); WriteClassDatas(stream); WriteStringDatas(stream); + WriteHiddenapiClassData(stream); // Write delayed id sections that depend on data sections. { diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h index dd2ebad26f..98041d3314 100644 --- a/dexlayout/dex_writer.h +++ b/dexlayout/dex_writer.h @@ -257,6 +257,7 @@ class DexWriter { void WriteStringDatas(Stream* stream); void WriteClassDatas(Stream* stream); void WriteMethodHandles(Stream* stream); + void WriteHiddenapiClassData(Stream* stream); void WriteMapItems(Stream* stream, MapItemQueue* queue); void GenerateAndWriteMapItems(Stream* stream); diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index 8905aa31c4..09f0b20ca1 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -222,6 +222,17 @@ static char* CreateAccessFlagStr(uint32_t flags, AccessFor for_what) { return str; } +static const char* GetHiddenapiFlagStr(uint32_t hiddenapi_flags) { + static const char* const kValue[] = { + "WHITELIST", /* 0x0 */ + "LIGHT_GREYLIST", /* 0x1 */ + "DARK_GREYLIST", /* 0x2 */ + "BLACKLIST", /* 0x3 */ + }; + DCHECK_LT(hiddenapi_flags, arraysize(kValue)); + return kValue[hiddenapi_flags]; +} + static std::string GetSignatureForProtoId(const dex_ir::ProtoId* proto) { if (proto == nullptr) { return "<no signature>"; @@ -1147,7 +1158,11 @@ void DexLayout::DumpCode(uint32_t idx, /* * Dumps a method. */ -void DexLayout::DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem* code, int i) { +void DexLayout::DumpMethod(uint32_t idx, + uint32_t flags, + uint32_t hiddenapi_flags, + const dex_ir::CodeItem* code, + int i) { // Bail for anything private if export only requested. if (options_.exports_only_ && (flags & (kAccPublic | kAccProtected)) == 0) { return; @@ -1158,12 +1173,16 @@ void DexLayout::DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem* char* type_descriptor = strdup(GetSignatureForProtoId(method_id->Proto()).c_str()); const char* back_descriptor = method_id->Class()->GetStringId()->Data(); char* access_str = CreateAccessFlagStr(flags, kAccessForMethod); + const char* hiddenapi_str = GetHiddenapiFlagStr(hiddenapi_flags); if (options_.output_format_ == kOutputPlain) { fprintf(out_file_, " #%d : (in %s)\n", i, back_descriptor); fprintf(out_file_, " name : '%s'\n", name); fprintf(out_file_, " type : '%s'\n", type_descriptor); fprintf(out_file_, " access : 0x%04x (%s)\n", flags, access_str); + if (hiddenapi_flags != 0u) { + fprintf(out_file_, " hiddenapi : 0x%04x (%s)\n", hiddenapi_flags, hiddenapi_str); + } if (code == nullptr) { fprintf(out_file_, " code : (none)\n"); } else { @@ -1257,7 +1276,11 @@ void DexLayout::DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem* /* * Dumps a static (class) field. */ -void DexLayout::DumpSField(uint32_t idx, uint32_t flags, int i, dex_ir::EncodedValue* init) { +void DexLayout::DumpSField(uint32_t idx, + uint32_t flags, + uint32_t hiddenapi_flags, + int i, + dex_ir::EncodedValue* init) { // Bail for anything private if export only requested. if (options_.exports_only_ && (flags & (kAccPublic | kAccProtected)) == 0) { return; @@ -1268,12 +1291,16 @@ void DexLayout::DumpSField(uint32_t idx, uint32_t flags, int i, dex_ir::EncodedV const char* type_descriptor = field_id->Type()->GetStringId()->Data(); const char* back_descriptor = field_id->Class()->GetStringId()->Data(); char* access_str = CreateAccessFlagStr(flags, kAccessForField); + const char* hiddenapi_str = GetHiddenapiFlagStr(hiddenapi_flags); if (options_.output_format_ == kOutputPlain) { fprintf(out_file_, " #%d : (in %s)\n", i, back_descriptor); fprintf(out_file_, " name : '%s'\n", name); fprintf(out_file_, " type : '%s'\n", type_descriptor); fprintf(out_file_, " access : 0x%04x (%s)\n", flags, access_str); + if (hiddenapi_flags != 0u) { + fprintf(out_file_, " hiddenapi : 0x%04x (%s)\n", hiddenapi_flags, hiddenapi_str); + } if (init != nullptr) { fputs(" value : ", out_file_); DumpEncodedValue(init); @@ -1304,8 +1331,11 @@ void DexLayout::DumpSField(uint32_t idx, uint32_t flags, int i, dex_ir::EncodedV /* * Dumps an instance field. */ -void DexLayout::DumpIField(uint32_t idx, uint32_t flags, int i) { - DumpSField(idx, flags, i, nullptr); +void DexLayout::DumpIField(uint32_t idx, + uint32_t flags, + uint32_t hiddenapi_flags, + int i) { + DumpSField(idx, flags, hiddenapi_flags, i, nullptr); } /* @@ -1431,6 +1461,7 @@ void DexLayout::DumpClass(int idx, char** last_package) { for (uint32_t i = 0; i < static_fields->size(); i++) { DumpSField((*static_fields)[i].GetFieldId()->GetIndex(), (*static_fields)[i].GetAccessFlags(), + dex_ir::HiddenapiClassData::GetFlags(header_, class_def, &(*static_fields)[i]), i, i < encoded_values_size ? (*encoded_values)[i].get() : nullptr); } // for @@ -1447,6 +1478,7 @@ void DexLayout::DumpClass(int idx, char** last_package) { for (uint32_t i = 0; i < instance_fields->size(); i++) { DumpIField((*instance_fields)[i].GetFieldId()->GetIndex(), (*instance_fields)[i].GetAccessFlags(), + dex_ir::HiddenapiClassData::GetFlags(header_, class_def, &(*instance_fields)[i]), i); } // for } @@ -1462,8 +1494,9 @@ void DexLayout::DumpClass(int idx, char** last_package) { for (uint32_t i = 0; i < direct_methods->size(); i++) { DumpMethod((*direct_methods)[i].GetMethodId()->GetIndex(), (*direct_methods)[i].GetAccessFlags(), + dex_ir::HiddenapiClassData::GetFlags(header_, class_def, &(*direct_methods)[i]), (*direct_methods)[i].GetCodeItem(), - i); + i); } // for } } @@ -1478,6 +1511,7 @@ void DexLayout::DumpClass(int idx, char** last_package) { for (uint32_t i = 0; i < virtual_methods->size(); i++) { DumpMethod((*virtual_methods)[i].GetMethodId()->GetIndex(), (*virtual_methods)[i].GetAccessFlags(), + dex_ir::HiddenapiClassData::GetFlags(header_, class_def, &(*virtual_methods)[i]), (*virtual_methods)[i].GetCodeItem(), i); } // for diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h index 2bca10ddfb..6e006b7686 100644 --- a/dexlayout/dexlayout.h +++ b/dexlayout/dexlayout.h @@ -148,7 +148,7 @@ class DexLayout { void DumpEncodedAnnotation(dex_ir::EncodedAnnotation* annotation); void DumpEncodedValue(const dex_ir::EncodedValue* data); void DumpFileHeader(); - void DumpIField(uint32_t idx, uint32_t flags, int i); + void DumpIField(uint32_t idx, uint32_t flags, uint32_t hiddenapi_flags, int i); void DumpInstruction(const dex_ir::CodeItem* code, uint32_t code_offset, uint32_t insn_idx, @@ -156,9 +156,17 @@ class DexLayout { const Instruction* dec_insn); void DumpInterface(const dex_ir::TypeId* type_item, int i); void DumpLocalInfo(const dex_ir::CodeItem* code); - void DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem* code, int i); + void DumpMethod(uint32_t idx, + uint32_t flags, + uint32_t hiddenapi_flags, + const dex_ir::CodeItem* code, + int i); void DumpPositionInfo(const dex_ir::CodeItem* code); - void DumpSField(uint32_t idx, uint32_t flags, int i, dex_ir::EncodedValue* init); + void DumpSField(uint32_t idx, + uint32_t flags, + uint32_t hiddenapi_flags, + int i, + dex_ir::EncodedValue* init); void DumpDexFile(); void LayoutClassDefsAndClassData(const DexFile* dex_file); diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index 7d3ae71d5f..bdf3ca6f11 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -146,7 +146,7 @@ void dumpClass(const DexFile* pDexFile, u4 idx) { dumpMethod(pDexFile, fileName, method.GetIndex(), - method.GetRawAccessFlags(), + method.GetAccessFlags(), method.GetCodeItem(), method.GetCodeItemOffset()); } diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h index 21db2cf2be..40bca564ae 100644 --- a/libdexfile/dex/class_accessor-inl.h +++ b/libdexfile/dex/class_accessor-inl.h @@ -28,34 +28,54 @@ namespace art { inline ClassAccessor::ClassAccessor(const ClassIteratorData& data) : ClassAccessor(data.dex_file_, data.class_def_idx_) {} -inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def) - : ClassAccessor(dex_file, dex_file.GetIndexForClassDef(class_def)) {} +inline ClassAccessor::ClassAccessor(const DexFile& dex_file, + const DexFile::ClassDef& class_def, + bool parse_hiddenapi_class_data) + : ClassAccessor(dex_file, + dex_file.GetClassData(class_def), + dex_file.GetIndexForClassDef(class_def), + parse_hiddenapi_class_data) {} inline ClassAccessor::ClassAccessor(const DexFile& dex_file, uint32_t class_def_index) - : ClassAccessor(dex_file, - dex_file.GetClassData(dex_file.GetClassDef(class_def_index)), - class_def_index) {} + : ClassAccessor(dex_file, dex_file.GetClassDef(class_def_index)) {} inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const uint8_t* class_data, - uint32_t class_def_index) + uint32_t class_def_index, + bool parse_hiddenapi_class_data) : dex_file_(dex_file), class_def_index_(class_def_index), ptr_pos_(class_data), + hiddenapi_ptr_pos_(nullptr), num_static_fields_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), num_instance_fields_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), num_direct_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u), - num_virtual_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {} + num_virtual_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) { + if (parse_hiddenapi_class_data && class_def_index != DexFile::kDexNoIndex32) { + const DexFile::HiddenapiClassData* hiddenapi_class_data = dex_file.GetHiddenapiClassData(); + if (hiddenapi_class_data != nullptr) { + hiddenapi_ptr_pos_ = hiddenapi_class_data->GetFlagsPointer(class_def_index); + } + } +} inline void ClassAccessor::Method::Read() { index_ += DecodeUnsignedLeb128(&ptr_pos_); access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); code_off_ = DecodeUnsignedLeb128(&ptr_pos_); + if (hiddenapi_ptr_pos_ != nullptr) { + hiddenapi_flags_ = DecodeUnsignedLeb128(&hiddenapi_ptr_pos_); + DCHECK(HiddenApiAccessFlags::AreValidFlags(hiddenapi_flags_)); + } } inline void ClassAccessor::Field::Read() { index_ += DecodeUnsignedLeb128(&ptr_pos_); access_flags_ = DecodeUnsignedLeb128(&ptr_pos_); + if (hiddenapi_ptr_pos_ != nullptr) { + hiddenapi_flags_ = DecodeUnsignedLeb128(&hiddenapi_ptr_pos_); + DCHECK(HiddenApiAccessFlags::AreValidFlags(hiddenapi_flags_)); + } } template <typename DataType, typename Visitor> @@ -78,12 +98,12 @@ inline void ClassAccessor::VisitFieldsAndMethods( const InstanceFieldVisitor& instance_field_visitor, const DirectMethodVisitor& direct_method_visitor, const VirtualMethodVisitor& virtual_method_visitor) const { - Field field(dex_file_, ptr_pos_); + Field field(dex_file_, ptr_pos_, hiddenapi_ptr_pos_); VisitMembers(num_static_fields_, static_field_visitor, &field); field.NextSection(); VisitMembers(num_instance_fields_, instance_field_visitor, &field); - Method method(dex_file_, field.ptr_pos_, /*is_static_or_direct*/ true); + Method method(dex_file_, field.ptr_pos_, field.hiddenapi_ptr_pos_, /*is_static_or_direct*/ true); VisitMembers(num_direct_methods_, direct_method_visitor, &method); method.NextSection(); VisitMembers(num_virtual_methods_, virtual_method_visitor, &method); @@ -131,19 +151,43 @@ inline const DexFile::CodeItem* ClassAccessor::Method::GetCodeItem() const { inline IterationRange<ClassAccessor::DataIterator<ClassAccessor::Field>> ClassAccessor::GetFieldsInternal(size_t count) const { - return { DataIterator<Field>(dex_file_, 0u, num_static_fields_, count, ptr_pos_), - DataIterator<Field>(dex_file_, count, num_static_fields_, count, ptr_pos_) }; + return { + DataIterator<Field>(dex_file_, + 0u, + num_static_fields_, + count, + ptr_pos_, + hiddenapi_ptr_pos_), + DataIterator<Field>(dex_file_, + count, + num_static_fields_, + count, + // The following pointers are bogus but unused in the `end` iterator. + ptr_pos_, + hiddenapi_ptr_pos_) }; } // Return an iteration range for the first <count> methods. inline IterationRange<ClassAccessor::DataIterator<ClassAccessor::Method>> ClassAccessor::GetMethodsInternal(size_t count) const { // Skip over the fields. - Field field(dex_file_, ptr_pos_); + Field field(dex_file_, ptr_pos_, hiddenapi_ptr_pos_); VisitMembers(NumFields(), VoidFunctor(), &field); // Return the iterator pair. - return { DataIterator<Method>(dex_file_, 0u, num_direct_methods_, count, field.ptr_pos_), - DataIterator<Method>(dex_file_, count, num_direct_methods_, count, field.ptr_pos_) }; + return { + DataIterator<Method>(dex_file_, + 0u, + num_direct_methods_, + count, + field.ptr_pos_, + field.hiddenapi_ptr_pos_), + DataIterator<Method>(dex_file_, + count, + num_direct_methods_, + count, + // The following pointers are bogus but unused in the `end` iterator. + field.ptr_pos_, + field.hiddenapi_ptr_pos_) }; } inline IterationRange<ClassAccessor::DataIterator<ClassAccessor::Field>> ClassAccessor::GetFields() @@ -181,14 +225,6 @@ inline IterationRange<ClassAccessor::DataIterator<ClassAccessor::Method>> return { std::next(methods.begin(), NumDirectMethods()), methods.end() }; } -inline void ClassAccessor::Field::UnHideAccessFlags() const { - DexFile::UnHideAccessFlags(const_cast<uint8_t*>(ptr_pos_), GetAccessFlags(), /*is_method*/ false); -} - -inline void ClassAccessor::Method::UnHideAccessFlags() const { - DexFile::UnHideAccessFlags(const_cast<uint8_t*>(ptr_pos_), GetAccessFlags(), /*is_method*/ true); -} - inline dex::TypeIndex ClassAccessor::GetClassIdx() const { return dex_file_.GetClassDef(class_def_index_).class_idx_; } diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h index d40577f31f..e9c1a82c54 100644 --- a/libdexfile/dex/class_accessor.h +++ b/libdexfile/dex/class_accessor.h @@ -20,7 +20,6 @@ #include "base/utils.h" #include "code_item_accessors.h" #include "dex_file.h" -#include "hidden_api_access_flags.h" #include "invoke_type.h" #include "method_reference.h" #include "modifiers.h" @@ -35,22 +34,20 @@ class ClassAccessor { class BaseItem { public: explicit BaseItem(const DexFile& dex_file, - const uint8_t* ptr_pos) : dex_file_(dex_file), ptr_pos_(ptr_pos) {} + const uint8_t* ptr_pos, + const uint8_t* hiddenapi_ptr_pos) + : dex_file_(dex_file), ptr_pos_(ptr_pos), hiddenapi_ptr_pos_(hiddenapi_ptr_pos) {} uint32_t GetIndex() const { return index_; } - uint32_t GetRawAccessFlags() const { - return access_flags_; - } - uint32_t GetAccessFlags() const { - return HiddenApiAccessFlags::RemoveFromDex(access_flags_); + return access_flags_; } - HiddenApiAccessFlags::ApiList DecodeHiddenAccessFlags() const { - return HiddenApiAccessFlags::DecodeFromDex(access_flags_); + uint32_t GetHiddenapiFlags() const { + return hiddenapi_flags_; } bool IsFinal() const { @@ -66,19 +63,21 @@ class ClassAccessor { } bool MemberIsNative() const { - return GetRawAccessFlags() & kAccNative; + return GetAccessFlags() & kAccNative; } bool MemberIsFinal() const { - return GetRawAccessFlags() & kAccFinal; + return GetAccessFlags() & kAccFinal; } protected: // Internal data pointer for reading. const DexFile& dex_file_; const uint8_t* ptr_pos_ = nullptr; + const uint8_t* hiddenapi_ptr_pos_ = nullptr; uint32_t index_ = 0u; uint32_t access_flags_ = 0u; + uint32_t hiddenapi_flags_ = 0u; }; // A decoded version of the method of a class_data_item. @@ -107,14 +106,13 @@ class ClassAccessor { return is_static_or_direct_; } - // Unhide the hidden API access flags at the iterator position. TODO: Deprecate. - void UnHideAccessFlags() const; - private: - explicit Method(const DexFile& dex_file, - const uint8_t* ptr_pos, - bool is_static_or_direct = true) - : BaseItem(dex_file, ptr_pos), is_static_or_direct_(is_static_or_direct) {} + Method(const DexFile& dex_file, + const uint8_t* ptr_pos, + const uint8_t* hiddenapi_ptr_pos = nullptr, + bool is_static_or_direct = true) + : BaseItem(dex_file, ptr_pos, hiddenapi_ptr_pos), + is_static_or_direct_(is_static_or_direct) {} void Read(); @@ -150,16 +148,15 @@ class ClassAccessor { // A decoded version of the field of a class_data_item. class Field : public BaseItem { public: - explicit Field(const DexFile& dex_file, - const uint8_t* ptr_pos) : BaseItem(dex_file, ptr_pos) {} + Field(const DexFile& dex_file, + const uint8_t* ptr_pos, + const uint8_t* hiddenapi_ptr_pos = nullptr) + : BaseItem(dex_file, ptr_pos, hiddenapi_ptr_pos) {} bool IsStatic() const { return is_static_; } - // Unhide the hidden API access flags at the iterator position. TODO: Deprecate. - void UnHideAccessFlags() const; - private: void Read(); @@ -185,8 +182,9 @@ class ClassAccessor { uint32_t position, uint32_t partition_pos, uint32_t iterator_end, - const uint8_t* ptr_pos) - : data_(dex_file, ptr_pos), + const uint8_t* ptr_pos, + const uint8_t* hiddenapi_ptr_pos) + : data_(dex_file, ptr_pos, hiddenapi_ptr_pos), position_(position), partition_pos_(partition_pos), iterator_end_(iterator_end) { @@ -268,13 +266,16 @@ class ClassAccessor { // Not explicit specifically for range-based loops. ALWAYS_INLINE ClassAccessor(const ClassIteratorData& data); - ALWAYS_INLINE ClassAccessor(const DexFile& dex_file, const DexFile::ClassDef& class_def); + ALWAYS_INLINE ClassAccessor(const DexFile& dex_file, + const DexFile::ClassDef& class_def, + bool parse_hiddenapi_class_data = false); ALWAYS_INLINE ClassAccessor(const DexFile& dex_file, uint32_t class_def_index); ClassAccessor(const DexFile& dex_file, const uint8_t* class_data, - uint32_t class_def_index = DexFile::kDexNoIndex32); + uint32_t class_def_index = DexFile::kDexNoIndex32, + bool parse_hiddenapi_class_data = false); // Return the code item for a method. const DexFile::CodeItem* GetCodeItem(const Method& method) const; @@ -353,6 +354,10 @@ class ClassAccessor { return ptr_pos_ != nullptr; } + bool HasHiddenapiClassData() const { + return hiddenapi_ptr_pos_ != nullptr; + } + uint32_t GetClassDefIndex() const { return class_def_index_; } @@ -377,6 +382,7 @@ class ClassAccessor { const DexFile& dex_file_; const uint32_t class_def_index_; const uint8_t* ptr_pos_ = nullptr; // Pointer into stream of class_data_item. + const uint8_t* hiddenapi_ptr_pos_ = nullptr; // Pointer into stream of hiddenapi_metadata. const uint32_t num_static_fields_ = 0u; const uint32_t num_instance_fields_ = 0u; const uint32_t num_direct_methods_ = 0u; diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index 48f38ca8a1..7ccb9c0bad 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -46,31 +46,6 @@ static_assert(std::is_trivially_copyable<dex::StringIndex>::value, "StringIndex static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong"); static_assert(std::is_trivially_copyable<dex::TypeIndex>::value, "TypeIndex not trivial"); -void DexFile::UnHideAccessFlags(uint8_t* data_ptr, - uint32_t new_access_flags, - bool is_method) { - // Go back 1 uleb to start. - data_ptr = ReverseSearchUnsignedLeb128(data_ptr); - if (is_method) { - // Methods have another uleb field before the access flags - data_ptr = ReverseSearchUnsignedLeb128(data_ptr); - } - DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data_ptr)), - new_access_flags); - UpdateUnsignedLeb128(data_ptr, new_access_flags); -} - -void DexFile::UnhideApis() const { - for (ClassAccessor accessor : GetClasses()) { - for (const ClassAccessor::Field& field : accessor.GetFields()) { - field.UnHideAccessFlags(); - } - for (const ClassAccessor::Method& method : accessor.GetMethods()) { - method.UnHideAccessFlags(); - } - } -} - uint32_t DexFile::CalculateChecksum() const { return CalculateChecksum(Begin(), Size()); } @@ -130,6 +105,7 @@ DexFile::DexFile(const uint8_t* base, num_method_handles_(0), call_site_ids_(nullptr), num_call_site_ids_(0), + hiddenapi_class_data_(nullptr), oat_dex_file_(oat_dex_file), container_(std::move(container)), is_compact_dex_(is_compact_dex), @@ -205,6 +181,11 @@ void DexFile::InitializeSectionsFromMapList() { } else if (map_item.type_ == kDexTypeCallSiteIdItem) { call_site_ids_ = reinterpret_cast<const CallSiteIdItem*>(Begin() + map_item.offset_); num_call_site_ids_ = map_item.size_; + } else if (map_item.type_ == kDexTypeHiddenapiClassData) { + hiddenapi_class_data_ = GetHiddenapiClassDataAtOffset(map_item.offset_); + } else { + // Pointers to other sections are not necessary to retain in the DexFile struct. + // Other items have pointers directly into their data. } } } diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index 30d8b6d9bf..6a52f67646 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -92,7 +92,7 @@ class DexFile { uint32_t endian_tag_ = 0; uint32_t link_size_ = 0; // unused uint32_t link_off_ = 0; // unused - uint32_t map_off_ = 0; // unused + uint32_t map_off_ = 0; // map list offset from data_off_ uint32_t string_ids_size_ = 0; // number of StringIds uint32_t string_ids_off_ = 0; // file offset of StringIds array uint32_t type_ids_size_ = 0; // number of TypeIds, we don't support more than 65535 @@ -134,6 +134,7 @@ class DexFile { kDexTypeAnnotationItem = 0x2004, kDexTypeEncodedArrayItem = 0x2005, kDexTypeAnnotationsDirectoryItem = 0x2006, + kDexTypeHiddenapiClassData = 0xF000, }; struct MapItem { @@ -147,6 +148,8 @@ class DexFile { uint32_t size_; MapItem list_[1]; + size_t Size() const { return sizeof(uint32_t) + (size_ * sizeof(MapItem)); } + private: DISALLOW_COPY_AND_ASSIGN(MapList); }; @@ -419,6 +422,27 @@ class DexFile { DISALLOW_COPY_AND_ASSIGN(AnnotationItem); }; + struct HiddenapiClassData { + uint32_t size_; // total size of the item + uint32_t flags_offset_[1]; // array of offsets from the beginning of this item, + // indexed by class def index + + // Returns a pointer to the beginning of a uleb128-stream of hiddenapi + // flags for a class def of given index. Values are in the same order + // as fields/methods in the class data. Returns null if the class does + // not have class data. + const uint8_t* GetFlagsPointer(uint32_t class_def_idx) const { + if (flags_offset_[class_def_idx] == 0) { + return nullptr; + } else { + return reinterpret_cast<const uint8_t*>(this) + flags_offset_[class_def_idx]; + } + } + + private: + DISALLOW_COPY_AND_ASSIGN(HiddenapiClassData); + }; + enum AnnotationResultStyle { // private kAllObjects, kPrimitivesOrObjects, @@ -837,6 +861,14 @@ class DexFile { return DataPointer<AnnotationItem>(offset); } + ALWAYS_INLINE const HiddenapiClassData* GetHiddenapiClassDataAtOffset(uint32_t offset) const { + return DataPointer<HiddenapiClassData>(offset); + } + + ALWAYS_INLINE const HiddenapiClassData* GetHiddenapiClassData() const { + return hiddenapi_class_data_; + } + const AnnotationItem* GetAnnotationItem(const AnnotationSetItem* set_item, uint32_t index) const { DCHECK_LE(index, set_item->size_); return GetAnnotationItemAtOffset(set_item->entries_[index]); @@ -992,12 +1024,6 @@ class DexFile { return container_.get(); } - // Changes the dex class data pointed to by data_ptr it to not have any hiddenapi flags. - static void UnHideAccessFlags(uint8_t* data_ptr, uint32_t new_access_flags, bool is_method); - - // Iterate dex classes and remove hiddenapi flags in fields and methods. - void UnhideApis() const; - IterationRange<ClassIterator> GetClasses() const; template <typename Visitor> @@ -1080,6 +1106,10 @@ class DexFile { // Number of elements in the call sites list. size_t num_call_site_ids_; + // Points to the base of the hiddenapi class data item_, or nullptr if the dex + // file does not have one. + const HiddenapiClassData* hiddenapi_class_data_; + // If this dex file was loaded from an oat file, oat_dex_file_ contains a // pointer to the OatDexFile it was loaded from. Otherwise oat_dex_file_ is // null. diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index 499a89b2ab..43471c3116 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -67,6 +67,7 @@ static uint32_t MapTypeToBitMask(DexFile::MapItemType map_item_type) { case DexFile::kDexTypeAnnotationItem: return 1 << 17; case DexFile::kDexTypeEncodedArrayItem: return 1 << 18; case DexFile::kDexTypeAnnotationsDirectoryItem: return 1 << 19; + case DexFile::kDexTypeHiddenapiClassData: return 1 << 20; } return 0; } @@ -94,6 +95,7 @@ static bool IsDataSectionType(DexFile::MapItemType map_item_type) { case DexFile::kDexTypeAnnotationItem: case DexFile::kDexTypeEncodedArrayItem: case DexFile::kDexTypeAnnotationsDirectoryItem: + case DexFile::kDexTypeHiddenapiClassData: return true; } return true; @@ -1096,7 +1098,7 @@ bool DexFileVerifier::CheckIntraClassDataItemFields(size_t count, return false; } if (!CheckClassDataItemField(curr_index, - field->GetRawAccessFlags(), + field->GetAccessFlags(), (*class_def)->access_flags_, *class_type_index, kStatic)) { @@ -1147,7 +1149,7 @@ bool DexFileVerifier::CheckIntraClassDataItemMethods(ClassAccessor::Method* meth return false; } if (!CheckClassDataItemMethod(curr_index, - method->GetRawAccessFlags(), + method->GetAccessFlags(), (*class_def)->access_flags_, *class_type_index, method->GetCodeItemOffset(), @@ -1555,6 +1557,107 @@ bool DexFileVerifier::CheckIntraAnnotationItem() { return true; } +bool DexFileVerifier::CheckIntraHiddenapiClassData() { + const DexFile::HiddenapiClassData* item = + reinterpret_cast<const DexFile::HiddenapiClassData*>(ptr_); + + // Check expected header size. + uint32_t num_header_elems = dex_file_->NumClassDefs() + 1; + uint32_t elem_size = sizeof(uint32_t); + uint32_t header_size = num_header_elems * elem_size; + if (!CheckListSize(item, num_header_elems, elem_size, "hiddenapi class data section header")) { + return false; + } + + // Check total size. + if (!CheckListSize(item, item->size_, 1u, "hiddenapi class data section")) { + return false; + } + + // Check that total size can fit header. + if (item->size_ < header_size) { + ErrorStringPrintf( + "Hiddenapi class data too short to store header (%u < %u)", item->size_, header_size); + return false; + } + + const uint8_t* data_end = ptr_ + item->size_; + ptr_ += header_size; + + // Check offsets for each class def. + for (uint32_t i = 0; i < dex_file_->NumClassDefs(); ++i) { + const DexFile::ClassDef& class_def = dex_file_->GetClassDef(i); + const uint8_t* class_data = dex_file_->GetClassData(class_def); + uint32_t offset = item->flags_offset_[i]; + + if (offset == 0) { + continue; + } + + // Check that class defs with no class data do not have any hiddenapi class data. + if (class_data == nullptr) { + ErrorStringPrintf( + "Hiddenapi class data offset not zero for class def %u with no class data", i); + return false; + } + + // Check that the offset is within the section. + if (offset > item->size_) { + ErrorStringPrintf( + "Hiddenapi class data offset out of section bounds (%u > %u) for class def %u", + offset, item->size_, i); + return false; + } + + // Check that the offset matches current pointer position. We do not allow + // offsets into already parsed data, or gaps between class def data. + uint32_t ptr_offset = ptr_ - reinterpret_cast<const uint8_t*>(item); + if (offset != ptr_offset) { + ErrorStringPrintf( + "Hiddenapi class data unexpected offset (%u != %u) for class def %u", + offset, ptr_offset, i); + return false; + } + + // Parse a uleb128 value for each field and method of this class. + bool failure = false; + auto fn_member = [&](const ClassAccessor::BaseItem& member, const char* member_type) { + if (failure) { + return; + } + uint32_t decoded_flags; + if (!DecodeUnsignedLeb128Checked(&ptr_, data_end, &decoded_flags)) { + ErrorStringPrintf("Hiddenapi class data value out of bounds (%p > %p) for %s %i", + ptr_, data_end, member_type, member.GetIndex()); + failure = true; + return; + } + if (!HiddenApiAccessFlags::AreValidFlags(decoded_flags)) { + ErrorStringPrintf("Hiddenapi class data flags invalid (%u) for %s %i", + decoded_flags, member_type, member.GetIndex()); + failure = true; + return; + } + }; + auto fn_field = [&](const ClassAccessor::Field& field) { fn_member(field, "field"); }; + auto fn_method = [&](const ClassAccessor::Method& method) { fn_member(method, "method"); }; + ClassAccessor accessor(*dex_file_, class_data); + accessor.VisitFieldsAndMethods(fn_field, fn_field, fn_method, fn_method); + if (failure) { + return false; + } + } + + if (ptr_ != data_end) { + ErrorStringPrintf("Hiddenapi class data wrong reported size (%u != %u)", + static_cast<uint32_t>(ptr_ - reinterpret_cast<const uint8_t*>(item)), + item->size_); + return false; + } + + return true; +} + bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() { const DexFile::AnnotationsDirectoryItem* item = reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr_); @@ -1769,6 +1872,12 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c } break; } + case DexFile::kDexTypeHiddenapiClassData: { + if (!CheckIntraHiddenapiClassData()) { + return false; + } + break; + } case DexFile::kDexTypeHeaderItem: case DexFile::kDexTypeMapList: break; @@ -1973,6 +2082,7 @@ bool DexFileVerifier::CheckIntraSection() { CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationItem) CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeEncodedArrayItem) CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationsDirectoryItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeHiddenapiClassData) #undef CHECK_INTRA_DATA_SECTION_CASE } @@ -2736,6 +2846,7 @@ bool DexFileVerifier::CheckInterSectionIterate(size_t offset, case DexFile::kDexTypeDebugInfoItem: case DexFile::kDexTypeAnnotationItem: case DexFile::kDexTypeEncodedArrayItem: + case DexFile::kDexTypeHiddenapiClassData: break; case DexFile::kDexTypeStringIdItem: { if (!CheckInterStringIdItem()) { @@ -2868,7 +2979,8 @@ bool DexFileVerifier::CheckInterSection() { case DexFile::kDexTypeAnnotationSetRefList: case DexFile::kDexTypeAnnotationSetItem: case DexFile::kDexTypeClassDataItem: - case DexFile::kDexTypeAnnotationsDirectoryItem: { + case DexFile::kDexTypeAnnotationsDirectoryItem: + case DexFile::kDexTypeHiddenapiClassData: { if (!CheckInterSectionIterate(section_offset, section_count, type)) { return false; } diff --git a/libdexfile/dex/dex_file_verifier.h b/libdexfile/dex/dex_file_verifier.h index 79ddea43d4..a81df48398 100644 --- a/libdexfile/dex/dex_file_verifier.h +++ b/libdexfile/dex/dex_file_verifier.h @@ -126,6 +126,7 @@ class DexFileVerifier { bool CheckIntraDebugInfoItem(); bool CheckIntraAnnotationItem(); bool CheckIntraAnnotationsDirectoryItem(); + bool CheckIntraHiddenapiClassData(); template <DexFile::MapItemType kType> bool CheckIntraSectionIterate(size_t offset, uint32_t count); diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h index 369615d678..837056be80 100644 --- a/libdexfile/dex/hidden_api_access_flags.h +++ b/libdexfile/dex/hidden_api_access_flags.h @@ -28,31 +28,14 @@ namespace art { * of information on whether the given class member should be hidden from apps * and under what circumstances. * - * The encoding is different inside DexFile, where we are concerned with size, - * and at runtime where we want to optimize for speed of access. The class - * provides helper functions to decode/encode both of them. + * Two bits are encoded for each class member in the HiddenapiClassData item, + * stored in a stream of uleb128-encoded values for each ClassDef item. + * The two bits correspond to values in the ApiList enum below. * - * Encoding in DexFile - * =================== - * - * First bit is encoded as inversion of visibility flags (public/private/protected). - * At most one can be set for any given class member. If two or three are set, - * this is interpreted as the first bit being set and actual visibility flags - * being the complement of the encoded flags. - * - * Second bit is either encoded as bit 5 for fields and non-native methods, where - * it carries no other meaning. If a method is native (bit 8 set), bit 9 is used. - * - * Bits were selected so that they never increase the length of unsigned LEB-128 - * encoding of the access flags. - * - * Encoding at runtime - * =================== - * - * Two bits are set aside in the uint32_t access flags in the intrinsics ordinal - * space (thus intrinsics need to be special-cased). These are two consecutive - * bits and they are directly used to store the integer value of the ApiList - * enum values. + * At runtime, two bits are set aside in the uint32_t access flags in the + * intrinsics ordinal space (thus intrinsics need to be special-cased). These are + * two consecutive bits and they are directly used to store the integer value of + * the ApiList enum values. * */ class HiddenApiAccessFlags { @@ -65,27 +48,6 @@ class HiddenApiAccessFlags { kNoList, }; - static ALWAYS_INLINE ApiList DecodeFromDex(uint32_t dex_access_flags) { - DexHiddenAccessFlags flags(dex_access_flags); - uint32_t int_value = (flags.IsFirstBitSet() ? 1 : 0) + (flags.IsSecondBitSet() ? 2 : 0); - return static_cast<ApiList>(int_value); - } - - static ALWAYS_INLINE uint32_t RemoveFromDex(uint32_t dex_access_flags) { - DexHiddenAccessFlags flags(dex_access_flags); - flags.SetFirstBit(false); - flags.SetSecondBit(false); - return flags.GetEncoding(); - } - - static ALWAYS_INLINE uint32_t EncodeForDex(uint32_t dex_access_flags, ApiList value) { - DexHiddenAccessFlags flags(RemoveFromDex(dex_access_flags)); - uint32_t int_value = static_cast<uint32_t>(value); - flags.SetFirstBit((int_value & 1) != 0); - flags.SetSecondBit((int_value & 2) != 0); - return flags.GetEncoding(); - } - static ALWAYS_INLINE ApiList DecodeFromRuntime(uint32_t runtime_access_flags) { // This is used in the fast path, only DCHECK here. DCHECK_EQ(runtime_access_flags & kAccIntrinsic, 0u); @@ -103,47 +65,14 @@ class HiddenApiAccessFlags { return runtime_access_flags | hidden_api_flags; } + static ALWAYS_INLINE bool AreValidFlags(uint32_t flags) { + return flags <= static_cast<uint32_t>(kBlacklist); + } + private: static const int kAccFlagsShift = CTZ(kAccHiddenApiBits); static_assert(IsPowerOfTwo((kAccHiddenApiBits >> kAccFlagsShift) + 1), "kAccHiddenApiBits are not continuous"); - - struct DexHiddenAccessFlags { - explicit DexHiddenAccessFlags(uint32_t access_flags) : access_flags_(access_flags) {} - - ALWAYS_INLINE uint32_t GetSecondFlag() { - return ((access_flags_ & kAccNative) != 0) ? kAccDexHiddenBitNative : kAccDexHiddenBit; - } - - ALWAYS_INLINE bool IsFirstBitSet() { - static_assert(IsPowerOfTwo(0u), "Following statement checks if *at most* one bit is set"); - return !IsPowerOfTwo(access_flags_ & kAccVisibilityFlags); - } - - ALWAYS_INLINE void SetFirstBit(bool value) { - if (IsFirstBitSet() != value) { - access_flags_ ^= kAccVisibilityFlags; - } - } - - ALWAYS_INLINE bool IsSecondBitSet() { - return (access_flags_ & GetSecondFlag()) != 0; - } - - ALWAYS_INLINE void SetSecondBit(bool value) { - if (value) { - access_flags_ |= GetSecondFlag(); - } else { - access_flags_ &= ~GetSecondFlag(); - } - } - - ALWAYS_INLINE uint32_t GetEncoding() const { - return access_flags_; - } - - uint32_t access_flags_; - }; }; inline std::ostream& operator<<(std::ostream& os, HiddenApiAccessFlags::ApiList value) { diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h index 38f8455b64..018b1419b1 100644 --- a/libdexfile/dex/modifiers.h +++ b/libdexfile/dex/modifiers.h @@ -42,11 +42,6 @@ static constexpr uint32_t kAccEnum = 0x4000; // class, field, ic (1.5) static constexpr uint32_t kAccJavaFlagsMask = 0xffff; // bits set from Java sources (low 16) -// The following flags are used to insert hidden API access flags into boot class path dex files. -// They are decoded by ClassAccessor and removed from the access flags before used by the runtime. -static constexpr uint32_t kAccDexHiddenBit = 0x00000020; // field, method (not native) -static constexpr uint32_t kAccDexHiddenBitNative = 0x00000200; // method (native) - static constexpr uint32_t kAccConstructor = 0x00010000; // method (dex only) <(cl)init> static constexpr uint32_t kAccDeclaredSynchronized = 0x00020000; // method (dex only) static constexpr uint32_t kAccClassIsProxy = 0x00040000; // class (dex only) diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 86a36f292b..358b7ba287 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -628,9 +628,6 @@ class OatDumper { CHECK(oat_dex_file != nullptr); CHECK(vdex_dex_file != nullptr); - // Remove hiddenapis - vdex_dex_file->UnhideApis(); - // If a CompactDex file is detected within a Vdex container, DexLayout is used to convert // back to a StandardDex file. Since the converted DexFile will most likely not reproduce // the original input Dex file, the `update_checksum_` option is used to recompute the @@ -1078,7 +1075,7 @@ class OatDumper { dex_file, method.GetIndex(), method.GetCodeItem(), - method.GetRawAccessFlags(), + method.GetAccessFlags(), &addr_found)) { success = false; } diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index 6745d91d53..41e4291edd 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -71,7 +71,6 @@ static void DoDexUnquicken(const art::DexFile& new_dex_file, original_dex_file, /* decompile_return_instruction= */ true); } - new_dex_file.UnhideApis(); } static void DCheckVerifyDexFile(const art::DexFile& dex) { @@ -106,9 +105,6 @@ std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& origi // this before unquickening. art::Options options; options.compact_dex_level_ = art::CompactDexLevel::kCompactDexLevelNone; - // Never verify the output since hidden API flags may cause the dex file verifier to fail. - // See b/74063493 - options.verify_output_ = false; // Add a filter to only include the class that has the matching descriptor. static constexpr bool kFilterByDescriptor = true; if (kFilterByDescriptor) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index e240167ba8..0247e4e076 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3277,7 +3277,9 @@ void ClassLinker::LoadClass(Thread* self, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, Handle<mirror::Class> klass) { - ClassAccessor accessor(dex_file, dex_class_def); + ClassAccessor accessor(dex_file, + dex_class_def, + /* parse_hiddenapi_class_data= */ klass->IsBootStrapClassLoaded()); if (!accessor.HasClassData()) { return; } @@ -3393,8 +3395,8 @@ void ClassLinker::LoadField(const ClassAccessor::Field& field, // also set its runtime hidden API access flags. uint32_t access_flags = field.GetAccessFlags(); if (klass->IsBootStrapClassLoaded()) { - access_flags = - HiddenApiAccessFlags::EncodeForRuntime(access_flags, field.DecodeHiddenAccessFlags()); + access_flags = HiddenApiAccessFlags::EncodeForRuntime( + access_flags, static_cast<HiddenApiAccessFlags::ApiList>(field.GetHiddenapiFlags())); } dst->SetAccessFlags(access_flags); } @@ -3415,10 +3417,9 @@ void ClassLinker::LoadMethod(const DexFile& dex_file, // Get access flags from the DexFile. If this is a boot class path class, // also set its runtime hidden API access flags. uint32_t access_flags = method.GetAccessFlags(); - if (klass->IsBootStrapClassLoaded()) { - access_flags = - HiddenApiAccessFlags::EncodeForRuntime(access_flags, method.DecodeHiddenAccessFlags()); + access_flags = HiddenApiAccessFlags::EncodeForRuntime( + access_flags, static_cast<HiddenApiAccessFlags::ApiList>(method.GetHiddenapiFlags())); } if (UNLIKELY(strcmp("finalize", method_name) == 0)) { diff --git a/test/etc/default-build b/test/etc/default-build index 8542ad0e92..3e6699ad4e 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -323,7 +323,8 @@ function make_dexmerge() { function make_hiddenapi() { local args=( "encode" ) while [[ $# -gt 0 ]]; do - args+=("--dex=$1") + args+=("--input-dex=$1") + args+=("--output-dex=$1") shift done if [ -f api-light-greylist.txt ]; then diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc index 68211a1641..06bea1a3bf 100644 --- a/tools/hiddenapi/hiddenapi.cc +++ b/tools/hiddenapi/hiddenapi.cc @@ -22,6 +22,8 @@ #include "android-base/stringprintf.h" #include "android-base/strings.h" +#include "base/bit_utils.h" +#include "base/stl_util.h" #include "base/mem_map.h" #include "base/os.h" #include "base/unix_file/fd_file.h" @@ -66,8 +68,9 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError("Usage: hiddenapi [command_name] [options]..."); UsageError(""); UsageError(" Command \"encode\": encode API list membership in boot dex files"); - UsageError(" --dex=<filename>: dex file which belongs to boot class path,"); - UsageError(" the file will be overwritten"); + UsageError(" --input-dex=<filename>: dex file which belongs to boot class path"); + UsageError(" --output-dex=<filename>: file to write encoded dex into"); + UsageError(" input and output dex files are paired in order of appearance"); UsageError(""); UsageError(" --light-greylist=<filename>:"); UsageError(" --dark-greylist=<filename>:"); @@ -147,30 +150,6 @@ class DexMember { inline const DexClass& GetDeclaringClass() const { return klass_; } - // Sets hidden bits in access flags and writes them back into the DEX in memory. - // Note that this will not update the cached data of the class accessor - // until it iterates over this item again and therefore will fail a CHECK if - // it is called multiple times on the same DexMember. - void SetHidden(HiddenApiAccessFlags::ApiList value) const { - const uint32_t old_flags = item_.GetRawAccessFlags(); - const uint32_t new_flags = HiddenApiAccessFlags::EncodeForDex(old_flags, value); - CHECK_EQ(UnsignedLeb128Size(new_flags), UnsignedLeb128Size(old_flags)); - - // Locate the LEB128-encoded access flags in class data. - // `ptr` initially points to the next ClassData item. We iterate backwards - // until we hit the terminating byte of the previous Leb128 value. - const uint8_t* ptr = item_.GetDataPointer(); - if (IsMethod()) { - ptr = ReverseSearchUnsignedLeb128(ptr); - DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), GetMethod().GetCodeItemOffset()); - } - ptr = ReverseSearchUnsignedLeb128(ptr); - DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), old_flags); - - // Overwrite the access flags. - UpdateUnsignedLeb128(const_cast<uint8_t*>(ptr), new_flags); - } - inline bool IsMethod() const { return is_method_; } inline bool IsVirtualMethod() const { return IsMethod() && !GetMethod().IsStaticOrDirect(); } inline bool IsConstructor() const { return IsMethod() && HasAccessFlags(kAccConstructor); } @@ -262,6 +241,10 @@ class ClassPath final { }); } + std::vector<const DexFile*> GetDexFiles() const { + return MakeNonOwningPointerVector(dex_files_); + } + void UpdateDexChecksums() { for (auto& dex_file : dex_files_) { // Obtain a writeable pointer to the dex header. @@ -559,6 +542,316 @@ class Hierarchy final { std::map<std::string, HierarchyClass> classes_; }; +// Builder of dex section containing hiddenapi flags. +class HiddenapiClassDataBuilder final { + public: + explicit HiddenapiClassDataBuilder(const DexFile& dex_file) + : num_classdefs_(dex_file.NumClassDefs()), + next_class_def_idx_(0u), + class_def_has_non_zero_flags_(false), + dex_file_has_non_zero_flags_(false), + data_(sizeof(uint32_t) * (num_classdefs_ + 1), 0u) { + *GetSizeField() = GetCurrentDataSize(); + } + + // Notify the builder that new flags for the next class def + // will be written now. The builder records the current offset + // into the header. + void BeginClassDef(uint32_t idx) { + CHECK_EQ(next_class_def_idx_, idx); + CHECK_LT(idx, num_classdefs_); + GetOffsetArray()[idx] = GetCurrentDataSize(); + class_def_has_non_zero_flags_ = false; + } + + // Notify the builder that all flags for this class def have been + // written. The builder updates the total size of the data struct + // and may set offset for class def in header to zero if no data + // has been written. + void EndClassDef(uint32_t idx) { + CHECK_EQ(next_class_def_idx_, idx); + CHECK_LT(idx, num_classdefs_); + + ++next_class_def_idx_; + + if (!class_def_has_non_zero_flags_) { + // No need to store flags for this class. Remove the written flags + // and set offset in header to zero. + data_.resize(GetOffsetArray()[idx]); + GetOffsetArray()[idx] = 0u; + } + + dex_file_has_non_zero_flags_ |= class_def_has_non_zero_flags_; + + if (idx == num_classdefs_ - 1) { + if (dex_file_has_non_zero_flags_) { + // This was the last class def and we have generated non-zero hiddenapi + // flags. Update total size in the header. + *GetSizeField() = GetCurrentDataSize(); + } else { + // This was the last class def and we have not generated any non-zero + // hiddenapi flags. Clear all the data. + data_.clear(); + } + } + } + + // Append flags at the end of the data struct. This should be called + // between BeginClassDef and EndClassDef in the order of appearance of + // fields/methods in the class data stream. + void WriteFlags(uint32_t flags) { + EncodeUnsignedLeb128(&data_, flags); + class_def_has_non_zero_flags_ |= (flags != 0u); + } + + // Return backing data, assuming that all flags have been written. + const std::vector<uint8_t>& GetData() const { + CHECK_EQ(next_class_def_idx_, num_classdefs_) << "Incomplete data"; + return data_; + } + + private: + // Returns pointer to the size field in the header of this dex section. + uint32_t* GetSizeField() { + // Assume malloc() aligns allocated memory to at least uint32_t. + CHECK(IsAligned<sizeof(uint32_t)>(data_.data())); + return reinterpret_cast<uint32_t*>(data_.data()); + } + + // Returns pointer to array of offsets (indexed by class def indices) in the + // header of this dex section. + uint32_t* GetOffsetArray() { return &GetSizeField()[1]; } + uint32_t GetCurrentDataSize() const { return data_.size(); } + + // Number of class defs in this dex file. + const uint32_t num_classdefs_; + + // Next expected class def index. + uint32_t next_class_def_idx_; + + // Whether non-zero flags have been encountered for this class def. + bool class_def_has_non_zero_flags_; + + // Whether any non-zero flags have been encountered for this dex file. + bool dex_file_has_non_zero_flags_; + + // Vector containing the data of the built data structure. + std::vector<uint8_t> data_; +}; + +// Edits a dex file, inserting a new HiddenapiClassData section. +class DexFileEditor final { + public: + DexFileEditor(const DexFile& old_dex, const std::vector<uint8_t>& hiddenapi_class_data) + : old_dex_(old_dex), + hiddenapi_class_data_(hiddenapi_class_data), + loaded_dex_header_(nullptr), + loaded_dex_maplist_(nullptr) {} + + // Copies dex file into a backing data vector, appends the given HiddenapiClassData + // and updates the MapList. + void Encode() { + // We do not support non-standard dex encodings, e.g. compact dex. + CHECK(old_dex_.IsStandardDexFile()); + + // If there are no data to append, copy the old dex file and return. + if (hiddenapi_class_data_.empty()) { + AllocateMemory(old_dex_.Size()); + Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false); + return; + } + + // Find the old MapList, find its size. + const DexFile::MapList* old_map = old_dex_.GetMapList(); + CHECK_LT(old_map->size_, std::numeric_limits<uint32_t>::max()); + + // Compute the size of the new dex file. We append the HiddenapiClassData, + // one MapItem and possibly some padding to align the new MapList. + CHECK(IsAligned<kMapListAlignment>(old_dex_.Size())) + << "End of input dex file is not 4-byte aligned, possibly because its MapList is not " + << "at the end of the file."; + size_t size_delta = + RoundUp(hiddenapi_class_data_.size(), kMapListAlignment) + sizeof(DexFile::MapItem); + size_t new_size = old_dex_.Size() + size_delta; + AllocateMemory(new_size); + + // Copy the old dex file into the backing data vector. Load the copied + // dex file to obtain pointers to its header and MapList. + Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false); + ReloadDex(/* verify= */ false); + + // Truncate the new dex file before the old MapList. This assumes that + // the MapList is the last entry in the dex file. This is currently true + // for our tooling. + // TODO: Implement the general case by zero-ing the old MapList (turning + // it into padding. + RemoveOldMapList(); + + // Append HiddenapiClassData. + size_t payload_offset = AppendHiddenapiClassData(); + + // Wrute new MapList with an entry for HiddenapiClassData. + CreateMapListWithNewItem(payload_offset); + + // Check that the pre-computed size matches the actual size. + CHECK_EQ(offset_, new_size); + + // Reload to all data structures. + ReloadDex(/* verify= */ false); + + // Update the dex checksum. + UpdateChecksum(); + + // Run DexFileVerifier on the new dex file as a CHECK. + ReloadDex(/* verify= */ true); + } + + // Writes the edited dex file into a file. + void WriteTo(const std::string& path) { + CHECK(!data_.empty()); + std::ofstream ofs(path.c_str(), std::ofstream::out | std::ofstream::binary); + ofs.write(reinterpret_cast<const char*>(data_.data()), data_.size()); + ofs.flush(); + CHECK(ofs.good()); + ofs.close(); + } + + private: + static constexpr size_t kMapListAlignment = 4u; + static constexpr size_t kHiddenapiClassDataAlignment = 4u; + + void ReloadDex(bool verify) { + std::string error_msg; + DexFileLoader loader; + loaded_dex_ = loader.Open( + data_.data(), + data_.size(), + "test_location", + old_dex_.GetLocationChecksum(), + /* oat_dex_file= */ nullptr, + /* verify= */ verify, + /* verify_checksum= */ verify, + &error_msg); + if (loaded_dex_.get() == nullptr) { + LOG(FATAL) << "Failed to load edited dex file: " << error_msg; + UNREACHABLE(); + } + + // Load the location of header and map list before we start editing the file. + loaded_dex_header_ = const_cast<DexFile::Header*>(&loaded_dex_->GetHeader()); + loaded_dex_maplist_ = const_cast<DexFile::MapList*>(loaded_dex_->GetMapList()); + } + + DexFile::Header& GetHeader() const { + CHECK(loaded_dex_header_ != nullptr); + return *loaded_dex_header_; + } + + DexFile::MapList& GetMapList() const { + CHECK(loaded_dex_maplist_ != nullptr); + return *loaded_dex_maplist_; + } + + void AllocateMemory(size_t total_size) { + data_.clear(); + data_.resize(total_size); + CHECK(IsAligned<kMapListAlignment>(data_.data())); + CHECK(IsAligned<kHiddenapiClassDataAlignment>(data_.data())); + offset_ = 0; + } + + uint8_t* GetCurrentDataPtr() { + return data_.data() + offset_; + } + + void UpdateDataSize(off_t delta, bool update_header) { + offset_ += delta; + if (update_header) { + DexFile::Header& header = GetHeader(); + header.file_size_ += delta; + header.data_size_ += delta; + } + } + + template<typename T> + T* Append(const T* src, size_t len, bool update_header = true) { + CHECK_LE(offset_ + len, data_.size()); + uint8_t* dst = GetCurrentDataPtr(); + memcpy(dst, src, len); + UpdateDataSize(len, update_header); + return reinterpret_cast<T*>(dst); + } + + void InsertPadding(size_t alignment) { + size_t len = RoundUp(offset_, alignment) - offset_; + std::vector<uint8_t> padding(len, 0); + Append(padding.data(), padding.size()); + } + + void RemoveOldMapList() { + size_t map_size = GetMapList().Size(); + uint8_t* map_start = reinterpret_cast<uint8_t*>(&GetMapList()); + CHECK_EQ(map_start + map_size, GetCurrentDataPtr()) << "MapList not at the end of dex file"; + UpdateDataSize(-static_cast<off_t>(map_size), /* update_header= */ true); + CHECK_EQ(map_start, GetCurrentDataPtr()); + loaded_dex_maplist_ = nullptr; // do not use this map list any more + } + + void CreateMapListWithNewItem(size_t payload_offset) { + InsertPadding(/* alignment= */ kMapListAlignment); + + size_t new_map_offset = offset_; + DexFile::MapList* map = Append(old_dex_.GetMapList(), old_dex_.GetMapList()->Size()); + + // Check last map entry is a pointer to itself. + DexFile::MapItem& old_item = map->list_[map->size_ - 1]; + CHECK(old_item.type_ == DexFile::kDexTypeMapList); + CHECK_EQ(old_item.size_, 1u); + CHECK_EQ(old_item.offset_, GetHeader().map_off_); + + // Create a new MapItem entry with new MapList details. + DexFile::MapItem new_item; + new_item.type_ = old_item.type_; + new_item.size_ = old_item.size_; + new_item.offset_ = new_map_offset; + + // Update pointer in the header. + GetHeader().map_off_ = new_map_offset; + + // Append a new MapItem and return its pointer. + map->size_++; + Append(&new_item, sizeof(DexFile::MapItem)); + + // Change penultimate entry to point to metadata. + old_item.type_ = DexFile::kDexTypeHiddenapiClassData; + old_item.size_ = 1u; // there is only one section + old_item.offset_ = payload_offset; + } + + size_t AppendHiddenapiClassData() { + size_t payload_offset = offset_; + CHECK_EQ(kMapListAlignment, kHiddenapiClassDataAlignment); + CHECK(IsAligned<kHiddenapiClassDataAlignment>(payload_offset)) + << "Should not need to align the section, previous data was already aligned"; + Append(hiddenapi_class_data_.data(), hiddenapi_class_data_.size()); + return payload_offset; + } + + void UpdateChecksum() { + GetHeader().checksum_ = loaded_dex_->CalculateChecksum(); + } + + const DexFile& old_dex_; + const std::vector<uint8_t>& hiddenapi_class_data_; + + std::vector<uint8_t> data_; + size_t offset_; + + std::unique_ptr<const DexFile> loaded_dex_; + DexFile::Header* loaded_dex_header_; + DexFile::MapList* loaded_dex_maplist_; +}; + class HiddenApi final { public: HiddenApi() {} @@ -590,8 +883,10 @@ class HiddenApi final { if (command == "encode") { for (int i = 1; i < argc; ++i) { const StringPiece option(argv[i]); - if (option.starts_with("--dex=")) { - boot_dex_paths_.push_back(option.substr(strlen("--dex=")).ToString()); + if (option.starts_with("--input-dex=")) { + boot_dex_paths_.push_back(option.substr(strlen("--input-dex=")).ToString()); + } else if (option.starts_with("--output-dex=")) { + output_dex_paths_.push_back(option.substr(strlen("--output-dex=")).ToString()); } else if (option.starts_with("--light-greylist=")) { light_greylist_path_ = option.substr(strlen("--light-greylist=")).ToString(); } else if (option.starts_with("--dark-greylist=")) { @@ -630,7 +925,9 @@ class HiddenApi final { void EncodeAccessFlags() { if (boot_dex_paths_.empty()) { - Usage("No boot DEX files specified"); + Usage("No input DEX files specified"); + } else if (output_dex_paths_.size() != boot_dex_paths_.size()) { + Usage("Number of input DEX files does not match number of output DEX files"); } // Load dex signatures. @@ -639,16 +936,41 @@ class HiddenApi final { OpenApiFile(dark_greylist_path_, api_list, HiddenApiAccessFlags::kDarkGreylist); OpenApiFile(blacklist_path_, api_list, HiddenApiAccessFlags::kBlacklist); - // Open all dex files. - ClassPath boot_classpath(boot_dex_paths_, /* open_writable= */ true); - - // Set access flags of all members. - boot_classpath.ForEachDexMember([&api_list](const DexMember& boot_member) { - auto it = api_list.find(boot_member.GetApiEntry()); - boot_member.SetHidden(it == api_list.end() ? HiddenApiAccessFlags::kWhitelist : it->second); - }); + // Iterate over input dex files and insert HiddenapiClassData sections. + for (size_t i = 0; i < boot_dex_paths_.size(); ++i) { + const std::string& input_path = boot_dex_paths_[i]; + const std::string& output_path = output_dex_paths_[i]; + + ClassPath boot_classpath({ input_path }, /* open_writable= */ false); + std::vector<const DexFile*> input_dex_files = boot_classpath.GetDexFiles(); + CHECK_EQ(input_dex_files.size(), 1u); + const DexFile& input_dex = *input_dex_files[0]; + + HiddenapiClassDataBuilder builder(input_dex); + boot_classpath.ForEachDexClass([&api_list, &builder](const DexClass& boot_class) { + builder.BeginClassDef(boot_class.GetClassDefIndex()); + if (boot_class.GetData() != nullptr) { + auto fn_shared = [&](const DexMember& boot_member) { + // TODO: Load whitelist and CHECK that entry was found. + auto it = api_list.find(boot_member.GetApiEntry()); + builder.WriteFlags( + (it == api_list.end()) ? HiddenApiAccessFlags::kWhitelist : it->second); + }; + auto fn_field = [&](const ClassAccessor::Field& boot_field) { + fn_shared(DexMember(boot_class, boot_field)); + }; + auto fn_method = [&](const ClassAccessor::Method& boot_method) { + fn_shared(DexMember(boot_class, boot_method)); + }; + boot_class.VisitFieldsAndMethods(fn_field, fn_field, fn_method, fn_method); + } + builder.EndClassDef(boot_class.GetClassDefIndex()); + }); - boot_classpath.UpdateDexChecksums(); + DexFileEditor dex_editor(input_dex, builder.GetData()); + dex_editor.Encode(); + dex_editor.WriteTo(output_path); + } } void OpenApiFile(const std::string& path, @@ -748,6 +1070,9 @@ class HiddenApi final { // Paths to DEX files which should be processed. std::vector<std::string> boot_dex_paths_; + // Output paths where modified DEX files should be written. + std::vector<std::string> output_dex_paths_; + // Set of public API stub classpaths. Each classpath is formed by a list // of DEX/APK files in the order they appear on the classpath. std::vector<std::vector<std::string>> stub_classpaths_; @@ -765,6 +1090,8 @@ class HiddenApi final { } // namespace art int main(int argc, char** argv) { + art::original_argc = argc; + art::original_argv = argv; android::base::InitLogging(argv); art::MemMap::Init(); art::HiddenApi().Run(argc, argv); diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc index 799546e07b..cdf797e598 100644 --- a/tools/hiddenapi/hiddenapi_test.cc +++ b/tools/hiddenapi/hiddenapi_test.cc @@ -47,6 +47,7 @@ class HiddenApiTest : public CommonRuntimeTest { const std::vector<std::string>& extra_args, ScratchFile* out_dex) { std::string error; + ScratchFile in_dex; std::unique_ptr<ZipArchive> jar( ZipArchive::Open(GetTestDexFileName("HiddenApi").c_str(), &error)); if (jar == nullptr) { @@ -58,7 +59,7 @@ class HiddenApiTest : public CommonRuntimeTest { LOG(FATAL) << "Could not find classes.dex in test file " << GetTestDexFileName("HiddenApi") << ": " << error; UNREACHABLE(); - } else if (!jar_classes_dex->ExtractToFile(*out_dex->GetFile(), &error)) { + } else if (!jar_classes_dex->ExtractToFile(*in_dex.GetFile(), &error)) { LOG(FATAL) << "Could not extract classes.dex from test file " << GetTestDexFileName("HiddenApi") << ": " << error; UNREACHABLE(); @@ -68,7 +69,8 @@ class HiddenApiTest : public CommonRuntimeTest { argv_str.push_back(GetHiddenApiCmd()); argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end()); argv_str.push_back("encode"); - argv_str.push_back("--dex=" + out_dex->GetFilename()); + argv_str.push_back("--input-dex=" + in_dex.GetFilename()); + argv_str.push_back("--output-dex=" + out_dex->GetFilename()); argv_str.push_back("--light-greylist=" + light_greylist.GetFilename()); argv_str.push_back("--dark-greylist=" + dark_greylist.GetFilename()); argv_str.push_back("--blacklist=" + blacklist.GetFilename()); @@ -92,7 +94,7 @@ class HiddenApiTest : public CommonRuntimeTest { } std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex( - fd.Release(), /* location= */ file.GetFilename(), /* verify= */ false, + fd.Release(), /* location= */ file.GetFilename(), /* verify= */ true, /* verify_checksum= */ true, /* mmap_shared= */ false, &error_msg)); if (dex_file.get() == nullptr) { LOG(FATAL) << "Open failed for '" << file.GetFilename() << "' " << error_msg; @@ -126,16 +128,20 @@ class HiddenApiTest : public CommonRuntimeTest { uint32_t expected_visibility, const DexFile::ClassDef& class_def, const DexFile& dex_file) { - ClassAccessor accessor(dex_file, class_def); + ClassAccessor accessor(dex_file, class_def, /* parse hiddenapi flags */ true); CHECK(accessor.HasClassData()) << "Class " << accessor.GetDescriptor() << " has no data"; + if (!accessor.HasHiddenapiClassData()) { + return HiddenApiAccessFlags::kWhitelist; + } + for (const ClassAccessor::Field& field : accessor.GetFields()) { const DexFile::FieldId& fid = dex_file.GetFieldId(field.GetIndex()); if (strcmp(name, dex_file.GetFieldName(fid)) == 0) { const uint32_t actual_visibility = field.GetAccessFlags() & kAccVisibilityFlags; CHECK_EQ(actual_visibility, expected_visibility) << "Field " << name << " in class " << accessor.GetDescriptor(); - return field.DecodeHiddenAccessFlags(); + return static_cast<HiddenApiAccessFlags::ApiList>(field.GetHiddenapiFlags()); } } @@ -149,9 +155,13 @@ class HiddenApiTest : public CommonRuntimeTest { bool expected_native, const DexFile::ClassDef& class_def, const DexFile& dex_file) { - ClassAccessor accessor(dex_file, class_def); + ClassAccessor accessor(dex_file, class_def, /* parse hiddenapi flags */ true); CHECK(accessor.HasClassData()) << "Class " << accessor.GetDescriptor() << " has no data"; + if (!accessor.HasHiddenapiClassData()) { + return HiddenApiAccessFlags::kWhitelist; + } + for (const ClassAccessor::Method& method : accessor.GetMethods()) { const DexFile::MethodId& mid = dex_file.GetMethodId(method.GetIndex()); if (strcmp(name, dex_file.GetMethodName(mid)) == 0) { @@ -160,7 +170,7 @@ class HiddenApiTest : public CommonRuntimeTest { const uint32_t actual_visibility = method.GetAccessFlags() & kAccVisibilityFlags; CHECK_EQ(actual_visibility, expected_visibility) << "Method " << name << " in class " << accessor.GetDescriptor(); - return method.DecodeHiddenAccessFlags(); + return static_cast<HiddenApiAccessFlags::ApiList>(method.GetHiddenapiFlags()); } } |