diff options
author | 2018-09-10 09:14:30 +0100 | |
---|---|---|
committer | 2018-10-23 14:51:01 +0100 | |
commit | 0dbc363f32d075017e1c4fb5e17715e3f12d0157 (patch) | |
tree | 950dfed748f63863db6b95d7b854940171b544c6 | |
parent | 62ada4cd2c9a7ef5d1ab4c538d660f8a6a7f7571 (diff) |
Add dex item for hiddenapi flags
Move hiddenapi access flags to own data section so as to:
(a) increase amount of information stored per method/field
(b) use encoding which can be supported long-term.
The dex item is implemented as:
- array of offsets indexed by class def index
- streams of uleb-128 encoded flags.
Offsets in array point to the beginning of the flags stream
for the given class def. Flags are encoded in the same order
as fields and methods are encoded in class data. Zero offset
means that the class either does not have class data, or all
of its flags are zero.
The patch updates:
(a) libdexfile with data structure declarations and accessor
methods,
(b) hiddenapi tool to create the new item from hiddenapi lists
and insert it into the given dex file,
(c) dexlayout to copy the flags into compact dex,
(d) dex file verifier to verify the item.
It also removes skipping of verification for boot class path
dex files as those now pass DexFileVerifier, and removes the
need for removing the flags for JVMTI.
The size increase is 450 KB.
Test: phone boots
Test: m test-art
Change-Id: Idec301db540cf164fccc97136d1df4abb8f758bd
-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 | 56 | ||||
-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 | 1 | ||||
-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, 866 insertions, 250 deletions
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index acd49d5b45..750619d7fe 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -3400,11 +3400,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()); @@ -3425,7 +3420,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()) { @@ -3437,7 +3432,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 947d3d5297..c38e563f18 100644 --- a/dexlayout/dex_ir_builder.cc +++ b/dexlayout/dex_ir_builder.cc @@ -170,6 +170,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); @@ -408,6 +409,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."; } @@ -624,6 +629,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 (size_t i = 0; i < dex_file.NumClassDefs(); ++i) { + ClassDef* class_def = header_->ClassDefs()[i]; + ClassData* class_data = class_def->GetClassData(); + const uint8_t* ptr = hiddenapi_class_data->GetFlagsPointer(i); + + 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_[i], + i, + class_def, + 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); @@ -908,13 +951,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. @@ -1180,7 +1223,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..929ed405a6 100644 --- a/dexlayout/dex_writer.cc +++ b/dexlayout/dex_writer.cc @@ -462,6 +462,58 @@ void DexWriter::WriteAnnotationsDirectories(Stream* stream) { } } +void DexWriter::WriteHiddenapiClassData(Stream* stream) { + if (header_->HiddenapiClassDatas().Empty()) { + return; + } + DCHECK_EQ(header_->HiddenapiClassDatas().Size(), header_->ClassDefs().Size()); + + 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 +782,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 +884,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 adb6a54d8a..d3e4633510 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 467542df61..a82fa3fc13 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..eb93fc9c25 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) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 7549c04b6f..bc86841f1a 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3241,7 +3241,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; } @@ -3357,8 +3359,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); } @@ -3379,10 +3381,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()); } } |