Fix VDEX header when no quickening info

When there is no quickening info to be written, the OatWriter logic
wrongly assumes that padding was written to align the file size to
32 bits. The file is therefore shorter than the size calculated from
header data. This patch fixes that.

Additionally, it enforces that OatWriter::WriteQuickeningInfo is
always called, which was not the case with some of our tests. This
requirement was already mentioned in OatWriter comments but not
adhered to.

Test: m test-art-host-gtest
Bug: 70267762
Change-Id: If25600761677efba173d7cac02931e71c6260f9d
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index cedbccf..85145d3 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -293,14 +293,7 @@
       bool image_space_ok = writer->PrepareImageAddressSpace();
       ASSERT_TRUE(image_space_ok);
 
-      for (size_t i = 0, size = vdex_files.size(); i != size; ++i) {
-        std::unique_ptr<BufferedOutputStream> vdex_out =
-            std::make_unique<BufferedOutputStream>(
-                std::make_unique<FileOutputStream>(vdex_files[i].GetFile()));
-        oat_writers[i]->WriteVerifierDeps(vdex_out.get(), nullptr);
-        oat_writers[i]->WriteChecksumsAndVdexHeader(vdex_out.get());
-      }
-
+      DCHECK_EQ(vdex_files.size(), oat_files.size());
       for (size_t i = 0, size = oat_files.size(); i != size; ++i) {
         MultiOatRelativePatcher patcher(driver->GetInstructionSet(),
                                         driver->GetInstructionSetFeatures());
@@ -308,6 +301,14 @@
         ElfWriter* const elf_writer = elf_writers[i].get();
         std::vector<const DexFile*> cur_dex_files(1u, class_path[i]);
         oat_writer->Initialize(driver, writer.get(), cur_dex_files);
+
+        std::unique_ptr<BufferedOutputStream> vdex_out =
+            std::make_unique<BufferedOutputStream>(
+                std::make_unique<FileOutputStream>(vdex_files[i].GetFile()));
+        oat_writer->WriteVerifierDeps(vdex_out.get(), nullptr);
+        oat_writer->WriteQuickeningInfo(vdex_out.get());
+        oat_writer->WriteChecksumsAndVdexHeader(vdex_out.get());
+
         oat_writer->PrepareLayout(&patcher);
         size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset();
         size_t text_size = oat_writer->GetOatSize() - rodata_size;
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 293078a..b5c5d98 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -2742,10 +2742,6 @@
   size_t initial_offset = vdex_size_;
   size_t start_offset = RoundUp(initial_offset, 4u);
 
-  vdex_size_ = start_offset;
-  vdex_quickening_info_offset_ = vdex_size_;
-  size_quickening_info_alignment_ = start_offset - initial_offset;
-
   off_t actual_offset = vdex_out->Seek(start_offset, kSeekSet);
   if (actual_offset != static_cast<off_t>(start_offset)) {
     PLOG(ERROR) << "Failed to seek to quickening info section. Actual: " << actual_offset
@@ -2789,7 +2785,16 @@
     size_quickening_info_ = 0;
   }
 
-  vdex_size_ += size_quickening_info_;
+  if (size_quickening_info_ == 0) {
+    // Nothing was written. Leave `vdex_size_` untouched and unaligned.
+    vdex_quickening_info_offset_ = initial_offset;
+    size_quickening_info_alignment_ = 0;
+  } else {
+    vdex_size_ = start_offset + size_quickening_info_;
+    vdex_quickening_info_offset_ = start_offset;
+    size_quickening_info_alignment_ = start_offset - initial_offset;
+  }
+
   return true;
 }
 
@@ -3864,6 +3869,7 @@
 
   DCHECK_NE(vdex_dex_files_offset_, 0u);
   DCHECK_NE(vdex_verifier_deps_offset_, 0u);
+  DCHECK_NE(vdex_quickening_info_offset_, 0u);
 
   size_t dex_section_size = vdex_verifier_deps_offset_ - vdex_dex_files_offset_;
   size_t verifier_deps_section_size = vdex_quickening_info_offset_ - vdex_verifier_deps_offset_;
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index fec05cc..e9958b1 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -45,6 +45,7 @@
 #include "oat_writer.h"
 #include "scoped_thread_state_change-inl.h"
 #include "utils/test_dex_file_builder.h"
+#include "vdex_file.h"
 
 namespace art {
 namespace linker {
@@ -225,6 +226,9 @@
     if (!oat_writer.WriteVerifierDeps(vdex_out.get(), nullptr)) {
       return false;
     }
+    if (!oat_writer.WriteQuickeningInfo(vdex_out.get())) {
+      return false;
+    }
     if (!oat_writer.WriteChecksumsAndVdexHeader(vdex_out.get())) {
       return false;
     }
@@ -652,6 +656,13 @@
                       &opened_dex_file2->GetHeader(),
                       dex_file2_data->GetHeader().file_size_));
   ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation());
+
+  const VdexFile::Header &vdex_header = opened_oat_file->GetVdexFile()->GetHeader();
+  ASSERT_EQ(vdex_header.GetQuickeningInfoSize(), 0u);
+
+  int64_t actual_vdex_size = vdex_file.GetFile()->GetLength();
+  ASSERT_GE(actual_vdex_size, 0);
+  ASSERT_EQ((uint64_t) actual_vdex_size, vdex_header.GetComputedFileSize());
 }
 
 TEST_F(OatTest, DexFileInputCheckOutput) {