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"
diff --git a/libdexfile/external/dex_file_ext_c_test.c b/libdexfile/external/dex_file_ext_c_test.c
index e113971..656a6cb 100644
--- a/libdexfile/external/dex_file_ext_c_test.c
+++ b/libdexfile/external/dex_file_ext_c_test.c
@@ -14,66 +14,6 @@
* limitations under the License.
*/
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-/* The main purpose of this test is to ensure this C header compiles in C, so
- * that no C++ features inadvertently leak into the C ABI. */
+/* The main purpose of this file is to ensure this C header compiles in C,
+ * so that no C++ features inadvertently leak into the C ABI. */
#include "art_api/dex_file_external.h"
-
-static const char gtest_output_arg[] = "--gtest_output=xml:";
-static const char gtest_output_xml[] = "\
-<?xml version=\"1.0\"?>\n\
-<testsuites tests=\"1\" failures=\"0\" disabled=\"0\" errors=\"0\" name=\"AllTests\">\n\
- <testsuite tests=\"1\" failures=\"0\" disabled=\"0\" errors=\"0\" name=\"NopTest\">\n\
- <testcase name=\"nop\" status=\"run\" />\n\
- </testsuite>\n\
-</testsuites>";
-
-static const char canned_stdout[] = "\
-[==========] Running 1 test from 1 test suite.\n\
-[----------] 1 test from NopTest\n\
-[ RUN ] NopTest.nop\n\
-[ OK ] NopTest.nop (0 ms)\n\
-[----------] 1 test from NopTest (0 ms total)\n\
-\n\
-[==========] 1 test from 1 test suite ran. (0 ms total)\n\
-[ PASSED ] 1 test.\n\
-";
-
-/* Writes a fake gtest xml report to the given path. */
-static int write_gtest_output_xml(char* gtest_output_path) {
- FILE* output_fd = fopen(gtest_output_path, "w");
- if (output_fd == NULL) {
- fprintf(stderr, "Failed to open %s: %s\n", gtest_output_path, strerror(errno));
- return 1;
- }
- if (fprintf(output_fd, gtest_output_xml) != sizeof(gtest_output_xml) - 1) {
- fprintf(stderr, "Failed to write %s: %s\n", gtest_output_path, strerror(errno));
- fclose(output_fd);
- return 1;
- }
- if (fclose(output_fd) != 0) {
- fprintf(stderr, "Failed to close %s: %s\n", gtest_output_path, strerror(errno));
- return 1;
- }
- return 0;
-}
-
-int main(int argc, char** argv) {
- int i;
- for (i = 1; i < argc; ++i) {
- if (strncmp(argv[i], gtest_output_arg, sizeof(gtest_output_arg) - 1) == 0) {
- /* The ART gtest framework expects all tests to understand --gtest_output. */
- if (write_gtest_output_xml(argv[i] + sizeof(gtest_output_arg) - 1)) {
- return 1;
- }
- }
- }
- /* Tradefed parses the output, so send something passable there. */
- if (fputs(canned_stdout, stdout) < 0) {
- return 1;
- }
- return 0;
-}
diff --git a/libdexfile/external/dex_file_ext_test.cc b/libdexfile/external/dex_file_ext_test.cc
new file mode 100644
index 0000000..54aaa3b
--- /dev/null
+++ b/libdexfile/external/dex_file_ext_test.cc
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include <android-base/file.h>
+#include <dex/dex_file.h>
+#include <gtest/gtest.h>
+
+#include "art_api/dex_file_external.h"
+#include "dex_file_test_data.h"
+
+namespace art_api {
+namespace dex {
+
+class ADexFileTest : public ::testing::Test {
+ protected:
+ void TearDown() override {
+ ADexFile_destroy(dex_);
+ }
+
+ ADexFile* dex_ = nullptr;
+};
+
+TEST_F(ADexFileTest, create) {
+ size_t size = sizeof(kDexData);
+ EXPECT_EQ(ADexFile_create(kDexData, size, &size, "", &dex_), ADEXFILE_ERROR_OK);
+ EXPECT_EQ(size, sizeof(kDexData));
+ EXPECT_NE(dex_, nullptr);
+}
+
+TEST_F(ADexFileTest, create_null_new_size) {
+ const size_t size = sizeof(kDexData);
+ EXPECT_EQ(ADexFile_create(kDexData, size, nullptr, "", &dex_), ADEXFILE_ERROR_OK);
+ EXPECT_NE(dex_, nullptr);
+}
+
+TEST_F(ADexFileTest, create_header_too_small) {
+ size_t size = sizeof(art::DexFile::Header) - 1;
+ EXPECT_EQ(ADexFile_create(kDexData, size, &size, "", &dex_), ADEXFILE_ERROR_NOT_ENOUGH_DATA);
+ EXPECT_EQ(size, sizeof(art::DexFile::Header));
+ EXPECT_EQ(dex_, nullptr);
+}
+
+TEST_F(ADexFileTest, create_file_too_small) {
+ size_t size = sizeof(art::DexFile::Header);
+ EXPECT_EQ(ADexFile_create(kDexData, size, &size, "", &dex_), ADEXFILE_ERROR_NOT_ENOUGH_DATA);
+ EXPECT_EQ(size, sizeof(kDexData));
+ EXPECT_EQ(dex_, nullptr);
+}
+
+static ADexFile* GetTestDexData() {
+ size_t size = sizeof(kDexData);
+ ADexFile* dex;
+ EXPECT_EQ(ADexFile_create(kDexData, size, &size, "", &dex), ADEXFILE_ERROR_OK);
+ EXPECT_EQ(size, sizeof(kDexData));
+ EXPECT_NE(dex, nullptr);
+ return dex;
+}
+
+TEST_F(ADexFileTest, findMethodAtOffset) {
+ dex_ = GetTestDexData();
+ ASSERT_NE(dex_, nullptr);
+
+ bool found_init = false;
+ auto check_init = [](void* ctx, const ADexFile_Method* method) {
+ size_t size;
+ size_t offset = ADexFile_Method_getCodeOffset(method, &size);
+ EXPECT_EQ(offset, 0x100u);
+ EXPECT_EQ(size, 8u);
+ EXPECT_STREQ(ADexFile_Method_getName(method, &size), "<init>");
+ EXPECT_STREQ(ADexFile_Method_getQualifiedName(method, false, &size), "Main.<init>");
+ EXPECT_STREQ(ADexFile_Method_getQualifiedName(method, true, &size), "void Main.<init>()");
+ EXPECT_STREQ(ADexFile_Method_getClassDescriptor(method, &size), "LMain;");
+ *reinterpret_cast<bool*>(ctx) = true;
+ };
+ EXPECT_EQ(ADexFile_findMethodAtOffset(dex_, 0x102, check_init, &found_init), 1u);
+ EXPECT_TRUE(found_init);
+
+ bool found_main = false;
+ auto check_main = [](void* ctx, const ADexFile_Method* method) {
+ size_t size;
+ size_t offset = ADexFile_Method_getCodeOffset(method, &size);
+ EXPECT_EQ(offset, 0x118u);
+ EXPECT_EQ(size, 2u);
+ EXPECT_STREQ(ADexFile_Method_getName(method, &size), "main");
+ EXPECT_STREQ(ADexFile_Method_getQualifiedName(method, false, &size), "Main.main");
+ EXPECT_STREQ(ADexFile_Method_getQualifiedName(method, true, &size), "void Main.main(java.lang.String[])");
+ EXPECT_STREQ(ADexFile_Method_getClassDescriptor(method, &size), "LMain;");
+ *reinterpret_cast<bool*>(ctx) = true;
+ };
+ EXPECT_EQ(ADexFile_findMethodAtOffset(dex_, 0x118, check_main, &found_main), 1u);
+ EXPECT_TRUE(found_main);
+}
+
+TEST_F(ADexFileTest, findMethodAtOffset_for_offset_boundaries) {
+ dex_ = GetTestDexData();
+ ASSERT_NE(dex_, nullptr);
+
+ auto no_cb = [](void*, const ADexFile_Method*) {};
+ EXPECT_EQ(ADexFile_findMethodAtOffset(dex_, 0x99, no_cb, nullptr), 0);
+ EXPECT_EQ(ADexFile_findMethodAtOffset(dex_, 0x100, no_cb, nullptr), 1);
+ EXPECT_EQ(ADexFile_findMethodAtOffset(dex_, 0x107, no_cb, nullptr), 1);
+ EXPECT_EQ(ADexFile_findMethodAtOffset(dex_, 0x108, no_cb, nullptr), 0);
+ EXPECT_EQ(ADexFile_findMethodAtOffset(dex_, 0x100000, no_cb, nullptr), 0);
+}
+
+TEST_F(ADexFileTest, forEachMethod) {
+ dex_ = GetTestDexData();
+ ASSERT_NE(dex_, nullptr);
+
+ std::vector<std::string> names;
+ auto add = [](void* ctx, const ADexFile_Method* method) {
+ reinterpret_cast<decltype(names)*>(ctx)->
+ push_back(ADexFile_Method_getQualifiedName(method, false, nullptr));
+ };
+ EXPECT_EQ(ADexFile_forEachMethod(dex_, add, &names), 2u);
+ EXPECT_EQ(names, std::vector<std::string>({"Main.<init>", "Main.main"}));
+}
+
+TEST_F(ADexFileTest, Error_toString) {
+ constexpr size_t kNumErrors = 4;
+ for (size_t i = 0; i < kNumErrors; i++) {
+ const char* err = ADexFile_Error_toString(static_cast<ADexFile_Error>(i));
+ ASSERT_NE(err, nullptr);
+ ASSERT_FALSE(std::string_view(err).empty());
+ }
+ const char* err = ADexFile_Error_toString(static_cast<ADexFile_Error>(kNumErrors));
+ ASSERT_EQ(err, nullptr);
+}
+
+} // namespace dex
+} // namespace art_api
diff --git a/libdexfile/external/dex_file_supp.cc b/libdexfile/external/dex_file_supp.cc
index 6313afe..23fc88a 100644
--- a/libdexfile/external/dex_file_supp.cc
+++ b/libdexfile/external/dex_file_supp.cc
@@ -33,21 +33,15 @@
namespace art_api {
namespace dex {
-#define FOR_ALL_DLFUNCS(MACRO) \
- MACRO(DexFile, ExtDexFileOpenFromMemory) \
- MACRO(DexFile, ExtDexFileGetMethodInfoForOffset) \
- MACRO(DexFile, ExtDexFileGetAllMethodInfos) \
- MACRO(DexFile, ExtDexFileClose)
-
-#ifdef STATIC_LIB
-#define DEFINE_DLFUNC_PTR(CLASS, DLFUNC) decltype(DLFUNC)* CLASS::g_##DLFUNC = DLFUNC;
+#if defined(STATIC_LIB)
+#define DEFINE_ADEX_FILE_SYMBOL(DLFUNC) decltype(DLFUNC)* g_##DLFUNC = DLFUNC;
#else
-#define DEFINE_DLFUNC_PTR(CLASS, DLFUNC) decltype(DLFUNC)* CLASS::g_##DLFUNC = nullptr;
+#define DEFINE_ADEX_FILE_SYMBOL(DLFUNC) decltype(DLFUNC)* g_##DLFUNC = nullptr;
#endif
-FOR_ALL_DLFUNCS(DEFINE_DLFUNC_PTR)
-#undef DEFINE_DLFUNC_PTR
+FOR_EACH_ADEX_FILE_SYMBOL(DEFINE_ADEX_FILE_SYMBOL)
+#undef DEFINE_ADEX_FILE_SYMBOL
-bool TryLoadLibdexfileExternal([[maybe_unused]] std::string* err_msg) {
+bool TryLoadLibdexfile([[maybe_unused]] std::string* err_msg) {
#if defined(STATIC_LIB)
// Nothing to do here since all function pointers are initialised statically.
return true;
@@ -76,18 +70,18 @@
return false;
}
-#define RESOLVE_DLFUNC_PTR(CLASS, DLFUNC) \
- decltype(DLFUNC)* DLFUNC##_ptr = reinterpret_cast<decltype(DLFUNC)*>(dlsym(handle, #DLFUNC)); \
- if ((DLFUNC) == nullptr) { \
+#define RESOLVE_ADEX_FILE_SYMBOL(DLFUNC) \
+ auto DLFUNC##_ptr = reinterpret_cast<decltype(DLFUNC)*>(dlsym(handle, #DLFUNC)); \
+ if (DLFUNC##_ptr == nullptr) { \
*err_msg = dlerror(); \
return false; \
}
- FOR_ALL_DLFUNCS(RESOLVE_DLFUNC_PTR);
-#undef RESOLVE_DLFUNC_PTR
+FOR_EACH_ADEX_FILE_SYMBOL(RESOLVE_ADEX_FILE_SYMBOL)
+#undef RESOLVE_ADEX_FILE_SYMBOL
-#define SET_DLFUNC_PTR(CLASS, DLFUNC) CLASS::g_##DLFUNC = DLFUNC##_ptr;
- FOR_ALL_DLFUNCS(SET_DLFUNC_PTR);
-#undef SET_DLFUNC_PTR
+#define SET_ADEX_FILE_SYMBOL(DLFUNC) g_##DLFUNC = DLFUNC##_ptr;
+ FOR_EACH_ADEX_FILE_SYMBOL(SET_ADEX_FILE_SYMBOL);
+#undef SET_ADEX_FILE_SYMBOL
is_loaded = true;
}
@@ -96,102 +90,13 @@
#endif // !defined(NO_DEXFILE_SUPPORT) && !defined(STATIC_LIB)
}
-void LoadLibdexfileExternal() {
+void LoadLibdexfile() {
#ifndef STATIC_LIB
- if (std::string err_msg; !TryLoadLibdexfileExternal(&err_msg)) {
+ if (std::string err_msg; !TryLoadLibdexfile(&err_msg)) {
LOG_ALWAYS_FATAL("%s", err_msg.c_str());
}
#endif
}
-DexFile::~DexFile() { g_ExtDexFileClose(ext_dex_file_); }
-
-std::unique_ptr<DexFile> DexFile::OpenFromMemory(const void* addr,
- size_t* size,
- const std::string& location,
- /*out*/ std::string* error_msg) {
- if (UNLIKELY(g_ExtDexFileOpenFromMemory == nullptr)) {
- // Load libdexfile_external.so in this factory function, so instance
- // methods don't need to check this.
- LoadLibdexfileExternal();
- }
- ExtDexFile* ext_dex_file;
- int res = g_ExtDexFileOpenFromMemory(addr, size, location.c_str(), &ext_dex_file);
- switch (static_cast<ExtDexFileError>(res)) {
- case kExtDexFileOk:
- return std::unique_ptr<DexFile>(new DexFile(ext_dex_file));
- case kExtDexFileInvalidHeader:
- *error_msg = std::string("Invalid DexFile header ") + location;
- return nullptr;
- case kExtDexFileNotEnoughData:
- *error_msg = std::string("Not enough data");
- return nullptr;
- case kExtDexFileError:
- break;
- }
- *error_msg = std::string("Failed to open DexFile ") + location;
- return nullptr;
-}
-
-std::unique_ptr<DexFile> DexFile::OpenFromFd(int fd,
- off_t offset,
- const std::string& location,
- /*out*/ std::string* error_msg) {
- using android::base::StringPrintf;
- size_t length;
- {
- struct stat sbuf;
- std::memset(&sbuf, 0, sizeof(sbuf));
- if (fstat(fd, &sbuf) == -1) {
- *error_msg = StringPrintf("fstat '%s' failed: %s", location.c_str(), std::strerror(errno));
- return nullptr;
- }
- if (S_ISDIR(sbuf.st_mode)) {
- *error_msg = StringPrintf("Attempt to mmap directory '%s'", location.c_str());
- return nullptr;
- }
- length = sbuf.st_size;
- }
-
- if (static_cast<off_t>(length) < offset) {
- *error_msg = StringPrintf(
- "Offset %" PRId64 " too large for '%s' of size %zu",
- int64_t{offset},
- location.c_str(),
- length);
- return nullptr;
- }
- length -= offset;
-
- std::unique_ptr<android::base::MappedFile> map;
- map = android::base::MappedFile::FromFd(fd, offset, length, PROT_READ);
- if (map == nullptr) {
- *error_msg = StringPrintf("mmap '%s' failed: %s", location.c_str(), std::strerror(errno));
- return nullptr;
- }
-
- std::unique_ptr<DexFile> dex = OpenFromMemory(map->data(), &length, location, error_msg);
- if (dex != nullptr) {
- dex->map_ = std::move(map); // Ensure the map gets freed with the dex file.
- }
- return dex;
-}
-
-MethodInfo DexFile::GetMethodInfoForOffset(int64_t dex_offset, bool with_signature) {
- MethodInfo res{};
- auto set_method = [&res](ExtDexFileMethodInfo* info) { res = AbsorbMethodInfo(info); };
- uint32_t flags = with_signature ? kExtDexFileWithSignature : 0;
- GetMethodInfoForOffset(dex_offset, set_method, flags);
- return res;
-}
-
-std::vector<MethodInfo> DexFile::GetAllMethodInfos(bool with_signature) {
- std::vector<MethodInfo> res;
- auto add_method = [&res](ExtDexFileMethodInfo* info) { res.push_back(AbsorbMethodInfo(info)); };
- uint32_t flags = with_signature ? kExtDexFileWithSignature : 0;
- GetAllMethodInfos(add_method, flags);
- return res;
-}
-
} // namespace dex
} // namespace art_api
diff --git a/libdexfile/external/dex_file_supp_test.cc b/libdexfile/external/dex_file_supp_test.cc
index ff263fc..3285edf 100644
--- a/libdexfile/external/dex_file_supp_test.cc
+++ b/libdexfile/external/dex_file_supp_test.cc
@@ -25,218 +25,104 @@
#include <gtest/gtest.h>
#include "art_api/dex_file_support.h"
+#include "dex_file_test_data.h"
namespace art_api {
namespace dex {
-static constexpr uint32_t kDexData[] = {
- 0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
- 0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
- 0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
- 0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
- 0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
- 0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
- 0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
- 0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
- 0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
- 0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
- 0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
- 0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
- 0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
- 0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
- 0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
- 0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
- 0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
-};
-
-TEST(DexFileTest, from_memory_header_too_small) {
- size_t size = sizeof(art::DexFile::Header) - 1;
- std::string error_msg;
- EXPECT_EQ(DexFile::OpenFromMemory(kDexData, &size, "", &error_msg), nullptr);
- EXPECT_EQ(size, sizeof(art::DexFile::Header));
- EXPECT_FALSE(error_msg.empty());
+TEST(DexFileTest, create) {
+ size_t size = sizeof(kDexData);
+ std::unique_ptr<DexFile> dex_file;
+ EXPECT_TRUE(DexFile::Create(kDexData, size, &size, "", &dex_file).Ok());
+ EXPECT_EQ(size, sizeof(kDexData));
+ EXPECT_NE(dex_file, nullptr);
}
-TEST(DexFileTest, from_memory_file_too_small) {
+TEST(DexFileTest, create_header_too_small) {
+ size_t size = sizeof(art::DexFile::Header) - 1;
+ std::unique_ptr<DexFile> dex_file;
+ DexFile::Error error = DexFile::Create(kDexData, size, &size, "", &dex_file);
+ EXPECT_FALSE(error.Ok());
+ EXPECT_EQ(error.Code(), ADEXFILE_ERROR_NOT_ENOUGH_DATA);
+ EXPECT_STREQ(error.ToString(), "Not enough data. Incomplete dex file.");
+ EXPECT_EQ(size, sizeof(art::DexFile::Header));
+ EXPECT_EQ(dex_file, nullptr);
+}
+
+TEST(DexFileTest, create_file_too_small) {
size_t size = sizeof(art::DexFile::Header);
- std::string error_msg;
- EXPECT_EQ(DexFile::OpenFromMemory(kDexData, &size, "", &error_msg), nullptr);
+ std::unique_ptr<DexFile> dex_file;
+ DexFile::Error error = DexFile::Create(kDexData, size, &size, "", &dex_file);
+ EXPECT_FALSE(error.Ok());
+ EXPECT_EQ(error.Code(), ADEXFILE_ERROR_NOT_ENOUGH_DATA);
+ EXPECT_STREQ(error.ToString(), "Not enough data. Incomplete dex file.");
EXPECT_EQ(size, sizeof(kDexData));
- EXPECT_FALSE(error_msg.empty());
+ EXPECT_EQ(dex_file, nullptr);
}
static std::unique_ptr<DexFile> GetTestDexData() {
size_t size = sizeof(kDexData);
- std::string error_msg;
- std::unique_ptr<DexFile> dex_file = DexFile::OpenFromMemory(kDexData, &size, "", &error_msg);
- EXPECT_TRUE(error_msg.empty());
+ std::unique_ptr<DexFile> dex_file;
+ EXPECT_TRUE(DexFile::Create(kDexData, size, &size, "", &dex_file).Ok());
+ EXPECT_EQ(size, sizeof(kDexData));
+ EXPECT_NE(dex_file, nullptr);
return dex_file;
}
-TEST(DexFileTest, from_memory) {
- EXPECT_NE(GetTestDexData(), nullptr);
-}
-
-TEST(DexFileTest, from_fd_header_too_small) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1);
- ASSERT_EQ(sizeof(art::DexFile::Header) - 1,
- static_cast<size_t>(
- TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header) - 1))));
-
- std::string error_msg;
- EXPECT_EQ(DexFile::OpenFromFd(tf.fd, 0, tf.path, &error_msg), nullptr);
- EXPECT_FALSE(error_msg.empty());
-}
-
-TEST(DexFileTest, from_fd_file_too_small) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1);
- ASSERT_EQ(sizeof(art::DexFile::Header),
- static_cast<size_t>(
- TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header)))));
-
- std::string error_msg;
- EXPECT_EQ(DexFile::OpenFromFd(tf.fd, 0, tf.path, &error_msg), nullptr);
- EXPECT_FALSE(error_msg.empty());
-}
-
-TEST(DexFileTest, from_fd) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1);
- ASSERT_EQ(sizeof(kDexData),
- static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
-
- std::string error_msg;
- EXPECT_NE(DexFile::OpenFromFd(tf.fd, 0, tf.path, &error_msg), nullptr);
- EXPECT_TRUE(error_msg.empty());
-}
-
-TEST(DexFileTest, from_fd_non_zero_offset) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1);
- ASSERT_EQ(0x100, lseek(tf.fd, 0x100, SEEK_SET));
- ASSERT_EQ(sizeof(kDexData),
- static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
-
- std::string error_msg;
- EXPECT_NE(DexFile::OpenFromFd(tf.fd, 0x100, tf.path, &error_msg), nullptr);
- EXPECT_TRUE(error_msg.empty());
-}
-
-TEST(DexFileTest, get_method_info_for_offset_without_signature) {
+TEST(DexFileTest, findMethodAtOffset) {
std::unique_ptr<DexFile> dex_file = GetTestDexData();
ASSERT_NE(dex_file, nullptr);
- MethodInfo info = dex_file->GetMethodInfoForOffset(0x102, false);
- EXPECT_EQ(info.offset, int32_t{0x100});
- EXPECT_EQ(info.len, int32_t{8});
- EXPECT_STREQ(info.name.data(), "Main.<init>");
+ bool found_init = false;
+ auto check_init = [&](const DexFile::Method& method) {
+ size_t size;
+ size_t offset = method.GetCodeOffset(&size);
+ EXPECT_EQ(offset, 0x100u);
+ EXPECT_EQ(size, 8u);
+ EXPECT_STREQ(method.GetName(), "<init>");
+ EXPECT_STREQ(method.GetQualifiedName(), "Main.<init>");
+ EXPECT_STREQ(method.GetQualifiedName(true), "void Main.<init>()");
+ EXPECT_STREQ(method.GetClassDescriptor(), "LMain;");
+ found_init = true;
+ };
+ EXPECT_EQ(dex_file->FindMethodAtOffset(0x102, check_init), 1u);
+ EXPECT_TRUE(found_init);
- info = dex_file->GetMethodInfoForOffset(0x118, false);
- EXPECT_EQ(info.offset, int32_t{0x118});
- EXPECT_EQ(info.len, int32_t{2});
- EXPECT_STREQ(info.name.data(), "Main.main");
-
- // Retrieve a cached result.
- info = dex_file->GetMethodInfoForOffset(0x104, false);
- EXPECT_EQ(info.offset, int32_t{0x100});
- EXPECT_EQ(info.len, int32_t{8});
- EXPECT_STREQ(info.name.data(), "Main.<init>");
-}
-
-TEST(DexFileTest, get_method_info_for_offset_with_signature) {
- std::unique_ptr<DexFile> dex_file = GetTestDexData();
- ASSERT_NE(dex_file, nullptr);
-
- MethodInfo info = dex_file->GetMethodInfoForOffset(0x102, true);
- EXPECT_EQ(info.offset, int32_t{0x100});
- EXPECT_EQ(info.len, int32_t{8});
- EXPECT_STREQ(info.name.data(), "void Main.<init>()");
-
- info = dex_file->GetMethodInfoForOffset(0x118, true);
- EXPECT_EQ(info.offset, int32_t{0x118});
- EXPECT_EQ(info.len, int32_t{2});
- EXPECT_STREQ(info.name.data(), "void Main.main(java.lang.String[])");
-
- // Retrieve a cached result.
- info = dex_file->GetMethodInfoForOffset(0x104, true);
- EXPECT_EQ(info.offset, int32_t{0x100});
- EXPECT_EQ(info.len, int32_t{8});
- EXPECT_STREQ(info.name.data(), "void Main.<init>()");
-
- // with_signature doesn't affect the cache.
- info = dex_file->GetMethodInfoForOffset(0x104, false);
- EXPECT_EQ(info.offset, int32_t{0x100});
- EXPECT_EQ(info.len, int32_t{8});
- EXPECT_STREQ(info.name.data(), "Main.<init>");
+ bool found_main = false;
+ auto check_main = [&](const DexFile::Method& method) {
+ size_t size;
+ size_t offset = method.GetCodeOffset(&size);
+ EXPECT_EQ(offset, 0x118u);
+ EXPECT_EQ(size, 2u);
+ EXPECT_STREQ(method.GetName(), "main");
+ EXPECT_STREQ(method.GetQualifiedName(), "Main.main");
+ EXPECT_STREQ(method.GetQualifiedName(true), "void Main.main(java.lang.String[])");
+ EXPECT_STREQ(method.GetClassDescriptor(), "LMain;");
+ found_main = true;
+ };
+ EXPECT_EQ(dex_file->FindMethodAtOffset(0x118, check_main), 1u);
+ EXPECT_TRUE(found_main);
}
TEST(DexFileTest, get_method_info_for_offset_boundaries) {
std::unique_ptr<DexFile> dex_file = GetTestDexData();
ASSERT_NE(dex_file, nullptr);
- MethodInfo info = dex_file->GetMethodInfoForOffset(0x100000, false);
- EXPECT_EQ(info.offset, int32_t{0});
-
- info = dex_file->GetMethodInfoForOffset(0x99, false);
- EXPECT_EQ(info.offset, int32_t{0});
- info = dex_file->GetMethodInfoForOffset(0x100, false);
- EXPECT_EQ(info.offset, int32_t{0x100});
- info = dex_file->GetMethodInfoForOffset(0x107, false);
- EXPECT_EQ(info.offset, int32_t{0x100});
- info = dex_file->GetMethodInfoForOffset(0x108, false);
- EXPECT_EQ(info.offset, int32_t{0});
-
- // Make sure that once the whole dex file has been cached, no problems occur.
- info = dex_file->GetMethodInfoForOffset(0x98, false);
- EXPECT_EQ(info.offset, int32_t{0});
-
- // Choose a value that is in the cached map, but not in a valid method.
- info = dex_file->GetMethodInfoForOffset(0x110, false);
- EXPECT_EQ(info.offset, int32_t{0});
+ EXPECT_EQ(dex_file->FindMethodAtOffset(0x99, [](auto){}), 0);
+ EXPECT_EQ(dex_file->FindMethodAtOffset(0x100, [](auto){}), 1);
+ EXPECT_EQ(dex_file->FindMethodAtOffset(0x107, [](auto){}), 1);
+ EXPECT_EQ(dex_file->FindMethodAtOffset(0x108, [](auto){}), 0);
+ EXPECT_EQ(dex_file->FindMethodAtOffset(0x100000, [](auto){}), 0);
}
TEST(DexFileTest, get_all_method_infos_without_signature) {
std::unique_ptr<DexFile> dex_file = GetTestDexData();
ASSERT_NE(dex_file, nullptr);
- std::vector<MethodInfo> infos;
- infos.emplace_back(MethodInfo{0x100, 8, std::string("Main.<init>")});
- infos.emplace_back(MethodInfo{0x118, 2, std::string("Main.main")});
- ASSERT_EQ(dex_file->GetAllMethodInfos(false), infos);
-}
-
-TEST(DexFileTest, get_all_method_infos_with_signature) {
- std::unique_ptr<DexFile> dex_file = GetTestDexData();
- ASSERT_NE(dex_file, nullptr);
-
- std::vector<MethodInfo> infos;
- infos.emplace_back(MethodInfo{0x100, 8, std::string("void Main.<init>()")});
- infos.emplace_back(MethodInfo{0x118, 2, std::string("void Main.main(java.lang.String[])")});
- ASSERT_EQ(dex_file->GetAllMethodInfos(true), infos);
-}
-
-TEST(DexFileTest, move_construct) {
- std::unique_ptr<DexFile> dex_file = GetTestDexData();
- ASSERT_NE(dex_file, nullptr);
-
- auto df1 = DexFile(std::move(*dex_file));
- auto df2 = DexFile(std::move(df1));
-
- MethodInfo info = df2.GetMethodInfoForOffset(0x100, false);
- EXPECT_EQ(info.offset, int32_t{0x100});
-}
-
-TEST(DexFileTest, pointer_construct) {
- std::unique_ptr<DexFile> dex_file = GetTestDexData();
- ASSERT_NE(dex_file, nullptr);
-
- auto new_dex = DexFile(dex_file);
- ASSERT_TRUE(dex_file.get() == nullptr);
-
- MethodInfo info = new_dex.GetMethodInfoForOffset(0x100, false);
- EXPECT_EQ(info.offset, int32_t{0x100});
+ std::vector<std::string> names;
+ auto add = [&](const DexFile::Method& method) { names.push_back(method.GetQualifiedName()); };
+ EXPECT_EQ(dex_file->ForEachMethod(add), 2u);
+ EXPECT_EQ(names, std::vector<std::string>({"Main.<init>", "Main.main"}));
}
} // namespace dex
diff --git a/libdexfile/external/dex_file_test_data.h b/libdexfile/external/dex_file_test_data.h
new file mode 100644
index 0000000..fd6b80b
--- /dev/null
+++ b/libdexfile/external/dex_file_test_data.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_EXTERNAL_DEX_FILE_TEST_DATA_H_
+#define ART_LIBDEXFILE_EXTERNAL_DEX_FILE_TEST_DATA_H_
+
+#include <stdint.h>
+
+namespace art_api {
+namespace dex {
+
+static constexpr uint32_t kDexData[] = {
+ 0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
+ 0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
+ 0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
+ 0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
+ 0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
+ 0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
+ 0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
+ 0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
+ 0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
+ 0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
+ 0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
+ 0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
+ 0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
+ 0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
+ 0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
+ 0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
+ 0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
+};
+
+} // namespace dex
+} // namespace art_api
+
+#endif // ART_LIBDEXFILE_EXTERNAL_DEX_FILE_TEST_DATA_H_
diff --git a/libdexfile/external/include/art_api/dex_file_external.h b/libdexfile/external/include/art_api/dex_file_external.h
index b880335..360be92 100644
--- a/libdexfile/external/include/art_api/dex_file_external.h
+++ b/libdexfile/external/include/art_api/dex_file_external.h
@@ -18,67 +18,140 @@
#define ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_DEX_FILE_EXTERNAL_H_
// Dex file external API
-
-#include <sys/types.h>
#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
+__BEGIN_DECLS
// This is the stable C ABI that backs art_api::dex below. Structs and functions
// may only be added here. C++ users should use dex_file_support.h instead.
-struct ExtDexFileMethodInfo {
- size_t sizeof_struct; // Size of this structure (to allow future extensions).
- uint32_t addr; // Start of dex byte-code relative to the start of the dex file.
- uint32_t size; // Size of the dex byte-code in bytes.
- const char* name;
- size_t name_size;
+struct ADexFile;
+typedef struct ADexFile ADexFile;
+
+struct ADexFile_Method;
+typedef struct ADexFile_Method ADexFile_Method;
+
+enum ADexFile_Error : uint32_t {
+ ADEXFILE_ERROR_OK = 0,
+ ADEXFILE_ERROR_INVALID_DEX = 1,
+ ADEXFILE_ERROR_INVALID_HEADER = 2,
+ ADEXFILE_ERROR_NOT_ENOUGH_DATA = 3,
};
-
-enum ExtDexFileError {
- kExtDexFileOk = 0,
- kExtDexFileError = 1, // Unspecified error.
- kExtDexFileNotEnoughData = 2,
- kExtDexFileInvalidHeader = 3,
-};
-
-enum ExtDexFileMethodFlags {
- kExtDexFileWithSignature = 1,
-};
-
-struct ExtDexFile;
-
-// Try to open a dex file in the given memory range.
-// If the memory range is too small, larger suggest size is written to the argument.
-int ExtDexFileOpenFromMemory(const void* addr,
- /*inout*/ size_t* size,
- const char* location,
- /*out*/ struct ExtDexFile** ext_dex_file);
+typedef enum ADexFile_Error ADexFile_Error;
// Callback used to return information about a dex method.
-typedef void ExtDexFileMethodInfoCallback(void* user_data,
- struct ExtDexFileMethodInfo* method_info);
+// The method information is valid only during the callback.
+typedef void ADexFile_MethodCallback(void* _Nullable callback_data,
+ const ADexFile_Method* _Nonnull method);
-// Find a single dex method based on the given dex offset.
-int ExtDexFileGetMethodInfoForOffset(struct ExtDexFile* ext_dex_file,
- uint32_t dex_offset,
- uint32_t flags,
- ExtDexFileMethodInfoCallback* method_info_cb,
- void* user_data);
+// Interprets a chunk of memory as a dex file.
+//
+// @param address Pointer to the start of dex file data.
+// The caller must retain the memory until the object is destroyed.
+// @param size Size of the memory range. If the size is too small, the method returns
+// ADEXFILE_ERROR_NOT_ENOUGH_DATA and sets new_size to some larger size
+// (which still might large enough, so several retries might be needed).
+// @param new_size On successful load, this contains exact dex file size from header.
+// @param location A string that describes the dex file. Preferably its path.
+// It is mostly used just for log messages and may be "".
+// @param dex_file The created dex file object, or nullptr on error.
+// It must be later freed with ADexFile_Destroy.
+//
+// @return ADEXFILE_ERROR_OK if successful.
+// @return ADEXFILE_ERROR_NOT_ENOUGH_DATA if the provided dex file is too short (truncated).
+// @return ADEXFILE_ERROR_INVALID_HEADER if the memory does not seem to represent DEX file.
+// @return ADEXFILE_ERROR_INVALID_DEX if any other non-specific error occurs.
+//
+// Thread-safe (creates new object).
+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);
-// Return all dex methods in the dex file.
-void ExtDexFileGetAllMethodInfos(struct ExtDexFile* ext_dex_file,
- uint32_t flags,
- ExtDexFileMethodInfoCallback* method_info_cb,
- void* user_data);
+// Find method at given offset and call callback with information about the method.
+//
+// @param dex_offset Offset relative to the start of the dex file header.
+// @param callback The callback to call when method is found. Any data that needs to
+// outlive the execution of the callback must be copied by the user.
+// @param callback_data Extra user-specified argument for the callback.
+//
+// @return Number of methods found (0 or 1).
+//
+// Not thread-safe for calls on the same ADexFile instance.
+size_t ADexFile_findMethodAtOffset(ADexFile* _Nonnull self,
+ size_t dex_offset,
+ ADexFile_MethodCallback* _Nonnull callback,
+ void* _Nullable callback_data);
-// Release all associated memory.
-void ExtDexFileClose(struct ExtDexFile* ext_dex_file);
+// Call callback for all methods in the DEX file.
+//
+// @param flags Specifies which information should be obtained.
+// @param callback The callback to call for all methods. Any data that needs to
+// outlive the execution of the callback must be copied by the user.
+// @param callback_data Extra user-specified argument for the callback.
+//
+// @return Number of methods found.
+//
+// Not thread-safe for calls on the same ADexFile instance.
+size_t ADexFile_forEachMethod(ADexFile* _Nonnull self,
+ ADexFile_MethodCallback* _Nonnull callback,
+ void* _Nullable callback_data);
-#ifdef __cplusplus
-} // extern "C"
-#endif
+// Free the given object.
+//
+// Thread-safe, can be called only once for given instance.
+void ADexFile_destroy(ADexFile* _Nullable self);
+
+// @return Offset of byte-code of the method relative to start of the dex file.
+// @param out_size Optionally return size of byte-code in bytes.
+// Not thread-safe for calls on the same ADexFile instance.
+size_t ADexFile_Method_getCodeOffset(const ADexFile_Method* _Nonnull self,
+ size_t* _Nullable out_size);
+
+// @return Method name only (without class).
+// The encoding is slightly modified UTF8 (see Dex specification).
+// @param out_size Optionally return string size (excluding null-terminator).
+//
+// Returned data may be short lived: it must be copied before calling
+// this method again within the same ADexFile.
+// (it is currently long lived, but this is not guaranteed in the future).
+//
+// Not thread-safe for calls on the same ADexFile instance.
+const char* _Nonnull ADexFile_Method_getName(const ADexFile_Method* _Nonnull self,
+ size_t* _Nullable out_size);
+
+// @return Method name (with class name).
+// The encoding is slightly modified UTF8 (see Dex specification).
+// @param out_size Optionally return string size (excluding null-terminator).
+// @param with_params Whether to include method parameters and return type.
+//
+// Returned data may be short lived: it must be copied before calling
+// this method again within the same ADexFile.
+// (it points to pretty printing buffer within the ADexFile instance)
+//
+// Not thread-safe for calls on the same ADexFile instance.
+const char* _Nonnull ADexFile_Method_getQualifiedName(const ADexFile_Method* _Nonnull self,
+ int with_params,
+ size_t* _Nullable out_size);
+
+// @return Class descriptor (mangled class name).
+// The encoding is slightly modified UTF8 (see Dex specification).
+// @param out_size Optionally return string size (excluding null-terminator).
+//
+// Returned data may be short lived: it must be copied before calling
+// this method again within the same ADexFile.
+// (it is currently long lived, but this is not guaranteed in the future).
+//
+// Not thread-safe for calls on the same ADexFile instance.
+const char* _Nonnull ADexFile_Method_getClassDescriptor(const ADexFile_Method* _Nonnull self,
+ size_t* _Nullable out_size);
+
+// @return Compile-time literal or nullptr on error.
+const char* _Nullable ADexFile_Error_toString(ADexFile_Error self);
+
+__END_DECLS
#endif // ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_DEX_FILE_EXTERNAL_H_
diff --git a/libdexfile/external/include/art_api/dex_file_support.h b/libdexfile/external/include/art_api/dex_file_support.h
index 7c4ff5c..2361bf9 100644
--- a/libdexfile/external/include/art_api/dex_file_support.h
+++ b/libdexfile/external/include/art_api/dex_file_support.h
@@ -19,133 +19,114 @@
// C++ wrapper for the dex file external API.
-#include <cstring>
#include <memory>
#include <string>
-#include <string_view>
-#include <utility>
-#include <vector>
#include <android-base/macros.h>
-#include <android-base/mapped_file.h>
#include "art_api/dex_file_external.h"
namespace art_api {
namespace dex {
-// Returns true if libdexfile_external.so is already loaded. Otherwise tries to
+#define FOR_EACH_ADEX_FILE_SYMBOL(MACRO) \
+ MACRO(ADexFile_Error_toString) \
+ MACRO(ADexFile_Method_getClassDescriptor) \
+ MACRO(ADexFile_Method_getCodeOffset) \
+ MACRO(ADexFile_Method_getName) \
+ MACRO(ADexFile_Method_getQualifiedName) \
+ MACRO(ADexFile_create) \
+ MACRO(ADexFile_destroy) \
+ MACRO(ADexFile_findMethodAtOffset) \
+ MACRO(ADexFile_forEachMethod) \
+
+#define DEFINE_ADEX_FILE_SYMBOL(DLFUNC) extern decltype(DLFUNC)* g_##DLFUNC;
+FOR_EACH_ADEX_FILE_SYMBOL(DEFINE_ADEX_FILE_SYMBOL)
+#undef DEFINE_ADEX_FILE_SYMBOL
+
+// Returns true if libdexfile.so is already loaded. Otherwise tries to
// load it and returns true if successful. Otherwise returns false and sets
-// *error_msg. If false is returned then calling any function below may abort
-// the process. Thread safe.
-bool TryLoadLibdexfileExternal(std::string* error_msg);
+// *error_msg. Thread safe.
+bool TryLoadLibdexfile(std::string* error_msg);
-// Loads the libdexfile_external.so library and sets up function pointers.
-// Aborts with a fatal error on any error. For internal use by the classes
-// below.
-void LoadLibdexfileExternal();
+// TryLoadLibdexfile and fatally abort process if unsuccessful.
+void LoadLibdexfile();
-using DexString = std::string;
-
-struct MethodInfo {
- uint32_t offset = 0; // Code offset relative to the start of the dex file header
- uint32_t len = 0; // Code length
- DexString name;
-};
-
-inline bool operator==(const MethodInfo& s1, const MethodInfo& s2) {
- return s1.offset == s2.offset && s1.len == s2.len && s1.name == s2.name;
-}
-
-// External stable API to access ordinary dex files and CompactDex. This wraps
-// the stable C ABI and handles instance ownership. Thread-compatible but not
-// thread-safe.
+// API for reading ordinary dex files and CompactDex files.
+// It is minimal 1:1 C++ wrapper around the C ABI.
+// See documentation in dex_file_external.h
class DexFile {
public:
- DexFile(DexFile&& dex_file) noexcept {
- std::swap(ext_dex_file_, dex_file.ext_dex_file_);
- std::swap(map_, dex_file.map_);
+ struct Method {
+ size_t GetCodeOffset(size_t* out_size = nullptr) const {
+ return g_ADexFile_Method_getCodeOffset(self, out_size);
+ }
+
+ const char* GetName(size_t* out_size = nullptr) const {
+ return g_ADexFile_Method_getName(self, out_size);
+ }
+
+ const char* GetQualifiedName(bool with_params = false, size_t* out_size = nullptr) const {
+ return g_ADexFile_Method_getQualifiedName(self, with_params, out_size);
+ }
+
+ const char* GetClassDescriptor(size_t* out_size = nullptr) const {
+ return g_ADexFile_Method_getClassDescriptor(self, out_size);
+ }
+
+ const ADexFile_Method* const self;
+ };
+
+ struct Error {
+ const char* ToString() const {
+ return g_ADexFile_Error_toString(self);
+ }
+
+ bool Ok() const {
+ return self == ADEXFILE_ERROR_OK;
+ }
+
+ ADexFile_Error Code() {
+ return self;
+ }
+
+ ADexFile_Error const self;
+ };
+
+ static Error Create(const void* address,
+ size_t size,
+ size_t* new_size,
+ const char* location,
+ /*out*/ std::unique_ptr<DexFile>* out_dex_file) {
+ LoadLibdexfile();
+ ADexFile* adex = nullptr;
+ ADexFile_Error error = g_ADexFile_create(address, size, new_size, location, &adex);
+ if (adex != nullptr) {
+ *out_dex_file = std::unique_ptr<DexFile>(new DexFile{adex});
+ }
+ return Error{error};
}
- explicit DexFile(std::unique_ptr<DexFile>& dex_file) noexcept {
- std::swap(ext_dex_file_, dex_file->ext_dex_file_);
- std::swap(map_, dex_file->map_);
- dex_file.reset();
+ virtual ~DexFile() {
+ g_ADexFile_destroy(self_);
}
- virtual ~DexFile();
-
- // Interprets a chunk of memory as a dex file. As long as *size is too small,
- // returns nullptr, sets *size to a new size to try again with, and sets
- // *error_msg. That might happen repeatedly. Also returns nullptr
- // on error in which case *error_msg is set to a nonempty string.
- //
- // location is a string that describes the dex file, and is preferably its
- // path. It is mostly used to make error messages better, and may be "".
- //
- // The caller must retain the memory.
- static std::unique_ptr<DexFile> OpenFromMemory(const void* addr,
- size_t* size,
- const std::string& location,
- /*out*/ std::string* error_msg);
-
- // mmaps the given file offset in the open fd and reads a dexfile from there.
- // Returns nullptr on error in which case *error_msg is set.
- //
- // location is a string that describes the dex file, and is preferably its
- // path. It is mostly used to make error messages better, and may be "".
- static std::unique_ptr<DexFile> OpenFromFd(int fd,
- off_t offset,
- const std::string& location,
- /*out*/ std::string* error_msg);
-
- // Given an offset relative to the start of the dex file header, if there is a
- // method whose instruction range includes that offset then calls the provided
- // callback with ExtDexFileMethodInfo* (which is live only during the callback).
- template<typename T /* lambda taking (ExtDexFileMethodInfo*) */>
- void GetMethodInfoForOffset(int64_t dex_offset, T& callback, uint32_t flags = 0) {
- auto cb = [](void* ctx, ExtDexFileMethodInfo* info) { (*reinterpret_cast<T*>(ctx))(info); };
- g_ExtDexFileGetMethodInfoForOffset(ext_dex_file_, dex_offset, flags, cb, &callback);
+ template<typename T /* lambda which takes (const DexFile::Method&) as argument */>
+ inline size_t FindMethodAtOffset(uint32_t dex_offset, T callback) {
+ auto cb = [](void* ctx, const ADexFile_Method* m) { (*reinterpret_cast<T*>(ctx))(Method{m}); };
+ return g_ADexFile_findMethodAtOffset(self_, dex_offset, cb, &callback);
}
- // Given an offset relative to the start of the dex file header, if there is a
- // method whose instruction range includes that offset then returns info about
- // it, otherwise returns a struct with offset == 0. MethodInfo.name receives
- // the full function signature if with_signature is set, otherwise it gets the
- // class and method name only.
- MethodInfo GetMethodInfoForOffset(int64_t dex_offset, bool with_signature);
-
- // Call the provided callback for all dex methods.
- template<typename T /* lambda taking (ExtDexFileMethodInfo*) */>
- void GetAllMethodInfos(T& callback, uint32_t flags = 0) {
- auto cb = [](void* ctx, ExtDexFileMethodInfo* info) { (*reinterpret_cast<T*>(ctx))(info); };
- g_ExtDexFileGetAllMethodInfos(ext_dex_file_, flags, cb, &callback);
+ template<typename T /* lambda which takes (const DexFile::Method&) as argument */>
+ inline size_t ForEachMethod(T callback) {
+ auto cb = [](void* ctx, const ADexFile_Method* m) { (*reinterpret_cast<T*>(ctx))(Method{m}); };
+ return g_ADexFile_forEachMethod(self_, cb, &callback);
}
- // Returns info structs about all methods in the dex file. MethodInfo.name
- // receives the full function signature if with_signature is set, otherwise it
- // gets the class and method name only.
- std::vector<MethodInfo> GetAllMethodInfos(bool with_signature);
+ protected:
+ explicit DexFile(ADexFile* self) : self_(self) {}
- private:
- static inline MethodInfo AbsorbMethodInfo(const ExtDexFileMethodInfo* info) {
- return {
- .offset = info->addr,
- .len = info->size,
- .name = std::string(info->name, info->name_size)
- };
- }
-
- friend bool TryLoadLibdexfileExternal(std::string* error_msg);
- explicit DexFile(ExtDexFile* ext_dex_file) : ext_dex_file_(ext_dex_file) {}
- ExtDexFile* ext_dex_file_ = nullptr; // Owned instance. nullptr only in moved-from zombies.
- std::unique_ptr<android::base::MappedFile> map_; // Owned map (if we allocated one).
-
- // These are initialized by TryLoadLibdexfileExternal.
- static decltype(ExtDexFileOpenFromMemory)* g_ExtDexFileOpenFromMemory;
- static decltype(ExtDexFileGetMethodInfoForOffset)* g_ExtDexFileGetMethodInfoForOffset;
- static decltype(ExtDexFileGetAllMethodInfos)* g_ExtDexFileGetAllMethodInfos;
- static decltype(ExtDexFileClose)* g_ExtDexFileClose;
+ ADexFile* const self_;
DISALLOW_COPY_AND_ASSIGN(DexFile);
};
diff --git a/libdexfile/external/libdexfile_external.map.txt b/libdexfile/external/libdexfile_external.map.txt
deleted file mode 100644
index e9d12fb..0000000
--- a/libdexfile/external/libdexfile_external.map.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-LIBDEXFILE_EXTERNAL_1 {
- global:
- ExtDexFileClose;
- ExtDexFileGetAllMethodInfos;
- ExtDexFileGetMethodInfoForOffset;
- ExtDexFileOpenFromMemory;
- local:
- *;
-};