Revert^4: 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.
This reverts commit d33d318685ec4a1c9e7995c914c104ab6487513b.
Change-Id: Id00e0efb38ee1eab8d7ed5c645a7778b6b94b849
Test: phone boots
Test: m test-art
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 23c486d..01c24fc 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -3401,11 +3401,6 @@
}
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 @@
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 @@
}
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 6b2a1b9..2b59342 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -1208,7 +1208,7 @@
*/
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 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 a04cfb6..a7dad07 100644
--- a/dexlayout/compact_dex_writer.cc
+++ b/dexlayout/compact_dex_writer.cc
@@ -441,6 +441,7 @@
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 598f7df..20ebc17 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 FieldId;
class FieldItem;
class Header;
+class HiddenapiClassData;
class MapList;
class MapItem;
class MethodHandleItem;
@@ -101,6 +102,7 @@
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 @@
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 @@
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 @@
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 @@
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 947d3d5..433f100 100644
--- a/dexlayout/dex_ir_builder.cc
+++ b/dexlayout/dex_ir_builder.cc
@@ -170,6 +170,7 @@
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 @@
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::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);
@@ -908,13 +951,13 @@
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 @@
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 365171b..ef6ccf9 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -462,6 +462,59 @@
}
}
+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 @@
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 @@
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 dd2ebad..98041d3 100644
--- a/dexlayout/dex_writer.h
+++ b/dexlayout/dex_writer.h
@@ -257,6 +257,7 @@
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 8905aa3..09f0b20 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -222,6 +222,17 @@
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 @@
/*
* 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 @@
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 @@
/*
* 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 @@
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 @@
/*
* 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 @@
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 @@
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 @@
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 @@
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 2bca10d..6e006b7 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -148,7 +148,7 @@
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 @@
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 7d3ae71..bdf3ca6 100644
--- a/dexlist/dexlist.cc
+++ b/dexlist/dexlist.cc
@@ -146,7 +146,7 @@
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 21db2cf..40bca56 100644
--- a/libdexfile/dex/class_accessor-inl.h
+++ b/libdexfile/dex/class_accessor-inl.h
@@ -28,34 +28,54 @@
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 @@
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 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 @@
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 d40577f..e9c1a82 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 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 {
+ uint32_t GetAccessFlags() const {
return access_flags_;
}
- uint32_t GetAccessFlags() const {
- return HiddenApiAccessFlags::RemoveFromDex(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 @@
}
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 @@
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 @@
// 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 @@
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 @@
// 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 @@
return ptr_pos_ != nullptr;
}
+ bool HasHiddenapiClassData() const {
+ return hiddenapi_ptr_pos_ != nullptr;
+ }
+
uint32_t GetClassDefIndex() const {
return class_def_index_;
}
@@ -377,6 +382,7 @@
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 48f38ca..7ccb9c0 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -46,31 +46,6 @@
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 @@
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 @@
} 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 30d8b6d..6a52f67 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -92,7 +92,7 @@
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 @@
kDexTypeAnnotationItem = 0x2004,
kDexTypeEncodedArrayItem = 0x2005,
kDexTypeAnnotationsDirectoryItem = 0x2006,
+ kDexTypeHiddenapiClassData = 0xF000,
};
struct MapItem {
@@ -147,6 +148,8 @@
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 @@
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 @@
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 @@
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 @@
// 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 499a89b..43471c3 100644
--- a/libdexfile/dex/dex_file_verifier.cc
+++ b/libdexfile/dex/dex_file_verifier.cc
@@ -67,6 +67,7 @@
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 @@
case DexFile::kDexTypeAnnotationItem:
case DexFile::kDexTypeEncodedArrayItem:
case DexFile::kDexTypeAnnotationsDirectoryItem:
+ case DexFile::kDexTypeHiddenapiClassData:
return true;
}
return true;
@@ -1096,7 +1098,7 @@
return false;
}
if (!CheckClassDataItemField(curr_index,
- field->GetRawAccessFlags(),
+ field->GetAccessFlags(),
(*class_def)->access_flags_,
*class_type_index,
kStatic)) {
@@ -1147,7 +1149,7 @@
return false;
}
if (!CheckClassDataItemMethod(curr_index,
- method->GetRawAccessFlags(),
+ method->GetAccessFlags(),
(*class_def)->access_flags_,
*class_type_index,
method->GetCodeItemOffset(),
@@ -1555,6 +1557,107 @@
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 @@
}
break;
}
+ case DexFile::kDexTypeHiddenapiClassData: {
+ if (!CheckIntraHiddenapiClassData()) {
+ return false;
+ }
+ break;
+ }
case DexFile::kDexTypeHeaderItem:
case DexFile::kDexTypeMapList:
break;
@@ -1973,6 +2082,7 @@
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 @@
case DexFile::kDexTypeDebugInfoItem:
case DexFile::kDexTypeAnnotationItem:
case DexFile::kDexTypeEncodedArrayItem:
+ case DexFile::kDexTypeHiddenapiClassData:
break;
case DexFile::kDexTypeStringIdItem: {
if (!CheckInterStringIdItem()) {
@@ -2868,7 +2979,8 @@
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 79ddea4..a81df48 100644
--- a/libdexfile/dex/dex_file_verifier.h
+++ b/libdexfile/dex/dex_file_verifier.h
@@ -126,6 +126,7 @@
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 369615d..837056b 100644
--- a/libdexfile/dex/hidden_api_access_flags.h
+++ b/libdexfile/dex/hidden_api_access_flags.h
@@ -28,31 +28,14 @@
* 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 @@
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 @@
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 38f8455..018b141 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -42,11 +42,6 @@
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 86a36f2..358b7ba 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -628,9 +628,6 @@
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 @@
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 6745d91..41e4291 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -71,7 +71,6 @@
original_dex_file,
/* decompile_return_instruction= */ true);
}
- new_dex_file.UnhideApis();
}
static void DCheckVerifyDexFile(const art::DexFile& dex) {
@@ -106,9 +105,6 @@
// 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 e240167..0247e4e 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3277,7 +3277,9 @@
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 @@
// 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 @@
// 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 8542ad0..3e6699a 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -323,7 +323,8 @@
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 68211a1..06bea1a 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 @@
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 @@
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 @@
});
}
+ 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 @@
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 @@
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 @@
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 @@
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);
+ // 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];
- // 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);
- });
+ 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];
- boot_classpath.UpdateDexChecksums();
+ 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());
+ });
+
+ 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 @@
// 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 @@
} // 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 799546e..cdf797e 100644
--- a/tools/hiddenapi/hiddenapi_test.cc
+++ b/tools/hiddenapi/hiddenapi_test.cc
@@ -47,6 +47,7 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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());
}
}