Rewrite libdexfile C API to follow NDK guidelines better.
Bug: 123517037
Bug: 145347927
Test: test-art-host-gtest-art_libdexfile_support_tests64
Test: test-art-host-gtest-art_libdexfile_external_tests64
Change-Id: I9673872585456937d2f54abb63e3dbd8aad4e9ad
diff --git a/libdexfile/external/dex_file_ext.cc b/libdexfile/external/dex_file_ext.cc
index 302e188..7c0bf2d 100644
--- a/libdexfile/external/dex_file_ext.cc
+++ b/libdexfile/external/dex_file_ext.cc
@@ -43,37 +43,36 @@
extern "C" {
-// Wraps DexFile to add the caching needed by the external interface. This is
-// what gets passed over as ExtDexFile*.
-struct ExtDexFile {
- struct MethodCacheEntry {
- uint32_t offset; // Offset relative to the start of the dex file header.
- uint32_t len;
- uint32_t index; // Method index.
- };
+struct ADexFile_Method {
+ ADexFile* adex;
+ uint32_t index;
+ size_t offset;
+ size_t size;
+};
- public:
- std::unique_ptr<const art::DexFile> dex_file_;
- explicit ExtDexFile(std::unique_ptr<const art::DexFile>&& dex_file)
+// Opaque implementation of ADexFile for the C interface.
+struct ADexFile {
+ explicit ADexFile(std::unique_ptr<const art::DexFile> dex_file)
: dex_file_(std::move(dex_file)) {}
- bool GetMethodDefIndex(uint32_t dex_offset, uint32_t* index, uint32_t* addr, uint32_t* size) {
+ inline bool FindMethod(uint32_t dex_offset, /*out*/ ADexFile_Method* result) {
uint32_t class_def_index;
if (GetClassDefIndex(dex_offset, &class_def_index)) {
art::ClassAccessor accessor(*dex_file_, class_def_index);
-
for (const art::ClassAccessor::Method& method : accessor.GetMethods()) {
art::CodeItemInstructionAccessor code = method.GetInstructions();
if (!code.HasCodeItem()) {
continue;
}
-
- uint32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
- uint32_t len = code.InsnsSizeInBytes();
- if (offset <= dex_offset && dex_offset < offset + len) {
- *index = method.GetIndex();
- *addr = offset;
- *size = len;
+ size_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
+ size_t size = code.InsnsSizeInBytes();
+ if (offset <= dex_offset && dex_offset < offset + size) {
+ *result = ADexFile_Method {
+ .adex = this,
+ .index = method.GetIndex(),
+ .offset = offset,
+ .size = size,
+ };
return true;
}
}
@@ -81,40 +80,43 @@
return false;
}
- private:
- bool GetClassDefIndex(uint32_t dex_offset, uint32_t* class_def_index) {
+ void CreateClassCache() {
+ // Create binary search table with (end_dex_offset, class_def_index) entries.
+ // That is, we don't assume that dex code of given class is consecutive.
+ std::deque<std::pair<uint32_t, uint32_t>> cache;
+ for (art::ClassAccessor accessor : dex_file_->GetClasses()) {
+ for (const art::ClassAccessor::Method& method : accessor.GetMethods()) {
+ art::CodeItemInstructionAccessor code = method.GetInstructions();
+ if (code.HasCodeItem()) {
+ int32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
+ DCHECK_NE(offset, 0);
+ cache.emplace_back(offset + code.InsnsSizeInBytes(), accessor.GetClassDefIndex());
+ }
+ }
+ }
+ std::sort(cache.begin(), cache.end());
+
+ // If two consecutive methods belong to same class, we can merge them.
+ // This tends to reduce the number of entries (used memory) by 10x.
+ size_t num_entries = cache.size();
+ if (cache.size() > 1) {
+ for (auto it = std::next(cache.begin()); it != cache.end(); it++) {
+ if (std::prev(it)->second == it->second) {
+ std::prev(it)->first = 0; // Clear entry with lower end_dex_offset (mark to remove).
+ num_entries--;
+ }
+ }
+ }
+
+ // The cache is immutable now. Store it as continuous vector to save space.
+ class_cache_.reserve(num_entries);
+ auto pred = [](auto it) { return it.first != 0; }; // Entries to copy (not cleared above).
+ std::copy_if(cache.begin(), cache.end(), std::back_inserter(class_cache_), pred);
+ }
+
+ inline bool GetClassDefIndex(uint32_t dex_offset, uint32_t* class_def_index) {
if (class_cache_.empty()) {
- // Create binary search table with (end_dex_offset, class_def_index) entries.
- // That is, we don't assume that dex code of given class is consecutive.
- std::deque<std::pair<uint32_t, uint32_t>> cache;
- for (art::ClassAccessor accessor : dex_file_->GetClasses()) {
- for (const art::ClassAccessor::Method& method : accessor.GetMethods()) {
- art::CodeItemInstructionAccessor code = method.GetInstructions();
- if (code.HasCodeItem()) {
- int32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
- DCHECK_NE(offset, 0);
- cache.emplace_back(offset + code.InsnsSizeInBytes(), accessor.GetClassDefIndex());
- }
- }
- }
- std::sort(cache.begin(), cache.end());
-
- // If two consecutive methods belong to same class, we can merge them.
- // This tends to reduce the number of entries (used memory) by 10x.
- size_t num_entries = cache.size();
- if (cache.size() > 1) {
- for (auto it = std::next(cache.begin()); it != cache.end(); it++) {
- if (std::prev(it)->second == it->second) {
- std::prev(it)->first = 0; // Clear entry with lower end_dex_offset (mark to remove).
- num_entries--;
- }
- }
- }
-
- // The cache is immutable now. Store it as continuous vector to save space.
- class_cache_.reserve(num_entries);
- auto pred = [](auto it) { return it.first != 0; }; // Entries to copy (not cleared above).
- std::copy_if(cache.begin(), cache.end(), std::back_inserter(class_cache_), pred);
+ CreateClassCache();
}
// Binary search in the class cache. First element of the pair is the key.
@@ -127,20 +129,31 @@
return false;
}
+ // The underlying ART object.
+ std::unique_ptr<const art::DexFile> dex_file_;
+
// Binary search table with (end_dex_offset, class_def_index) entries.
std::vector<std::pair<uint32_t, uint32_t>> class_cache_;
+
+ // Used as short lived temporary when needed. Avoids alloc/free.
+ std::string temporary_qualified_name_;
};
-int ExtDexFileOpenFromMemory(const void* addr,
- /*inout*/ size_t* size,
- const char* location,
- /*out*/ ExtDexFile** ext_dex_file) {
- if (*size < sizeof(art::DexFile::Header)) {
- *size = sizeof(art::DexFile::Header);
- return kExtDexFileNotEnoughData;
+ADexFile_Error ADexFile_create(const void* _Nonnull address,
+ size_t size,
+ size_t* _Nullable new_size,
+ const char* _Nonnull location,
+ /*out*/ ADexFile* _Nullable * _Nonnull out_dex_file) {
+ *out_dex_file = nullptr;
+
+ if (size < sizeof(art::DexFile::Header)) {
+ if (new_size != nullptr) {
+ *new_size = sizeof(art::DexFile::Header);
+ }
+ return ADEXFILE_ERROR_NOT_ENOUGH_DATA;
}
- const art::DexFile::Header* header = reinterpret_cast<const art::DexFile::Header*>(addr);
+ const art::DexFile::Header* header = reinterpret_cast<const art::DexFile::Header*>(address);
uint32_t file_size = header->file_size_;
if (art::CompactDexFile::IsMagicValid(header->magic_)) {
// Compact dex files store the data section separately so that it can be shared.
@@ -149,25 +162,27 @@
// In practice, this should be fine, as such sharing only happens on disk.
uint32_t computed_file_size;
if (__builtin_add_overflow(header->data_off_, header->data_size_, &computed_file_size)) {
- return kExtDexFileInvalidHeader;
+ return ADEXFILE_ERROR_INVALID_HEADER;
}
if (computed_file_size > file_size) {
file_size = computed_file_size;
}
} else if (!art::StandardDexFile::IsMagicValid(header->magic_)) {
- return kExtDexFileInvalidHeader;
+ return ADEXFILE_ERROR_INVALID_HEADER;
}
- if (*size < file_size) {
- *size = file_size;
- return kExtDexFileNotEnoughData;
+ if (size < file_size) {
+ if (new_size != nullptr) {
+ *new_size = file_size;
+ }
+ return ADEXFILE_ERROR_NOT_ENOUGH_DATA;
}
std::string loc_str(location);
art::DexFileLoader loader;
std::string error_msg;
- std::unique_ptr<const art::DexFile> dex_file = loader.Open(static_cast<const uint8_t*>(addr),
- *size,
+ std::unique_ptr<const art::DexFile> dex_file = loader.Open(static_cast<const uint8_t*>(address),
+ size,
loc_str,
header->checksum_,
/*oat_dex_file=*/nullptr,
@@ -175,78 +190,118 @@
/*verify_checksum=*/false,
&error_msg);
if (dex_file == nullptr) {
- LOG(ERROR) << "Can not opend dex file " << loc_str << ": " << error_msg;
- return kExtDexFileError;
+ LOG(ERROR) << "Can not open dex file " << loc_str << ": " << error_msg;
+ return ADEXFILE_ERROR_INVALID_DEX;
}
- *ext_dex_file = new ExtDexFile(std::move(dex_file));
- return kExtDexFileOk;
+ *out_dex_file = new ADexFile(std::move(dex_file));
+ return ADEXFILE_ERROR_OK;
}
-int ExtDexFileGetMethodInfoForOffset(ExtDexFile* ext_dex_file,
- uint32_t dex_offset,
- uint32_t flags,
- ExtDexFileMethodInfoCallback* method_info_cb,
- void* user_data) {
- if (!ext_dex_file->dex_file_->IsInDataSection(ext_dex_file->dex_file_->Begin() + dex_offset)) {
- return false; // The DEX offset is not within the bytecode of this dex file.
+void ADexFile_destroy(ADexFile* self) {
+ delete self;
+}
+
+size_t ADexFile_findMethodAtOffset(ADexFile* self,
+ size_t dex_offset,
+ ADexFile_MethodCallback* callback,
+ void* callback_data) {
+ const art::DexFile* dex_file = self->dex_file_.get();
+ if (!dex_file->IsInDataSection(dex_file->Begin() + dex_offset)) {
+ return 0; // The DEX offset is not within the bytecode of this dex file.
}
- if (ext_dex_file->dex_file_->IsCompactDexFile()) {
+ if (dex_file->IsCompactDexFile()) {
// The data section of compact dex files might be shared.
// Check the subrange unique to this compact dex.
const art::CompactDexFile::Header& cdex_header =
- ext_dex_file->dex_file_->AsCompactDexFile()->GetHeader();
+ dex_file->AsCompactDexFile()->GetHeader();
uint32_t begin = cdex_header.data_off_ + cdex_header.OwnedDataBegin();
uint32_t end = cdex_header.data_off_ + cdex_header.OwnedDataEnd();
if (dex_offset < begin || dex_offset >= end) {
- return false; // The DEX offset is not within the bytecode of this dex file.
+ return 0; // The DEX offset is not within the bytecode of this dex file.
}
}
- uint32_t method_index, addr, size;
- if (!ext_dex_file->GetMethodDefIndex(dex_offset, &method_index, &addr, &size)) {
- return false;
+ ADexFile_Method info;
+ if (!self->FindMethod(dex_offset, &info)) {
+ return 0;
}
- bool with_signature = flags & kExtDexFileWithSignature;
- std::string name = ext_dex_file->dex_file_->PrettyMethod(method_index, with_signature);
- ExtDexFileMethodInfo info {
- .sizeof_struct = sizeof(ExtDexFileMethodInfo),
- .addr = addr,
- .size = size,
- .name = name.c_str(),
- .name_size = name.size()
- };
- method_info_cb(user_data, &info);
- return true;
+ callback(callback_data, &info);
+ return 1;
}
-void ExtDexFileGetAllMethodInfos(ExtDexFile* ext_dex_file,
- uint32_t flags,
- ExtDexFileMethodInfoCallback* method_info_cb,
- void* user_data) {
- const art::DexFile* dex_file = ext_dex_file->dex_file_.get();
- for (art::ClassAccessor accessor : ext_dex_file->dex_file_->GetClasses()) {
+size_t ADexFile_forEachMethod(ADexFile* self,
+ ADexFile_MethodCallback* callback,
+ void* callback_data) {
+ size_t count = 0;
+ for (art::ClassAccessor accessor : self->dex_file_->GetClasses()) {
for (const art::ClassAccessor::Method& method : accessor.GetMethods()) {
art::CodeItemInstructionAccessor code = method.GetInstructions();
if (code.HasCodeItem()) {
- const uint8_t* insns = reinterpret_cast<const uint8_t*>(code.Insns());
- bool with_signature = flags & kExtDexFileWithSignature;
- std::string name = dex_file->PrettyMethod(method.GetIndex(), with_signature);
- ExtDexFileMethodInfo info {
- .sizeof_struct = sizeof(ExtDexFileMethodInfo),
- .addr = static_cast<uint32_t>(insns - dex_file->Begin()),
+ size_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - self->dex_file_->Begin();
+ ADexFile_Method info {
+ .adex = self,
+ .index = method.GetIndex(),
+ .offset = offset,
.size = code.InsnsSizeInBytes(),
- .name = name.c_str(),
- .name_size = name.size()
};
- method_info_cb(user_data, &info);
+ callback(callback_data, &info);
+ count++;
}
}
}
+ return count;
}
-void ExtDexFileClose(ExtDexFile* ext_dex_file) { delete (ext_dex_file); }
+size_t ADexFile_Method_getCodeOffset(const ADexFile_Method* self,
+ size_t* out_size) {
+ if (out_size != nullptr) {
+ *out_size = self->size;
+ }
+ return self->offset;
+}
+
+const char* ADexFile_Method_getName(const ADexFile_Method* self,
+ size_t* out_size) {
+ const char* name = self->adex->dex_file_->GetMethodName(self->index);
+ if (out_size != nullptr) {
+ *out_size = strlen(name);
+ }
+ return name;
+}
+
+const char* ADexFile_Method_getQualifiedName(const ADexFile_Method* self,
+ int with_params,
+ size_t* out_size) {
+ std::string& temp = self->adex->temporary_qualified_name_;
+ temp.clear();
+ self->adex->dex_file_->AppendPrettyMethod(self->index, with_params, &temp);
+ if (out_size != nullptr) {
+ *out_size = temp.size();
+ }
+ return temp.data();
+}
+
+const char* ADexFile_Method_getClassDescriptor(const ADexFile_Method* self,
+ size_t* out_size) {
+ const art::dex::MethodId& method_id = self->adex->dex_file_->GetMethodId(self->index);
+ const char* name = self->adex->dex_file_->GetMethodDeclaringClassDescriptor(method_id);
+ if (out_size != nullptr) {
+ *out_size = strlen(name);
+ }
+ return name;
+}
+
+const char* ADexFile_Error_toString(ADexFile_Error self) {
+ switch (self) {
+ case ADEXFILE_ERROR_OK: return "Ok";
+ case ADEXFILE_ERROR_INVALID_DEX: return "Dex file is invalid.";
+ case ADEXFILE_ERROR_NOT_ENOUGH_DATA: return "Not enough data. Incomplete dex file.";
+ case ADEXFILE_ERROR_INVALID_HEADER: return "Invalid dex file header.";
+ }
+ return nullptr;
+}
} // extern "C"