diff options
| -rw-r--r-- | libdexfile/Android.bp | 5 | ||||
| -rw-r--r-- | libdexfile/external/dex_file_ext.cc | 242 | ||||
| -rw-r--r-- | libdexfile/external/dex_file_supp.cc | 105 | ||||
| -rw-r--r-- | libdexfile/external/dex_file_supp_test.cc | 69 | ||||
| -rw-r--r-- | libdexfile/external/include/art_api/dex_file_external.h | 65 | ||||
| -rw-r--r-- | libdexfile/external/include/art_api/dex_file_support.h | 153 | ||||
| -rw-r--r-- | libdexfile/external/libdexfile_external.map.txt | 6 |
7 files changed, 221 insertions, 424 deletions
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp index 63f2827c26..8aa287fbd8 100644 --- a/libdexfile/Android.bp +++ b/libdexfile/Android.bp @@ -395,7 +395,10 @@ art_cc_library { "external/dex_file_supp.cc", ], runtime_libs: ["libdexfile_external"], - shared_libs: ["liblog"], + shared_libs: [ + "liblog", + "libbase", + ], header_libs: ["libdexfile_external_headers"], export_header_lib_headers: ["libdexfile_external_headers"], diff --git a/libdexfile/external/dex_file_ext.cc b/libdexfile/external/dex_file_ext.cc index c574ff3e05..65c0920bcf 100644 --- a/libdexfile/external/dex_file_ext.cc +++ b/libdexfile/external/dex_file_ext.cc @@ -42,74 +42,29 @@ #include <dex/dex_file-inl.h> #include <dex/dex_file_loader.h> -namespace art { -namespace { - -struct MethodCacheEntry { - int32_t offset; // Offset relative to the start of the dex file header. - int32_t len; - int32_t index; // Method index. -}; - -class MappedFileContainer : public DexFileContainer { - public: - explicit MappedFileContainer(std::unique_ptr<android::base::MappedFile>&& map) - : map_(std::move(map)) {} - ~MappedFileContainer() override {} - int GetPermissions() override { return 0; } - bool IsReadOnly() override { return true; } - bool EnableWrite() override { return false; } - bool DisableWrite() override { return false; } - - private: - std::unique_ptr<android::base::MappedFile> map_; - DISALLOW_COPY_AND_ASSIGN(MappedFileContainer); -}; - -} // namespace -} // namespace art - extern "C" { - -struct ExtDexFileString { - const std::string str_; -}; - -static const ExtDexFileString empty_string{""}; - -const ExtDexFileString* ExtDexFileMakeString(const char* str, size_t size) { - if (size == 0) { - return &empty_string; - } - return new ExtDexFileString{std::string(str, size)}; -} - -const char* ExtDexFileGetString(const ExtDexFileString* ext_string, /*out*/ size_t* size) { - DCHECK(ext_string != nullptr); - *size = ext_string->str_.size(); - return ext_string->str_.data(); -} - -void ExtDexFileFreeString(const ExtDexFileString* ext_string) { - DCHECK(ext_string != nullptr); - if (ext_string != &empty_string) { - delete (ext_string); - } -} - // 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. + }; + public: std::unique_ptr<const art::DexFile> dex_file_; explicit ExtDexFile(std::unique_ptr<const art::DexFile>&& dex_file) : dex_file_(std::move(dex_file)) {} - art::MethodCacheEntry* GetMethodCacheEntryForOffset(int64_t dex_offset) { + bool GetMethodDefIndex(uint32_t dex_offset, uint32_t* index, uint32_t* addr, uint32_t* size) { // First look in the method cache. auto it = method_cache_.upper_bound(dex_offset); if (it != method_cache_.end() && dex_offset >= it->second.offset) { - return &it->second; + *index = it->second.index; + *addr = it->second.offset; + *size = it->second.len; + return true; } uint32_t class_def_index; @@ -122,17 +77,18 @@ struct ExtDexFile { continue; } - int32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin(); - int32_t len = code.InsnsSizeInBytes(); + 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) { - int32_t index = method.GetIndex(); - auto res = method_cache_.emplace(offset + len, art::MethodCacheEntry{offset, len, index}); - return &res.first->second; + *index = method.GetIndex(); + *addr = offset; + *size = len; + method_cache_.emplace(offset + len, MethodCacheEntry{offset, len, *index}); + return true; } } } - - return nullptr; + return false; } private: @@ -183,18 +139,16 @@ struct ExtDexFile { // Binary search table with (end_dex_offset, class_def_index) entries. std::vector<std::pair<uint32_t, uint32_t>> class_cache_; - std::map<uint32_t, art::MethodCacheEntry> method_cache_; // end_dex_offset -> method. + std::map<uint32_t, MethodCacheEntry> method_cache_; // end_dex_offset -> method. }; int ExtDexFileOpenFromMemory(const void* addr, /*inout*/ size_t* size, const char* location, - /*out*/ const ExtDexFileString** ext_error_msg, /*out*/ ExtDexFile** ext_dex_file) { if (*size < sizeof(art::DexFile::Header)) { *size = sizeof(art::DexFile::Header); - *ext_error_msg = nullptr; - return false; + return kExtDexFileNotEnoughData; } const art::DexFile::Header* header = reinterpret_cast<const art::DexFile::Header*>(addr); @@ -206,23 +160,18 @@ int ExtDexFileOpenFromMemory(const void* addr, // 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)) { - *ext_error_msg = new ExtDexFileString{ - android::base::StringPrintf("Corrupt CompactDexFile header in '%s'", location)}; - return false; + return kExtDexFileInvalidHeader; } if (computed_file_size > file_size) { file_size = computed_file_size; } } else if (!art::StandardDexFile::IsMagicValid(header->magic_)) { - *ext_error_msg = new ExtDexFileString{ - android::base::StringPrintf("Unrecognized dex file header in '%s'", location)}; - return false; + return kExtDexFileInvalidHeader; } if (*size < file_size) { *size = file_size; - *ext_error_msg = nullptr; - return false; + return kExtDexFileNotEnoughData; } std::string loc_str(location); @@ -237,99 +186,19 @@ int ExtDexFileOpenFromMemory(const void* addr, /*verify_checksum=*/false, &error_msg); if (dex_file == nullptr) { - *ext_error_msg = new ExtDexFileString{std::move(error_msg)}; - return false; - } - - *ext_dex_file = new ExtDexFile(std::move(dex_file)); - return true; -} - -int ExtDexFileOpenFromFd(int fd, - off_t offset, - const char* location, - /*out*/ const ExtDexFileString** ext_error_msg, - /*out*/ ExtDexFile** ext_dex_file) { - size_t length; - { - struct stat sbuf; - std::memset(&sbuf, 0, sizeof(sbuf)); - if (fstat(fd, &sbuf) == -1) { - *ext_error_msg = new ExtDexFileString{ - android::base::StringPrintf("fstat '%s' failed: %s", location, std::strerror(errno))}; - return false; - } - if (S_ISDIR(sbuf.st_mode)) { - *ext_error_msg = new ExtDexFileString{ - android::base::StringPrintf("Attempt to mmap directory '%s'", location)}; - return false; - } - length = sbuf.st_size; - } - - if (length < offset + sizeof(art::DexFile::Header)) { - *ext_error_msg = new ExtDexFileString{android::base::StringPrintf( - "Offset %" PRId64 " too large for '%s' of size %zu", - int64_t{offset}, - location, - length)}; - return false; - } - - // Cannot use MemMap in libartbase here, because it pulls in dlopen which we - // can't have when being compiled statically. - std::unique_ptr<android::base::MappedFile> map = - android::base::MappedFile::FromFd(fd, offset, length, PROT_READ); - if (map == nullptr) { - *ext_error_msg = new ExtDexFileString{ - android::base::StringPrintf("mmap '%s' failed: %s", location, std::strerror(errno))}; - return false; - } - - const art::DexFile::Header* header = reinterpret_cast<const art::DexFile::Header*>(map->data()); - uint32_t file_size; - if (__builtin_add_overflow(offset, header->file_size_, &file_size)) { - *ext_error_msg = - new ExtDexFileString{android::base::StringPrintf("Corrupt header in '%s'", location)}; - return false; - } - if (length < file_size) { - *ext_error_msg = new ExtDexFileString{ - android::base::StringPrintf("Dex file '%s' too short: expected %" PRIu32 ", got %" PRIu64, - location, - file_size, - uint64_t{length})}; - return false; + LOG(ERROR) << "Can not opend dex file " << loc_str << ": " << error_msg; + return kExtDexFileError; } - void* addr = map->data(); - size_t size = map->size(); - auto container = std::make_unique<art::MappedFileContainer>(std::move(map)); - - std::string loc_str(location); - std::string error_msg; - art::DexFileLoader loader; - std::unique_ptr<const art::DexFile> dex_file = loader.Open(reinterpret_cast<const uint8_t*>(addr), - size, - loc_str, - header->checksum_, - /*oat_dex_file=*/nullptr, - /*verify=*/false, - /*verify_checksum=*/false, - &error_msg, - std::move(container)); - if (dex_file == nullptr) { - *ext_error_msg = new ExtDexFileString{std::move(error_msg)}; - return false; - } *ext_dex_file = new ExtDexFile(std::move(dex_file)); - return true; + return kExtDexFileOk; } int ExtDexFileGetMethodInfoForOffset(ExtDexFile* ext_dex_file, - int64_t dex_offset, - int with_signature, - /*out*/ ExtDexFileMethodInfo* method_info) { + 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. } @@ -346,40 +215,49 @@ int ExtDexFileGetMethodInfoForOffset(ExtDexFile* ext_dex_file, } } - art::MethodCacheEntry* entry = ext_dex_file->GetMethodCacheEntryForOffset(dex_offset); - if (entry != nullptr) { - method_info->offset = entry->offset; - method_info->len = entry->len; - method_info->name = - new ExtDexFileString{ext_dex_file->dex_file_->PrettyMethod(entry->index, with_signature)}; - return true; + uint32_t method_index, addr, size; + if (!ext_dex_file->GetMethodDefIndex(dex_offset, &method_index, &addr, &size)) { + return false; } - return false; + 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; } void ExtDexFileGetAllMethodInfos(ExtDexFile* ext_dex_file, - int with_signature, + 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()) { for (const art::ClassAccessor::Method& method : accessor.GetMethods()) { art::CodeItemInstructionAccessor code = method.GetInstructions(); - if (!code.HasCodeItem()) { - continue; + 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 = code.InsnsSizeInBytes(), + .name = name.c_str(), + .name_size = name.size() + }; + method_info_cb(user_data, &info); } - - ExtDexFileMethodInfo method_info; - method_info.offset = static_cast<int32_t>(reinterpret_cast<const uint8_t*>(code.Insns()) - - ext_dex_file->dex_file_->Begin()); - method_info.len = code.InsnsSizeInBytes(); - method_info.name = new ExtDexFileString{ - ext_dex_file->dex_file_->PrettyMethod(method.GetIndex(), with_signature)}; - method_info_cb(&method_info, user_data); } } } -void ExtDexFileFree(ExtDexFile* ext_dex_file) { delete (ext_dex_file); } +void ExtDexFileClose(ExtDexFile* ext_dex_file) { delete (ext_dex_file); } } // extern "C" diff --git a/libdexfile/external/dex_file_supp.cc b/libdexfile/external/dex_file_supp.cc index c0bfe544fe..7200451a31 100644 --- a/libdexfile/external/dex_file_supp.cc +++ b/libdexfile/external/dex_file_supp.cc @@ -17,7 +17,12 @@ #include "art_api/dex_file_support.h" #include <dlfcn.h> +#include <inttypes.h> #include <mutex> +#include <sys/stat.h> + +#include <android-base/mapped_file.h> +#include <android-base/stringprintf.h> #ifndef STATIC_LIB // Not used in the static lib, so avoid a dependency on this header in @@ -29,14 +34,10 @@ namespace art_api { namespace dex { #define FOR_ALL_DLFUNCS(MACRO) \ - MACRO(DexString, ExtDexFileMakeString) \ - MACRO(DexString, ExtDexFileGetString) \ - MACRO(DexString, ExtDexFileFreeString) \ MACRO(DexFile, ExtDexFileOpenFromMemory) \ - MACRO(DexFile, ExtDexFileOpenFromFd) \ MACRO(DexFile, ExtDexFileGetMethodInfoForOffset) \ MACRO(DexFile, ExtDexFileGetAllMethodInfos) \ - MACRO(DexFile, ExtDexFileFree) + MACRO(DexFile, ExtDexFileClose) #ifdef STATIC_LIB #define DEFINE_DLFUNC_PTR(CLASS, DLFUNC) decltype(DLFUNC)* CLASS::g_##DLFUNC = DLFUNC; @@ -103,15 +104,97 @@ void LoadLibdexfileExternal() { #endif } -DexFile::~DexFile() { g_ExtDexFileFree(ext_dex_file_); } +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::AbsorbMethodInfo(const ExtDexFileMethodInfo& ext_method_info) { - return {ext_method_info.offset, ext_method_info.len, DexString(ext_method_info.name)}; +MethodInfo DexFile::GetMethodInfoForOffset(int64_t dex_offset, bool with_signature) { + MethodInfo res{}; + auto set_method = [](void* ctx, ExtDexFileMethodInfo* info) { + *reinterpret_cast<MethodInfo*>(ctx) = AbsorbMethodInfo(info); + }; + uint32_t flags = with_signature ? kExtDexFileWithSignature : 0; + g_ExtDexFileGetMethodInfoForOffset(ext_dex_file_, dex_offset, flags, set_method, &res); + return res; } -void DexFile::AddMethodInfoCallback(const ExtDexFileMethodInfo* ext_method_info, void* ctx) { - auto vect = static_cast<MethodInfoVector*>(ctx); - vect->emplace_back(AbsorbMethodInfo(*ext_method_info)); +std::vector<MethodInfo> DexFile::GetAllMethodInfos(bool with_signature) { + std::vector<MethodInfo> res; + auto add_method = [](void* ctx, ExtDexFileMethodInfo* info) { + reinterpret_cast<std::vector<MethodInfo>*>(ctx)->push_back(AbsorbMethodInfo(info)); + }; + uint32_t flags = with_signature ? kExtDexFileWithSignature : 0; + g_ExtDexFileGetAllMethodInfos(ext_dex_file_, flags, add_method, &res); + return res; } } // namespace dex diff --git a/libdexfile/external/dex_file_supp_test.cc b/libdexfile/external/dex_file_supp_test.cc index 0d7644a2a4..ff263fc539 100644 --- a/libdexfile/external/dex_file_supp_test.cc +++ b/libdexfile/external/dex_file_supp_test.cc @@ -49,69 +49,12 @@ static constexpr uint32_t kDexData[] = { 0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c, }; -TEST(DexStringTest, alloc_string) { - auto s = DexString("123"); - EXPECT_EQ(std::string_view(s), "123"); -} - -TEST(DexStringTest, alloc_empty_string) { - auto s = DexString(""); - EXPECT_TRUE(std::string_view(s).empty()); -} - -TEST(DexStringTest, move_construct) { - auto s1 = DexString("foo"); - auto s2 = DexString(std::move(s1)); - EXPECT_TRUE(std::string_view(s1).empty()); // NOLINT bugprone-use-after-move - EXPECT_EQ(std::string_view(s2), "foo"); -} - -TEST(DexStringTest, move_assign) { - auto s1 = DexString("foo"); - DexString s2; - EXPECT_TRUE(std::string_view(s2).empty()); - s2 = std::move(s1); - EXPECT_TRUE(std::string_view(s1).empty()); // NOLINT bugprone-use-after-move - EXPECT_EQ(std::string_view(s2), "foo"); -} - -TEST(DexStringTest, reassign) { - auto s = DexString("foo"); - s = DexString("bar"); - EXPECT_EQ(std::string_view(s), "bar"); -} - -TEST(DexStringTest, data_access) { - auto s = DexString("foo"); - EXPECT_STREQ(s.data(), "foo"); - EXPECT_STREQ(s.c_str(), "foo"); -} - -TEST(DexStringTest, size_access) { - auto s = DexString("foo"); - EXPECT_EQ(s.size(), size_t{3}); - EXPECT_EQ(s.length(), size_t{3}); -} - -TEST(DexStringTest, equality) { - auto s = DexString("foo"); - EXPECT_EQ(s, DexString("foo")); - EXPECT_FALSE(s == DexString("bar")); -} - -TEST(DexStringTest, equality_with_nul) { - auto s = DexString(std::string("foo\0bar", 7)); - EXPECT_EQ(s.size(), size_t{7}); - EXPECT_EQ(s, DexString(std::string("foo\0bar", 7))); - EXPECT_FALSE(s == DexString(std::string("foo\0baz", 7))); -} - 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_TRUE(error_msg.empty()); + EXPECT_FALSE(error_msg.empty()); } TEST(DexFileTest, from_memory_file_too_small) { @@ -119,7 +62,7 @@ TEST(DexFileTest, from_memory_file_too_small) { std::string error_msg; EXPECT_EQ(DexFile::OpenFromMemory(kDexData, &size, "", &error_msg), nullptr); EXPECT_EQ(size, sizeof(kDexData)); - EXPECT_TRUE(error_msg.empty()); + EXPECT_FALSE(error_msg.empty()); } static std::unique_ptr<DexFile> GetTestDexData() { @@ -259,8 +202,8 @@ TEST(DexFileTest, get_all_method_infos_without_signature) { ASSERT_NE(dex_file, nullptr); std::vector<MethodInfo> infos; - infos.emplace_back(MethodInfo{0x100, 8, DexString("Main.<init>")}); - infos.emplace_back(MethodInfo{0x118, 2, DexString("Main.main")}); + 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); } @@ -269,8 +212,8 @@ TEST(DexFileTest, get_all_method_infos_with_signature) { ASSERT_NE(dex_file, nullptr); std::vector<MethodInfo> infos; - infos.emplace_back(MethodInfo{0x100, 8, DexString("void Main.<init>()")}); - infos.emplace_back(MethodInfo{0x118, 2, DexString("void Main.main(java.lang.String[])")}); + 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); } diff --git a/libdexfile/external/include/art_api/dex_file_external.h b/libdexfile/external/include/art_api/dex_file_external.h index b29e759644..b880335b15 100644 --- a/libdexfile/external/include/art_api/dex_file_external.h +++ b/libdexfile/external/include/art_api/dex_file_external.h @@ -20,6 +20,7 @@ // Dex file external API #include <sys/types.h> +#include <stdint.h> #ifdef __cplusplus extern "C" { @@ -28,59 +29,53 @@ extern "C" { // 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. -// Opaque wrapper for an std::string allocated in libdexfile which must be freed -// using ExtDexFileFreeString. -struct ExtDexFileString; - -// Returns an ExtDexFileString initialized to the given string. -const struct ExtDexFileString* ExtDexFileMakeString(const char* str, size_t size); - -// Returns a pointer to the underlying null-terminated character array and its -// size for the given ExtDexFileString. -const char* ExtDexFileGetString(const struct ExtDexFileString* ext_string, /*out*/ size_t* size); +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; +}; -// Frees an ExtDexFileString. -void ExtDexFileFreeString(const struct ExtDexFileString* ext_string); +enum ExtDexFileError { + kExtDexFileOk = 0, + kExtDexFileError = 1, // Unspecified error. + kExtDexFileNotEnoughData = 2, + kExtDexFileInvalidHeader = 3, +}; -struct ExtDexFileMethodInfo { - int32_t offset; - int32_t len; - const struct ExtDexFileString* name; +enum ExtDexFileMethodFlags { + kExtDexFileWithSignature = 1, }; struct ExtDexFile; -// See art_api::dex::DexFile::OpenFromMemory. Returns true on success. +// 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*/ const struct ExtDexFileString** error_msg, /*out*/ struct ExtDexFile** ext_dex_file); -// See art_api::dex::DexFile::OpenFromFd. Returns true on success. -int ExtDexFileOpenFromFd(int fd, - off_t offset, - const char* location, - /*out*/ const struct ExtDexFileString** error_msg, - /*out*/ struct ExtDexFile** ext_dex_file); +// Callback used to return information about a dex method. +typedef void ExtDexFileMethodInfoCallback(void* user_data, + struct ExtDexFileMethodInfo* method_info); -// See art_api::dex::DexFile::GetMethodInfoForOffset. Returns true on success. +// Find a single dex method based on the given dex offset. int ExtDexFileGetMethodInfoForOffset(struct ExtDexFile* ext_dex_file, - int64_t dex_offset, - int with_signature, - /*out*/ struct ExtDexFileMethodInfo* method_info); - -typedef void ExtDexFileMethodInfoCallback(const struct ExtDexFileMethodInfo* ext_method_info, - void* user_data); + uint32_t dex_offset, + uint32_t flags, + ExtDexFileMethodInfoCallback* method_info_cb, + void* user_data); -// See art_api::dex::DexFile::GetAllMethodInfos. +// Return all dex methods in the dex file. void ExtDexFileGetAllMethodInfos(struct ExtDexFile* ext_dex_file, - int with_signature, + uint32_t flags, ExtDexFileMethodInfoCallback* method_info_cb, void* user_data); -// Frees an ExtDexFile. -void ExtDexFileFree(struct ExtDexFile* ext_dex_file); +// Release all associated memory. +void ExtDexFileClose(struct ExtDexFile* ext_dex_file); #ifdef __cplusplus } // extern "C" diff --git a/libdexfile/external/include/art_api/dex_file_support.h b/libdexfile/external/include/art_api/dex_file_support.h index 404fa6544b..3fbf16933c 100644 --- a/libdexfile/external/include/art_api/dex_file_support.h +++ b/libdexfile/external/include/art_api/dex_file_support.h @@ -27,6 +27,7 @@ #include <vector> #include <android-base/macros.h> +#include <android-base/mapped_file.h> #include "art_api/dex_file_external.h" @@ -44,75 +45,11 @@ bool TryLoadLibdexfileExternal(std::string* error_msg); // below. void LoadLibdexfileExternal(); -// Minimal std::string look-alike for a string returned from libdexfile. -class DexString final { - public: - DexString(DexString&& dex_str) noexcept : ext_string_(dex_str.ext_string_) { - dex_str.ext_string_ = MakeExtDexFileString("", 0); - } - explicit DexString(const char* str = "") - : ext_string_(MakeExtDexFileString(str, std::strlen(str))) {} - explicit DexString(std::string_view str) - : ext_string_(MakeExtDexFileString(str.data(), str.size())) {} - ~DexString() { g_ExtDexFileFreeString(ext_string_); } - - DexString& operator=(DexString&& dex_str) noexcept { - std::swap(ext_string_, dex_str.ext_string_); - return *this; - } - - const char* data() const { - size_t ignored; - return g_ExtDexFileGetString(ext_string_, &ignored); - } - const char* c_str() const { return data(); } - - size_t size() const { - size_t len; - (void)g_ExtDexFileGetString(ext_string_, &len); - return len; - } - size_t length() const { return size(); } - - operator std::string_view() const { - size_t len; - const char* chars = g_ExtDexFileGetString(ext_string_, &len); - return std::string_view(chars, len); - } - - private: - friend bool TryLoadLibdexfileExternal(std::string* error_msg); - friend class DexFile; - friend bool operator==(const DexString&, const DexString&); - explicit DexString(const ExtDexFileString* ext_string) : ext_string_(ext_string) {} - const ExtDexFileString* ext_string_; // Owned instance. Never nullptr. - - // These are initialized by TryLoadLibdexfileExternal. - static decltype(ExtDexFileMakeString)* g_ExtDexFileMakeString; - static decltype(ExtDexFileGetString)* g_ExtDexFileGetString; - static decltype(ExtDexFileFreeString)* g_ExtDexFileFreeString; - - static const struct ExtDexFileString* MakeExtDexFileString(const char* str, size_t size) { - if (UNLIKELY(g_ExtDexFileMakeString == nullptr)) { - LoadLibdexfileExternal(); - } - return g_ExtDexFileMakeString(str, size); - } - - DISALLOW_COPY_AND_ASSIGN(DexString); -}; - -inline bool operator==(const DexString& s1, const DexString& s2) { - size_t l1, l2; - const char* str1 = DexString::g_ExtDexFileGetString(s1.ext_string_, &l1); - const char* str2 = DexString::g_ExtDexFileGetString(s2.ext_string_, &l2); - // Use memcmp to avoid assumption about absence of null characters in the strings. - return l1 == l2 && !std::memcmp(str1, str2, l1); -} +using DexString = std::string; struct MethodInfo { - int32_t offset; // Code offset relative to the start of the dex file header - int32_t len; // Code length + uint32_t offset = 0; // Code offset relative to the start of the dex file header + uint32_t len = 0; // Code length DexString name; }; @@ -126,20 +63,21 @@ inline bool operator==(const MethodInfo& s1, const MethodInfo& s2) { class DexFile { public: DexFile(DexFile&& dex_file) noexcept { - ext_dex_file_ = dex_file.ext_dex_file_; - dex_file.ext_dex_file_ = nullptr; + std::swap(ext_dex_file_, dex_file.ext_dex_file_); + std::swap(map_, dex_file.map_); } explicit DexFile(std::unique_ptr<DexFile>& dex_file) noexcept { - ext_dex_file_ = dex_file->ext_dex_file_; - dex_file->ext_dex_file_ = nullptr; - dex_file.reset(nullptr); + std::swap(ext_dex_file_, dex_file->ext_dex_file_); + std::swap(map_, dex_file->map_); + dex_file.reset(); } + 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 to "". That might happen repeatedly. Also returns nullptr + // *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 @@ -149,20 +87,7 @@ class DexFile { static std::unique_ptr<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; - const ExtDexFileString* ext_error_msg = nullptr; - if (g_ExtDexFileOpenFromMemory(addr, size, location.c_str(), &ext_error_msg, &ext_dex_file)) { - return std::unique_ptr<DexFile>(new DexFile(ext_dex_file)); - } - *error_msg = (ext_error_msg == nullptr) ? "" : std::string(DexString(ext_error_msg)); - return nullptr; - } + /*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. @@ -172,65 +97,39 @@ class DexFile { static std::unique_ptr<DexFile> OpenFromFd(int fd, off_t offset, const std::string& location, - /*out*/ std::string* error_msg) { - if (UNLIKELY(g_ExtDexFileOpenFromFd == nullptr)) { - // Load libdexfile_external.so in this factory function, so instance - // methods don't need to check this. - LoadLibdexfileExternal(); - } - ExtDexFile* ext_dex_file; - const ExtDexFileString* ext_error_msg = nullptr; - if (g_ExtDexFileOpenFromFd(fd, offset, location.c_str(), &ext_error_msg, &ext_dex_file)) { - return std::unique_ptr<DexFile>(new DexFile(ext_dex_file)); - } - *error_msg = std::string(DexString(ext_error_msg)); - return nullptr; - } + /*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 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) { - ExtDexFileMethodInfo ext_method_info; - if (g_ExtDexFileGetMethodInfoForOffset(ext_dex_file_, - dex_offset, - with_signature, - &ext_method_info)) { - return AbsorbMethodInfo(ext_method_info); - } - return {/*offset=*/0, /*len=*/0, /*name=*/DexString()}; - } + MethodInfo GetMethodInfoForOffset(int64_t dex_offset, bool with_signature); // 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) { - MethodInfoVector res; - g_ExtDexFileGetAllMethodInfos(ext_dex_file_, - with_signature, - AddMethodInfoCallback, - static_cast<void*>(&res)); - return res; - } + std::vector<MethodInfo> GetAllMethodInfos(bool with_signature); 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_; // Owned instance. nullptr only in moved-from zombies. - - typedef std::vector<MethodInfo> MethodInfoVector; - - static MethodInfo AbsorbMethodInfo(const ExtDexFileMethodInfo& ext_method_info); - static void AddMethodInfoCallback(const ExtDexFileMethodInfo* ext_method_info, void* user_data); + 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(ExtDexFileOpenFromFd)* g_ExtDexFileOpenFromFd; static decltype(ExtDexFileGetMethodInfoForOffset)* g_ExtDexFileGetMethodInfoForOffset; static decltype(ExtDexFileGetAllMethodInfos)* g_ExtDexFileGetAllMethodInfos; - static decltype(ExtDexFileFree)* g_ExtDexFileFree; + static decltype(ExtDexFileClose)* g_ExtDexFileClose; DISALLOW_COPY_AND_ASSIGN(DexFile); }; diff --git a/libdexfile/external/libdexfile_external.map.txt b/libdexfile/external/libdexfile_external.map.txt index 450b633d32..e9d12fbee6 100644 --- a/libdexfile/external/libdexfile_external.map.txt +++ b/libdexfile/external/libdexfile_external.map.txt @@ -1,12 +1,8 @@ LIBDEXFILE_EXTERNAL_1 { global: - ExtDexFileFree; - ExtDexFileFreeString; + ExtDexFileClose; ExtDexFileGetAllMethodInfos; ExtDexFileGetMethodInfoForOffset; - ExtDexFileGetString; - ExtDexFileMakeString; - ExtDexFileOpenFromFd; ExtDexFileOpenFromMemory; local: *; |