Add support for dex containers (DEX v41).
Allow multiple dex files within single "container"
(either a zip entry or a plain on-disk dex file).
This allows sharing of string (and other) dex data,
since the offsets can point to shared data payload.
Bug: 266950186
Test: test.py -b --host
Change-Id: I4f5901fd2f26a5a9dba427eb48c0fa5ddb6243d8
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 68a7d02..774a108 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1584,7 +1584,7 @@
CHECK(!DexFileLoader::IsMultiDexLocation(bcp_dex_files[bcp_df_pos]->GetLocation()));
++bcp_df_pos;
while (bcp_df_pos != bcp_df_end &&
- DexFileLoader::IsMultiDexLocation(bcp_dex_files[bcp_df_pos]->GetLocation().c_str())) {
+ DexFileLoader::IsMultiDexLocation(bcp_dex_files[bcp_df_pos]->GetLocation())) {
++bcp_df_pos;
}
}
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index ed57dc3..0effe91 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -1320,6 +1320,10 @@
ASSERT_EQ(oat_dex_files.size(), 1u);
// Check that the code sections match what we expect.
for (const OatDexFile* oat_dex : oat_dex_files) {
+ if (oat_dex->GetDexVersion() >= DexFile::kDexContainerVersion) {
+ continue; // Compact dex isn't supported together with dex container.
+ }
+
const DexLayoutSections* const sections = oat_dex->GetDexLayoutSections();
// Testing of logging the sections.
ASSERT_TRUE(sections != nullptr);
@@ -1433,6 +1437,10 @@
std::vector<std::unique_ptr<const CompactDexFile>> compact_dex_files;
for (const OatDexFile* oat_dex : oat_dex_files) {
std::unique_ptr<const DexFile> dex_file(oat_dex->OpenDexFile(&error_msg));
+ if (dex_file->HasDexContainer()) {
+ ASSERT_FALSE(dex_file->IsCompactDexFile());
+ continue; // Compact dex isn't supported together with dex container.
+ }
ASSERT_TRUE(dex_file != nullptr) << error_msg;
ASSERT_TRUE(dex_file->IsCompactDexFile());
compact_dex_files.push_back(
@@ -2011,7 +2019,7 @@
ASSERT_GT(header->file_size_,
sizeof(*header) + sizeof(dex::MapList) + sizeof(dex::MapItem) * 2);
// Move map list to be right after the header.
- header->map_off_ = sizeof(DexFile::Header);
+ header->map_off_ = header->header_size_;
dex::MapList* map_list = const_cast<dex::MapList*>(dex->GetMapList());
map_list->list_[0].type_ = DexFile::kDexTypeHeaderItem;
map_list->list_[0].size_ = 1u;
@@ -2022,6 +2030,7 @@
map_list->size_ = 2;
header->data_off_ = header->map_off_;
header->data_size_ = map_list->Size();
+ header->SetDexContainer(0, header->file_size_);
});
}
std::unique_ptr<const DexFile> dex_file(OpenDexFile(temp_dex.GetFilename().c_str()));
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 5dc070f..c5fcbc9 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -3145,6 +3145,15 @@
}
}
+ // Compact dex reader/writer does not understand dex containers,
+ // which is ok since dex containers replace compat-dex.
+ for (OatDexFile& oat_dex_file : oat_dex_files_) {
+ const DexFile* dex_file = oat_dex_file.GetDexFile();
+ if (dex_file->HasDexContainer()) {
+ compact_dex_level_ = CompactDexLevel::kCompactDexLevelNone;
+ }
+ }
+
if (extract_dex_files_into_vdex_) {
vdex_dex_files_offset_ = vdex_size_;
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
index 96fa9f2..c1cf6cd 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -789,7 +789,7 @@
}
void DexWriter::WriteHeader(Stream* stream) {
- StandardDexFile::Header header;
+ StandardDexFile::HeaderV41 header{};
if (CompactDexFile::IsMagicValid(header_->Magic())) {
StandardDexFile::WriteMagic(header.magic_.data());
if (header_->SupportDefaultMethods()) {
@@ -823,15 +823,17 @@
header.class_defs_off_ = header_->ClassDefs().GetOffset();
header.data_size_ = header_->DataSize();
header.data_off_ = header_->DataOffset();
+ header.SetDexContainer(0, header_->FileSize());
- CHECK_EQ(sizeof(header), GetHeaderSize());
- static_assert(sizeof(header) == 0x70, "Size doesn't match dex spec");
+ static_assert(sizeof(header) == 0x78, "Size doesn't match dex spec");
stream->Seek(0);
- stream->Overwrite(reinterpret_cast<uint8_t*>(&header), sizeof(header));
+ stream->Overwrite(reinterpret_cast<uint8_t*>(&header), GetHeaderSize());
}
size_t DexWriter::GetHeaderSize() const {
- return sizeof(StandardDexFile::Header);
+ return header_->Magic() == DexFile::Magic{'d', 'e', 'x', '\n', '0', '4', '1', '\0'} ?
+ sizeof(StandardDexFile::HeaderV41) :
+ sizeof(StandardDexFile::Header);
}
bool DexWriter::Write(DexContainer* output, std::string* error_msg) {
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 3db9cf3..e8d0a07 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -2197,6 +2197,14 @@
GetOptions()));
SetHeader(header.get());
+ // Dexlayout does not support containers, but allow it if it has just single dex file.
+ const DexFile::Header& hdr = dex_file->GetHeader();
+ if (hdr.HeaderOffset() != 0u || hdr.ContainerSize() != hdr.file_size_) {
+ *error_msg = "DEX containers are not supported in dexlayout";
+ DCHECK(false) << *error_msg;
+ return false;
+ }
+
if (options_.verbose_) {
fprintf(out_file_,
"Opened '%s', DEX version '%.3s'\n",
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 4e4811f..97c19ee 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -746,6 +746,7 @@
header.link_off_ = header.file_size_;
header.link_size_ = 16 * KB;
header.file_size_ += header.link_size_;
+ header.SetDexContainer(0, header.file_size_);
file_size = header.file_size_;
});
TEMP_FAILURE_RETRY(temp_dex.GetFile()->SetLength(file_size));
diff --git a/libdexfile/dex/code_item_accessors_test.cc b/libdexfile/dex/code_item_accessors_test.cc
index a923d04..0360361 100644
--- a/libdexfile/dex/code_item_accessors_test.cc
+++ b/libdexfile/dex/code_item_accessors_test.cc
@@ -41,6 +41,7 @@
auto* header = reinterpret_cast<DexFile::Header*>(data->data());
StandardDexFile::WriteMagic(data->data());
StandardDexFile::WriteCurrentVersion(data->data());
+ header->header_size_ = sizeof(*header);
header->file_size_ = data->size();
}
DexFileLoader dex_file_loader(data->data(), data->size(), "location");
diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc
index b27855e..39addb5 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -34,6 +34,7 @@
#include "base/leb128.h"
#include "base/stl_util.h"
#include "class_accessor-inl.h"
+#include "compact_dex_file.h"
#include "descriptors_names.h"
#include "dex_file-inl.h"
#include "standard_dex_file.h"
@@ -96,13 +97,51 @@
return container_->DisableWrite();
}
+bool DexFile::Header::HasDexContainer() const {
+ if (CompactDexFile::IsMagicValid(magic_.data())) {
+ return false;
+ }
+ DCHECK_EQ(header_size_, GetVersion() >= 41 ? sizeof(HeaderV41) : sizeof(Header));
+ return header_size_ >= sizeof(HeaderV41);
+}
+
+uint32_t DexFile::Header::HeaderOffset() const {
+ return HasDexContainer() ? reinterpret_cast<const HeaderV41*>(this)->header_offset_ : 0;
+}
+
+uint32_t DexFile::Header::ContainerSize() const {
+ return HasDexContainer() ? reinterpret_cast<const HeaderV41*>(this)->container_size_ : file_size_;
+}
+
+void DexFile::Header::SetDexContainer(size_t header_offset, size_t container_size) {
+ if (HasDexContainer()) {
+ DCHECK_LE(header_offset, container_size);
+ DCHECK_LE(file_size_, container_size - header_offset);
+ data_off_ = 0;
+ data_size_ = 0;
+ auto* headerV41 = reinterpret_cast<HeaderV41*>(this);
+ DCHECK_GE(header_size_, sizeof(*headerV41));
+ headerV41->header_offset_ = header_offset;
+ headerV41->container_size_ = container_size;
+ } else {
+ DCHECK_EQ(header_offset, 0u);
+ DCHECK_EQ(container_size, file_size_);
+ }
+}
+
template <typename T>
ALWAYS_INLINE const T* DexFile::GetSection(const uint32_t* offset, DexFileContainer* container) {
size_t size = container->End() - begin_;
if (size < sizeof(Header)) {
return nullptr; // Invalid dex file.
}
- return reinterpret_cast<const T*>(begin_ + *offset);
+ // Compact dex is inconsistent: section offsets are relative to the
+ // header as opposed to the data section like all other its offsets.
+ if (CompactDexFile::IsMagicValid(begin_)) {
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(header_);
+ return reinterpret_cast<const T*>(data + *offset);
+ }
+ return reinterpret_cast<const T*>(data_.data() + *offset);
}
DexFile::DexFile(const uint8_t* base,
@@ -205,7 +244,14 @@
size_t size = container->End() - data;
if (size >= sizeof(StandardDexFile::Header) && StandardDexFile::IsMagicValid(data)) {
auto header = reinterpret_cast<const DexFile::Header*>(data);
- size = header->file_size_;
+ CHECK_EQ(container->Data().size(), 0u) << "Unsupported for standard dex";
+ if (size >= sizeof(HeaderV41) && header->header_size_ >= sizeof(HeaderV41)) {
+ auto headerV41 = reinterpret_cast<const DexFile::HeaderV41*>(data);
+ data -= headerV41->header_offset_; // Allow underflow and later overflow.
+ size = headerV41->container_size_;
+ } else {
+ size = header->file_size_;
+ }
} else if (size >= sizeof(CompactDexFile::Header) && CompactDexFile::IsMagicValid(data)) {
auto header = reinterpret_cast<const CompactDexFile::Header*>(data);
// TODO: Remove. This is a hack. See comment of the Data method.
@@ -222,13 +268,18 @@
}
void DexFile::InitializeSectionsFromMapList() {
+ // NB: This function must survive random data to pass fuzzing and testing.
static_assert(sizeof(MapList) <= sizeof(Header));
DCHECK_GE(DataSize(), sizeof(MapList));
if (header_->map_off_ == 0 || header_->map_off_ > DataSize() - sizeof(MapList)) {
// Bad offset. The dex file verifier runs after this method and will reject the file.
return;
}
- const MapList* map_list = reinterpret_cast<const MapList*>(DataBegin() + header_->map_off_);
+ const uint8_t* map_list_raw = DataBegin() + header_->map_off_;
+ if (map_list_raw < Begin()) {
+ return;
+ }
+ const MapList* map_list = reinterpret_cast<const MapList*>(map_list_raw);
const size_t count = map_list->size_;
size_t map_limit =
@@ -244,10 +295,10 @@
for (size_t i = 0; i < count; ++i) {
const MapItem& map_item = map_list->list_[i];
if (map_item.type_ == kDexTypeMethodHandleItem) {
- method_handles_ = reinterpret_cast<const MethodHandleItem*>(Begin() + map_item.offset_);
+ method_handles_ = GetSection<MethodHandleItem>(&map_item.offset_, container_.get());
num_method_handles_ = map_item.size_;
} else if (map_item.type_ == kDexTypeCallSiteIdItem) {
- call_site_ids_ = reinterpret_cast<const CallSiteIdItem*>(Begin() + map_item.offset_);
+ call_site_ids_ = GetSection<CallSiteIdItem>(&map_item.offset_, container_.get());
num_call_site_ids_ = map_item.size_;
} else if (map_item.type_ == kDexTypeHiddenapiClassData) {
hiddenapi_class_data_ =
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index 5068480..5f4f984 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -116,6 +116,8 @@
static constexpr size_t kDexMagicSize = 4;
static constexpr size_t kDexVersionLen = 4;
+ static constexpr uint32_t kDexContainerVersion = 41;
+
// First Dex format version enforcing class definition ordering rules.
static constexpr uint32_t kClassDefinitionOrderEnforcedVersion = 37;
@@ -162,6 +164,26 @@
// Decode the dex magic version
uint32_t GetVersion() const;
+
+ // Returns true for standard DEX version 41 or newer.
+ bool HasDexContainer() const;
+
+ // Returns offset of this header within the container.
+ // Returns 0 for older dex versions without container.
+ uint32_t HeaderOffset() const;
+
+ // Returns size of the whole container.
+ // Returns file_size_ for older dex versions without container.
+ uint32_t ContainerSize() const;
+
+ // Set the DEX container fields to the given values.
+ // Must be [0, file_size_) for older dex versions.
+ void SetDexContainer(size_t header_offset, size_t container_size);
+ };
+
+ struct HeaderV41 : public Header {
+ uint32_t container_size_ = 0; // total size of all dex files in the container.
+ uint32_t header_offset_ = 0; // offset of this dex's header in the container.
};
// Map item type codes.
@@ -267,6 +289,20 @@
return GetHeader().GetVersion();
}
+ // Returns true if this is DEX V41 or later (i.e. supports container).
+ // Returns true even if the container contains just a single DEX file.
+ bool HasDexContainer() const { return GetHeader().HasDexContainer(); }
+
+ // Returns the whole memory range of the DEX V41 container.
+ // Returns just the range of the DEX file for V40 or older.
+ ArrayRef<const uint8_t> GetDexContainerRange() const {
+ return {Begin() - header_->HeaderOffset(), header_->ContainerSize()};
+ }
+
+ bool IsDexContainerFirstEntry() const { return Begin() == GetDexContainerRange().begin(); }
+
+ bool IsDexContainerLastEntry() const { return End() == GetDexContainerRange().end(); }
+
// Returns true if the byte string points to the magic value.
virtual bool IsMagicValid() const = 0;
@@ -765,12 +801,14 @@
bool DisableWrite() const;
- const uint8_t* Begin() const {
- return begin_;
- }
+ const uint8_t* Begin() const { return begin_; }
+
+ const uint8_t* End() const { return Begin() + Size(); }
size_t Size() const { return header_->file_size_; }
+ size_t SizeIncludingSharedData() const { return GetDexContainerRange().end() - Begin(); }
+
static ArrayRef<const uint8_t> GetDataRange(const uint8_t* data, DexFileContainer* container);
const uint8_t* DataBegin() const { return data_.data(); }
@@ -888,6 +926,7 @@
// Data memory range: Most dex offsets are relative to this memory range.
// Standard dex: same as (begin_, size_).
+ // Dex container: all dex files (starting from the first header).
// Compact: shared data which is located after all non-shared data.
//
// This is different to the "data section" in the standard dex header.
diff --git a/libdexfile/dex/dex_file_loader.cc b/libdexfile/dex/dex_file_loader.cc
index 0266c41..2fdb16e 100644
--- a/libdexfile/dex/dex_file_loader.cc
+++ b/libdexfile/dex/dex_file_loader.cc
@@ -89,7 +89,11 @@
bool IsReadOnly() const override { return GetPermissions() == PROT_READ; }
bool EnableWrite() override {
- CHECK(IsReadOnly());
+ if (!IsReadOnly()) {
+ // We can already write to the container.
+ // This method may be called multiple times by tests if DexFiles share container.
+ return true;
+ }
if (!mem_map_.IsValid()) {
return false;
} else {
@@ -150,10 +154,10 @@
}
std::string DexFileLoader::GetMultiDexLocation(size_t index, const char* dex_location) {
+ DCHECK(!IsMultiDexLocation(dex_location));
if (index == 0) {
return dex_location;
}
- DCHECK(!IsMultiDexLocation(dex_location));
return StringPrintf("%s%cclasses%zu.dex", dex_location, kMultiDexSeparator, index + 1);
}
@@ -367,14 +371,15 @@
DCHECK(!error_msg->empty());
return false;
}
+ size_t multidex_count = 0;
for (size_t i = 0;; ++i) {
std::string name = GetMultiDexClassesDexName(i);
- std::string multidex_location = GetMultiDexLocation(i, location_.c_str());
bool ok = OpenFromZipEntry(*zip_archive,
name.c_str(),
- multidex_location,
+ location_,
verify,
verify_checksum,
+ &multidex_count,
error_code,
error_msg,
dex_files);
@@ -398,23 +403,32 @@
return false;
}
DCHECK(root_container_ != nullptr);
- std::unique_ptr<const DexFile> dex_file =
- OpenCommon(root_container_,
- root_container_->Begin(),
- root_container_->Size(),
- location_,
- /*location_checksum*/ {}, // Use default checksum from dex header.
- /*oat_dex_file=*/nullptr,
- verify,
- verify_checksum,
- error_msg,
- nullptr);
- if (dex_file.get() != nullptr) {
+ size_t header_offset = 0;
+ for (size_t i = 0;; i++) {
+ std::string multidex_location = GetMultiDexLocation(i, location_.c_str());
+ std::unique_ptr<const DexFile> dex_file =
+ OpenCommon(root_container_,
+ root_container_->Begin() + header_offset,
+ root_container_->Size() - header_offset,
+ multidex_location,
+ /*location_checksum*/ {}, // Use default checksum from dex header.
+ /*oat_dex_file=*/nullptr,
+ verify,
+ verify_checksum,
+ error_msg,
+ error_code);
+ if (dex_file == nullptr) {
+ return false;
+ }
dex_files->push_back(std::move(dex_file));
- return true;
- } else {
- return false;
+ size_t file_size = dex_files->back()->GetHeader().file_size_;
+ CHECK_LE(file_size, root_container_->Size() - header_offset);
+ header_offset += file_size;
+ if (dex_files->back()->IsDexContainerLastEntry()) {
+ break;
+ }
}
+ return true;
}
*error_msg = StringPrintf("Expected valid zip or dex file");
return false;
@@ -481,6 +495,7 @@
const std::string& location,
bool verify,
bool verify_checksum,
+ size_t* multidex_count,
DexFileLoaderErrorCode* error_code,
std::string* error_msg,
std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
@@ -538,21 +553,37 @@
return false;
}
- std::unique_ptr<const DexFile> dex_file = OpenCommon(container,
- container->Begin(),
- container->Size(),
- location,
- zip_entry->GetCrc32(),
- /*oat_dex_file=*/nullptr,
- verify,
- verify_checksum,
- error_msg,
- error_code);
- if (dex_file == nullptr) {
- return false;
+ size_t header_offset = 0;
+ for (size_t i = 0;; i++) {
+ std::string multidex_location = GetMultiDexLocation(*multidex_count, location.c_str());
+ ++(*multidex_count);
+ uint32_t multidex_checksum = zip_entry->GetCrc32() + i;
+ std::unique_ptr<const DexFile> dex_file = OpenCommon(container,
+ container->Begin() + header_offset,
+ container->Size() - header_offset,
+ multidex_location,
+ multidex_checksum,
+ /*oat_dex_file=*/nullptr,
+ verify,
+ verify_checksum,
+ error_msg,
+ error_code);
+ if (dex_file == nullptr) {
+ return false;
+ }
+ if (dex_file->IsCompactDexFile()) {
+ *error_msg = StringPrintf("Can not open compact dex file from zip '%s'", location.c_str());
+ return false;
+ }
+ CHECK(dex_file->IsReadOnly()) << multidex_location;
+ dex_files->push_back(std::move(dex_file));
+ size_t file_size = dex_files->back()->GetHeader().file_size_;
+ CHECK_LE(file_size, container->Size() - header_offset);
+ header_offset += file_size;
+ if (dex_files->back()->IsDexContainerLastEntry()) {
+ break;
+ }
}
- CHECK(dex_file->IsReadOnly()) << location;
- dex_files->push_back(std::move(dex_file));
return true;
}
diff --git a/libdexfile/dex/dex_file_loader.h b/libdexfile/dex/dex_file_loader.h
index 1cc7d4a..f912053 100644
--- a/libdexfile/dex/dex_file_loader.h
+++ b/libdexfile/dex/dex_file_loader.h
@@ -93,13 +93,19 @@
CHECK_LT(*i, dex_files.size()) << "No dex files";
std::optional<uint32_t> checksum;
for (; *i < dex_files.size(); ++(*i)) {
- const char* location = dex_files[*i]->GetLocation().c_str();
+ const auto* dex_file = &*dex_files[*i];
+ bool is_primary_dex = !IsMultiDexLocation(dex_file->GetLocation().c_str());
if (!checksum.has_value()) { // First dex file.
- CHECK(!IsMultiDexLocation(location)) << location; // Expect primary dex.
- } else if (!IsMultiDexLocation(location)) { // Later dex file.
+ CHECK(is_primary_dex) << dex_file->GetLocation(); // Expect primary dex.
+ } else if (is_primary_dex) { // Later dex file.
break; // Found another primary dex file, terminate iteration.
}
- checksum = checksum.value_or(kEmptyMultiDexChecksum) ^ dex_files[*i]->GetLocationChecksum();
+ if (!is_primary_dex && dex_file->GetDexVersion() >= DexFile::kDexContainerVersion) {
+ if (dex_file->GetLocationChecksum() == dex_files[*i - 1]->GetLocationChecksum() + 1) {
+ continue;
+ }
+ }
+ checksum = checksum.value_or(kEmptyMultiDexChecksum) ^ dex_file->GetLocationChecksum();
}
CHECK(checksum.has_value());
return checksum.value();
@@ -193,16 +199,18 @@
bool verify,
bool verify_checksum,
std::string* error_msg) {
- return Open(
+ std::unique_ptr<const DexFile> dex_file = Open(
/*header_offset=*/0, location_checksum, oat_dex_file, verify, verify_checksum, error_msg);
+ // This API returns only singe DEX file, so check there is just single dex in the container.
+ CHECK(dex_file == nullptr || dex_file->IsDexContainerLastEntry()) << location_;
+ return dex_file;
}
std::unique_ptr<const DexFile> Open(uint32_t location_checksum,
bool verify,
bool verify_checksum,
std::string* error_msg) {
- return Open(/*header_offset=*/0,
- location_checksum,
+ return Open(location_checksum,
/*oat_dex_file=*/nullptr,
verify,
verify_checksum,
@@ -304,9 +312,10 @@
const std::string& location,
bool verify,
bool verify_checksum,
- DexFileLoaderErrorCode* error_code,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
+ /*inout*/ size_t* multidex_count,
+ /*out*/ DexFileLoaderErrorCode* error_code,
+ /*out*/ std::string* error_msg,
+ /*out*/ std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
// The DexFileLoader can be backed either by file or by memory (i.e. DexFileContainer).
// We can not just mmap the file since APKs might be unreasonably large for 32-bit system.
diff --git a/libdexfile/dex/dex_file_loader_test.cc b/libdexfile/dex/dex_file_loader_test.cc
index 279411a..ecfd23a 100644
--- a/libdexfile/dex/dex_file_loader_test.cc
+++ b/libdexfile/dex/dex_file_loader_test.cc
@@ -111,17 +111,37 @@
"uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC"
"AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA==";
+// Taken from 001-Main.
static const char kRawDex41[] =
- "ZGV4CjA0MQC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI"
- "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB"
- "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA"
- "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA"
- "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB"
- "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW"
- "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA"
- "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA"
- "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC"
- "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA==";
+ "ZGV4CjA0MQBBaEGw/8clTiOn3IafJ++m20gViy5Peh7UAgAAeAAAAHhWNBIAAAAAAAAAAEACAAAK"
+ "AAAAeAAAAAQAAACgAAAAAgAAALAAAAAAAAAAAAAAAAMAAADIAAAAAQAAAOAAAAAAAAAAAAAAANQC"
+ "AAAAAAAAOgEAAEIBAABKAQAAXgEAAGkBAABsAQAAcAEAAIUBAACLAQAAkQEAAAEAAAACAAAABAAA"
+ "AAYAAAAEAAAAAgAAAAAAAAAFAAAAAgAAADQBAAAAAAAAAAAAAAAAAQAIAAAAAQAAAAAAAAAAAAAA"
+ "AQAAAAEAAAAAAAAAAwAAAAAAAAAxAgAAAAAAAAEAAQABAAAAKgEAAAQAAABwEAIAAAAOAAEAAQAA"
+ "AAAALgEAAAEAAAAOABEADgATAQgOAAABAAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcv"
+ "T2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJWTAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEYXJncwAEbWFp"
+ "bgCdAX5+RDh7ImJhY2tlbmQiOiJkZXgiLCJjb21waWxhdGlvbi1tb2RlIjoiZGVidWciLCJoYXMt"
+ "Y2hlY2tzdW1zIjpmYWxzZSwibWluLWFwaSI6MjYsInNoYS0xIjoiNTRjYmIzMTZlNGI3OWFhMDM1"
+ "ZDUwMTM4ZTI3NjY4OGJiOTM5ZGIwNCIsInZlcnNpb24iOiI4LjMuMTQtZGV2In0AAAACAACBgASA"
+ "AgEJmAIADAAAAAAAAAABAAAAAAAAAAEAAAAKAAAAeAAAAAIAAAAEAAAAoAAAAAMAAAACAAAAsAAA"
+ "AAUAAAADAAAAyAAAAAYAAAABAAAA4AAAAAEgAAACAAAAAAEAAAMgAAACAAAAKgEAAAEQAAABAAAA"
+ "NAEAAAIgAAAKAAAAOgEAAAAgAAABAAAAMQIAAAAQAAABAAAAQAIAAA==";
+
+// Taken from 001-Main and modified.
+static const char kRawDex42[] =
+ "ZGV4CjA0MgBBaEGw/8clTiOn3IafJ++m20gViy5Peh7UAgAAeAAAAHhWNBIAAAAAAAAAAEACAAAK"
+ "AAAAeAAAAAQAAACgAAAAAgAAALAAAAAAAAAAAAAAAAMAAADIAAAAAQAAAOAAAAAAAAAAAAAAANQC"
+ "AAAAAAAAOgEAAEIBAABKAQAAXgEAAGkBAABsAQAAcAEAAIUBAACLAQAAkQEAAAEAAAACAAAABAAA"
+ "AAYAAAAEAAAAAgAAAAAAAAAFAAAAAgAAADQBAAAAAAAAAAAAAAAAAQAIAAAAAQAAAAAAAAAAAAAA"
+ "AQAAAAEAAAAAAAAAAwAAAAAAAAAxAgAAAAAAAAEAAQABAAAAKgEAAAQAAABwEAIAAAAOAAEAAQAA"
+ "AAAALgEAAAEAAAAOABEADgATAQgOAAABAAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcv"
+ "T2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJWTAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEYXJncwAEbWFp"
+ "bgCdAX5+RDh7ImJhY2tlbmQiOiJkZXgiLCJjb21waWxhdGlvbi1tb2RlIjoiZGVidWciLCJoYXMt"
+ "Y2hlY2tzdW1zIjpmYWxzZSwibWluLWFwaSI6MjYsInNoYS0xIjoiNTRjYmIzMTZlNGI3OWFhMDM1"
+ "ZDUwMTM4ZTI3NjY4OGJiOTM5ZGIwNCIsInZlcnNpb24iOiI4LjMuMTQtZGV2In0AAAACAACBgASA"
+ "AgEJmAIADAAAAAAAAAABAAAAAAAAAAEAAAAKAAAAeAAAAAIAAAAEAAAAoAAAAAMAAAACAAAAsAAA"
+ "AAUAAAADAAAAyAAAAAYAAAABAAAA4AAAAAEgAAACAAAAAAEAAAMgAAACAAAAKgEAAAEQAAABAAAA"
+ "NAEAAAIgAAAKAAAAOgEAAAAgAAABAAAAMQIAAAAQAAABAAAAQAIAAA==";
static const char kRawDexZeroLength[] =
"UEsDBAoAAAAAAOhxAkkAAAAAAAAAAAAAAAALABwAY2xhc3Nlcy5kZXhVVAkAA2QNoVdnDaFXdXgL"
@@ -324,7 +344,8 @@
static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
const char* location,
- std::vector<uint8_t>* dex_bytes) {
+ std::vector<uint8_t>* dex_bytes,
+ size_t expected_dex_files = 1) {
// read dex files.
DexFileLoaderErrorCode error_code;
std::string error_msg;
@@ -332,7 +353,7 @@
bool success = OpenDexFilesBase64(base64, location, dex_bytes, &dex_files, &error_code,
&error_msg);
CHECK(success) << error_msg;
- EXPECT_EQ(1U, dex_files.size());
+ EXPECT_EQ(expected_dex_files, dex_files.size());
return std::move(dex_files[0]);
}
@@ -433,9 +454,18 @@
EXPECT_EQ(40u, header.GetVersion());
}
-TEST_F(DexFileLoaderTest, Version41Rejected) {
+TEST_F(DexFileLoaderTest, Version41Accepted) {
std::vector<uint8_t> dex_bytes;
- DecodeDexFile(kRawDex41, &dex_bytes);
+ std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex41, kLocationString, &dex_bytes, 1));
+ ASSERT_TRUE(raw.get() != nullptr);
+
+ const DexFile::Header& header = raw->GetHeader();
+ EXPECT_EQ(41u, header.GetVersion());
+}
+
+TEST_F(DexFileLoaderTest, Version42Rejected) {
+ std::vector<uint8_t> dex_bytes;
+ DecodeDexFile(kRawDex42, &dex_bytes);
static constexpr bool kVerifyChecksum = true;
DexFileLoaderErrorCode error_code;
diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc
index b94d7ce..1a6ff81 100644
--- a/libdexfile/dex/dex_file_verifier.cc
+++ b/libdexfile/dex/dex_file_verifier.cc
@@ -127,8 +127,8 @@
public:
DexFileVerifier(const DexFile* dex_file, const char* location, bool verify_checksum)
: dex_file_(dex_file),
- offset_base_address_(dex_file->Begin()),
- size_(0), // Initialized after we verify the header.
+ offset_base_address_(dex_file->DataBegin()),
+ size_(dex_file->DataSize()),
location_(location),
verify_checksum_(verify_checksum),
header_(&dex_file->GetHeader()),
@@ -150,6 +150,7 @@
private:
template <class T = uint8_t>
ALWAYS_INLINE const T* OffsetToPtr(size_t offset) {
+ DCHECK_GE(offset, static_cast<size_t>(dex_file_->Begin() - offset_base_address_));
DCHECK_LE(offset, size_);
return reinterpret_cast<const T*>(offset_base_address_ + offset);
}
@@ -385,11 +386,12 @@
const DexFile* const dex_file_;
const uint8_t* const offset_base_address_;
- size_t size_;
+ const size_t size_;
ArrayRef<const uint8_t> data_; // The "data" section of the dex file.
const char* const location_;
const bool verify_checksum_;
const DexFile::Header* const header_;
+ uint32_t dex_version_ = 0;
struct OffsetTypeMapEmptyFn {
// Make a hash map slot empty by making the offset 0. Offset 0 is a valid dex file offset that
@@ -570,6 +572,11 @@
return false;
}
}
+ size_t hdr_offset = PtrToOffset(header_);
+ if (offset < hdr_offset) {
+ ErrorStringPrintf("Offset(%d) should be after header(%zu) for %s.", offset, hdr_offset, label);
+ return false;
+ }
if (size_ <= offset) {
ErrorStringPrintf("Offset(%d) should be within file size(%zu) for %s.", offset, size_, label);
return false;
@@ -603,10 +610,11 @@
ErrorStringPrintf("Unknown dex version");
return false;
}
+ dex_version_ = header_->GetVersion();
// Check file size from the header.
size_t file_size = header_->file_size_;
- size_t header_size = sizeof(DexFile::Header);
+ size_t header_size = (dex_version_ >= 41) ? sizeof(DexFile::HeaderV41) : sizeof(DexFile::Header);
if (file_size < header_size) {
ErrorStringPrintf("Bad file size (%zu, expected at least %zu)", file_size, header_size);
return false;
@@ -616,7 +624,6 @@
return false;
}
CHECK_GE(size, header_size); // Implied by the two checks above.
- size_ = file_size;
// Check header size.
if (header_->header_size_ != header_size) {
@@ -642,6 +649,22 @@
}
}
+ if (dex_version_ >= 41) {
+ auto headerV41 = reinterpret_cast<const DexFile::HeaderV41*>(header_);
+ if (headerV41->container_size_ <= headerV41->header_offset_) {
+ ErrorStringPrintf("Dex container is too small: size=%ud header_offset=%ud",
+ headerV41->container_size_,
+ headerV41->header_offset_);
+ return false;
+ }
+ uint32_t remainder = headerV41->container_size_ - headerV41->header_offset_;
+ if (headerV41->file_size_ > remainder) {
+ ErrorStringPrintf(
+ "Header file_size(%ud) is past multi-dex size(%ud)", headerV41->file_size_, remainder);
+ return false;
+ }
+ }
+
// Check that all offsets are inside the file.
bool ok =
CheckValidOffsetAndSize(header_->link_off_,
@@ -686,7 +709,9 @@
"data");
if (ok) {
- data_ = ArrayRef<const uint8_t>(OffsetToPtr(header_->data_off_), header_->data_size_);
+ data_ = (dex_version_ >= 41)
+ ? ArrayRef<const uint8_t>(dex_file_->Begin(), EndOfFile() - dex_file_->Begin())
+ : ArrayRef<const uint8_t>(OffsetToPtr(header_->data_off_), header_->data_size_);
}
return ok;
}
@@ -722,9 +747,8 @@
last_type);
return false;
}
- if (UNLIKELY(item->offset_ >= header_->file_size_)) {
- ErrorStringPrintf("Map item after end of file: %x, size %x",
- item->offset_, header_->file_size_);
+ if (UNLIKELY(item->offset_ >= size_)) {
+ ErrorStringPrintf("Map item after end of file: %x, size %zx", item->offset_, size_);
return false;
}
@@ -961,6 +985,10 @@
if (!CheckListSize(OffsetToPtr(offset), aligned_offset - offset, sizeof(uint8_t), "section")) {
return false;
}
+ if (dex_version_ >= 41) {
+ ptr_ += aligned_offset - offset;
+ return true;
+ }
while (offset < aligned_offset) {
if (UNLIKELY(*ptr_ != '\0')) {
ErrorStringPrintf("Non-zero padding %x before section of type %zu at offset 0x%zx",
@@ -2290,17 +2318,19 @@
// Check each item based on its type.
switch (type) {
- case DexFile::kDexTypeHeaderItem:
+ case DexFile::kDexTypeHeaderItem: {
if (UNLIKELY(section_count != 1)) {
ErrorStringPrintf("Multiple header items");
return false;
}
- if (UNLIKELY(section_offset != 0)) {
- ErrorStringPrintf("Header at %x, not at start of file", section_offset);
+ uint32_t expected = dex_version_ >= 41 ? PtrToOffset(dex_file_->Begin()) : 0;
+ if (UNLIKELY(section_offset != expected)) {
+ ErrorStringPrintf("Header at %x, expected %x", section_offset, expected);
return false;
}
ptr_ = OffsetToPtr(header_->header_size_);
break;
+ }
#define CHECK_INTRA_ID_SECTION_CASE(type) \
case type: \
diff --git a/libdexfile/dex/standard_dex_file.cc b/libdexfile/dex/standard_dex_file.cc
index 912cff6..4dc96d4 100644
--- a/libdexfile/dex/standard_dex_file.cc
+++ b/libdexfile/dex/standard_dex_file.cc
@@ -24,18 +24,20 @@
namespace art {
const uint8_t StandardDexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' };
-const uint8_t StandardDexFile::kDexMagicVersions[StandardDexFile::kNumDexVersions]
- [StandardDexFile::kDexVersionLen] = {
- {'0', '3', '5', '\0'},
- // Dex version 036 skipped because of an old dalvik bug on some versions of android where dex
- // files with that version number would erroneously be accepted and run.
- {'0', '3', '7', '\0'},
- // Dex version 038: Android "O" and beyond.
- {'0', '3', '8', '\0'},
- // Dex version 039: Android "P" and beyond.
- {'0', '3', '9', '\0'},
- // Dex version 040: beyond Android "10" (previously known as Android "Q").
- {'0', '4', '0', '\0'},
+const uint8_t StandardDexFile::kDexMagicVersions
+ [StandardDexFile::kNumDexVersions][StandardDexFile::kDexVersionLen] = {
+ {'0', '3', '5', '\0'},
+ // Dex version 036 skipped because of an old dalvik bug on some versions of android where
+ // dex files with that version number would erroneously be accepted and run.
+ {'0', '3', '7', '\0'},
+ // Dex version 038: Android "O" and beyond.
+ {'0', '3', '8', '\0'},
+ // Dex version 039: Android "P" and beyond.
+ {'0', '3', '9', '\0'},
+ // Dex version 040: Android "Q" and beyond (aka Android 10).
+ {'0', '4', '0', '\0'},
+ // Dex version 041: Android "V" and beyond (aka Android 15).
+ {'0', '4', '1', '\0'},
};
void StandardDexFile::WriteMagic(uint8_t* magic) {
diff --git a/libdexfile/dex/standard_dex_file.h b/libdexfile/dex/standard_dex_file.h
index 05f7d41..0b12c18 100644
--- a/libdexfile/dex/standard_dex_file.h
+++ b/libdexfile/dex/standard_dex_file.h
@@ -91,7 +91,7 @@
static void WriteVersionBeforeDefaultMethods(uint8_t* magic);
static const uint8_t kDexMagic[kDexMagicSize];
- static constexpr size_t kNumDexVersions = 5;
+ static constexpr size_t kNumDexVersions = 6;
static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen];
// Returns true if the byte string points to the magic value.
diff --git a/libdexfile/external/dex_file_ext.cc b/libdexfile/external/dex_file_ext.cc
index e71233c..774755f 100644
--- a/libdexfile/external/dex_file_ext.cc
+++ b/libdexfile/external/dex_file_ext.cc
@@ -155,6 +155,13 @@
}
const art::DexFile::Header* header = reinterpret_cast<const art::DexFile::Header*>(address);
+ if (size < header->header_size_) {
+ if (new_size != nullptr) {
+ *new_size = header->header_size_;
+ }
+ return ADEXFILE_ERROR_NOT_ENOUGH_DATA;
+ }
+
uint32_t dex_size = header->file_size_; // Size of "one dex file" excluding any shared data.
uint32_t full_size = dex_size; // Includes referenced shared data past the end of dex.
if (art::CompactDexFile::IsMagicValid(header->magic_)) {
@@ -169,7 +176,9 @@
if (computed_file_size > full_size) {
full_size = computed_file_size;
}
- } else if (!art::StandardDexFile::IsMagicValid(header->magic_)) {
+ } else if (art::StandardDexFile::IsMagicValid(header->magic_)) {
+ full_size = header->ContainerSize() - header->HeaderOffset();
+ } else {
return ADEXFILE_ERROR_INVALID_HEADER;
}
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 512fe33..2f23cab 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -647,6 +647,11 @@
CHECK(oat_dex_file != nullptr);
CHECK(vdex_dex_file != nullptr);
+ if (!vdex_dex_file->IsDexContainerFirstEntry()) {
+ // All the data was already exported together with the primary dex file.
+ continue;
+ }
+
// If a CompactDex file is detected within a Vdex container, DexLayout is used to convert
// back to a StandardDex file. Since the converted DexFile will most likely not reproduce
// the original input Dex file, the `update_checksum_` option is used to recompute the
@@ -993,6 +998,9 @@
return false;
}
}
+ // Extend the data range to export all the dex files in the container.
+ CHECK(dex_file->IsDexContainerFirstEntry()) << dex_file_location;
+ fsize = dex_file->GetHeader().ContainerSize();
}
// Verify output directory exists
diff --git a/openjdkjvmti/ti_class_definition.cc b/openjdkjvmti/ti_class_definition.cc
index a9e7b31..72fe7c7 100644
--- a/openjdkjvmti/ti_class_definition.cc
+++ b/openjdkjvmti/ti_class_definition.cc
@@ -106,7 +106,8 @@
dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
const art::DexFile& cur_dex = m_klass->GetDexFile();
- current_dex_file_ = art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.Size());
+ current_dex_file_ =
+ art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.SizeIncludingSharedData());
return OK;
}
@@ -132,7 +133,8 @@
}
}
const art::DexFile& cur_dex = m_klass->GetDexFile();
- current_dex_file_ = art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.Size());
+ current_dex_file_ =
+ art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.SizeIncludingSharedData());
return OK;
}
}
@@ -189,8 +191,8 @@
}
}
// Keep the dex_data alive.
- dex_data_memory_.resize(original_dex_file->Size());
- memcpy(dex_data_memory_.data(), original_dex_file->Begin(), original_dex_file->Size());
+ dex_data_memory_.resize(original_dex_file->SizeIncludingSharedData());
+ memcpy(dex_data_memory_.data(), original_dex_file->Begin(), dex_data_memory_.size());
dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
// In case dex_data gets re-used for redefinition, keep the dex file live
@@ -200,7 +202,8 @@
current_dex_file_ = art::ArrayRef<const unsigned char>(current_dex_memory_);
} else {
// Dex file will always stay live, use it directly.
- dex_data_ = art::ArrayRef<const unsigned char>(dex_file.Begin(), dex_file.Size());
+ dex_data_ =
+ art::ArrayRef<const unsigned char>(dex_file.Begin(), dex_file.SizeIncludingSharedData());
current_dex_file_ = dex_data_;
}
return OK;
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 43e1bf8..54a007f 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -131,8 +131,14 @@
ASSERT_EQ(multi1[0]->GetHeader().checksum_, multi2[0]->GetHeader().checksum_);
ASSERT_NE(multi1[1]->GetHeader().checksum_, multi2[1]->GetHeader().checksum_);
- ASSERT_EQ(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum());
- ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum());
+ if (multi1[0]->HasDexContainer()) {
+ // Checksum is the CRC of the whole container, so both of them should differ.
+ ASSERT_NE(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum());
+ ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum());
+ } else {
+ ASSERT_EQ(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum());
+ ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum());
+ }
}
void SetUpRuntimeOptions(RuntimeOptions* options) override {
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 4cee0d4..97164f7 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -17,6 +17,7 @@
#include "oat_file.h"
#include <dlfcn.h>
+
#ifndef __APPLE__
#include <link.h> // for dl_iterate_phdr.
#endif
@@ -2211,7 +2212,7 @@
// Initialize TypeLookupTable.
if (lookup_table_data_ != nullptr) {
// Peek the number of classes from the DexFile.
- const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer_);
+ auto* dex_header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer_);
const uint32_t num_class_defs = dex_header->class_defs_size_;
if (lookup_table_data_ + TypeLookupTable::RawDataLength(num_class_defs) >
GetOatFile()->DexEnd()) {
@@ -2219,6 +2220,9 @@
} else {
const uint8_t* dex_data = dex_file_pointer_;
// TODO: Clean this up to create the type lookup table after the dex file has been created?
+ if (StandardDexFile::IsMagicValid(dex_header->magic_)) {
+ dex_data -= dex_header->HeaderOffset();
+ }
if (CompactDexFile::IsMagicValid(dex_header->magic_)) {
dex_data += dex_header->data_off_;
}
@@ -2564,6 +2568,10 @@
CHECK(Runtime::Current()->IsAotCompiler());
}
+uint32_t OatDexFile::GetDexVersion() const {
+ return atoi(reinterpret_cast<const char*>(&dex_file_magic_[4]));
+}
+
bool OatFile::IsBackedByVdexOnly() const {
return oat_dex_files_storage_.size() >= 1 && oat_dex_files_storage_[0]->IsBackedByVdexOnly();
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 806e616..2e1bd2e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -528,6 +528,8 @@
DexFile::Magic GetMagic() const { return dex_file_magic_; }
+ uint32_t GetDexVersion() const;
+
// Returns checksum of original DexFile that was the source of this OatDexFile;
uint32_t GetDexFileLocationChecksum() const {
return dex_file_location_checksum_;
diff --git a/test/180-native-default-method/build.py b/test/180-native-default-method/build.py
index 62aac82..d88008f 100644
--- a/test/180-native-default-method/build.py
+++ b/test/180-native-default-method/build.py
@@ -14,7 +14,7 @@
# limitations under the License.
def build(ctx):
- ctx.default_build()
+ ctx.default_build(d8_dex_container=False)
if ctx.jvm:
return
# Change the generated dex file to have a v35 magic number if it is version 38
diff --git a/test/MultiDex/Second.java b/test/MultiDex/Second.java
index 5067bcc..ec8849a 100644
--- a/test/MultiDex/Second.java
+++ b/test/MultiDex/Second.java
@@ -16,12 +16,6 @@
class Second {
public String getSecond() {
- return "I Second That.";
- }
-
- // This method makes sure the second dex file has quickening
- // instructions.
- public String callSecond() {
- return getSecond();
+ return "Original";
}
}
diff --git a/test/MultiDexModifiedSecondary/Second.java b/test/MultiDexModifiedSecondary/Second.java
index 3555a7f..08ad625 100644
--- a/test/MultiDexModifiedSecondary/Second.java
+++ b/test/MultiDexModifiedSecondary/Second.java
@@ -15,11 +15,7 @@
*/
class Second {
- public String getThird() {
- return "I Third That.";
- }
-
public String getSecond() {
- return "I Second That.";
+ return "Modified"; // Must have the same length as original so that dex size is same.
}
}
diff --git a/test/run_test_build.py b/test/run_test_build.py
index e1f6e73..7dfec8a 100755
--- a/test/run_test_build.py
+++ b/test/run_test_build.py
@@ -216,6 +216,7 @@
javac_args=[],
javac_classpath: List[Path]=[],
d8_flags=[],
+ d8_dex_container=True,
smali_args=[],
use_smali=True,
use_jasmin=True,
@@ -305,7 +306,10 @@
# packaged in a jar file.
def make_dex(src_dir: Path):
dst_jar = Path(src_dir.name + ".jar")
- args = d8_flags + ["--min-api", str(api_level), "--output", dst_jar]
+ args = []
+ if d8_dex_container:
+ args += ["-JDcom.android.tools.r8.dexContainerExperiment"]
+ args += d8_flags + ["--min-api", str(api_level), "--output", dst_jar]
args += ["--lib", self.bootclasspath] if use_desugar else ["--no-desugaring"]
args += sorted(src_dir.glob("**/*.class"))
self.d8(args)
@@ -328,7 +332,11 @@
# It is useful to normalize non-deterministic smali output.
tmp_dir = self.test_dir / "dexmerge"
tmp_dir.mkdir()
- self.d8(["--min-api", str(api_level), "--output", tmp_dir] + srcs)
+ flags = []
+ if d8_dex_container:
+ flags += ["-JDcom.android.tools.r8.dexContainerExperiment"]
+ flags += ["--min-api", str(api_level), "--output", tmp_dir]
+ self.d8(flags + srcs)
assert not (tmp_dir / "classes2.dex").exists()
for src_file in srcs:
src_file.unlink()
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index 4c42e13..a1fea05 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -299,14 +299,14 @@
for (const std::string& filename : dex_paths) {
DexFileLoader dex_file_loader(filename);
+ DexFileLoaderErrorCode error_code;
bool success = dex_file_loader.Open(/* verify= */ true,
/* verify_checksum= */ true,
+ /*allow_no_dex_files=*/ ignore_empty,
+ &error_code,
&error_msg,
&dex_files_);
- // If requested ignore a jar with no classes.dex files.
- if (!success && ignore_empty && error_msg != "Entry not found") {
- CHECK(success) << "Open failed for '" << filename << "' " << error_msg;
- }
+ CHECK(success) << "Open failed for '" << filename << "' " << error_msg;
}
}
@@ -660,16 +660,15 @@
// Writes the edited dex file into a file.
void WriteTo(const std::string& path) {
+ CHECK_GT(inputs_.size(), 0u);
std::vector<uint8_t> output;
// Copy the old dex files into the backing data vector.
- size_t truncated_size = 0;
std::vector<size_t> header_offset;
for (size_t i = 0; i < inputs_.size(); i++) {
const DexFile* dex = inputs_[i].first;
header_offset.push_back(output.size());
- std::copy(
- dex->Begin(), dex->Begin() + dex->GetHeader().file_size_, std::back_inserter(output));
+ std::copy(dex->Begin(), dex->End(), std::back_inserter(output));
// Clear the old map list (make it into padding).
const dex::MapList* map = dex->GetMapList();
@@ -678,9 +677,7 @@
CHECK_LE(map_off, output.size()) << "Map list past the end of file";
CHECK_EQ(map_size, output.size() - map_off) << "Map list expected at the end of file";
std::fill_n(output.data() + map_off, map_size, 0);
- truncated_size = output.size() - map_size;
}
- output.resize(truncated_size); // Truncate last map list.
// Append the hidden api data into the backing data vector.
std::vector<size_t> hiddenapi_offset;
@@ -691,7 +688,8 @@
std::copy(hiddenapi_data.begin(), hiddenapi_data.end(), std::back_inserter(output));
}
- // Update the dex headers and map lists.
+ // Append modified map lists.
+ std::vector<uint32_t> map_list_offset;
for (size_t i = 0; i < inputs_.size(); i++) {
output.resize(RoundUp(output.size(), kMapListAlignment)); // Align.
@@ -716,16 +714,18 @@
uint32_t payload_offset = hiddenapi_offset[i];
items.push_back(dex::MapItem{DexFile::kDexTypeHiddenapiClassData, 0, 1u, payload_offset});
}
- uint32_t map_offset = output.size();
- items.push_back(dex::MapItem{DexFile::kDexTypeMapList, 0, 1u, map_offset});
+ map_list_offset.push_back(output.size());
+ items.push_back(dex::MapItem{DexFile::kDexTypeMapList, 0, 1u, map_list_offset.back()});
uint32_t item_count = items.size();
Append(&output, &item_count, 1);
Append(&output, items.data(), items.size());
+ }
- // Update header.
+ // Update headers.
+ for (size_t i = 0; i < inputs_.size(); i++) {
uint8_t* begin = output.data() + header_offset[i];
auto* header = reinterpret_cast<DexFile::Header*>(begin);
- header->map_off_ = map_offset;
+ header->map_off_ = map_list_offset[i];
if (i + 1 < inputs_.size()) {
CHECK_EQ(header->file_size_, header_offset[i + 1] - header_offset[i]);
} else {
@@ -733,6 +733,7 @@
header->data_size_ = output.size() - header->data_off_;
header->file_size_ = output.size() - header_offset[i];
}
+ header->SetDexContainer(header_offset[i], output.size());
size_t sha1_start = offsetof(DexFile::Header, file_size_);
SHA1(begin + sha1_start, header->file_size_ - sha1_start, header->signature_.data());
header->checksum_ = DexFile::CalculateChecksum(begin, header->file_size_);