Fix input cdex -> output non-cdex

Previously there was a bug where the shared data section would not
get copied over. Fixed this by copying it over.

Added regression test.

Test: test-art-host-gtest
Bug: 73105322
Bug: 73059081
Bug: 73059886
Bug: 73058502
Bug: 73058759
Bug: 63756964
Change-Id: I0efd4272cfaae604ea955c3548f5a054b243ec68
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index d0b0d49..1ed31d1 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -796,7 +796,7 @@
                          app_image_file_name,
                          /* use_fd */ true,
                          /* num_profile_classes */ 1,
-                         { input_vdex, output_vdex, kDisableCompactDex },
+                         { input_vdex, output_vdex },
                          /* expect_success */ true);
       EXPECT_GT(vdex_file2.GetFile()->GetLength(), 0u);
     }
@@ -926,6 +926,49 @@
     ASSERT_TRUE(success_);
   }
 
+  void RunUnquickenMultiDexCDex() {
+    std::string dex_location = GetScratchDir() + "/UnquickenMultiDex.jar";
+    std::string odex_location = GetOdexDir() + "/UnquickenMultiDex.odex";
+    std::string odex_location2 = GetOdexDir() + "/UnquickenMultiDex2.odex";
+    std::string vdex_location = GetOdexDir() + "/UnquickenMultiDex.vdex";
+    std::string vdex_location2 = GetOdexDir() + "/UnquickenMultiDex2.vdex";
+    Copy(GetTestDexFileName("MultiDex"), dex_location);
+
+    std::unique_ptr<File> vdex_file1(OS::CreateEmptyFile(vdex_location.c_str()));
+    std::unique_ptr<File> vdex_file2(OS::CreateEmptyFile(vdex_location2.c_str()));
+    CHECK(vdex_file1 != nullptr) << vdex_location;
+    CHECK(vdex_file2 != nullptr) << vdex_location2;
+
+    // Quicken the dex file into a vdex file.
+    {
+      std::string input_vdex = "--input-vdex-fd=-1";
+      std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd());
+      GenerateOdexForTest(dex_location,
+                          odex_location,
+                          CompilerFilter::kQuicken,
+                          { input_vdex, output_vdex, "--compact-dex-level=fast"},
+                          /* expect_success */ true,
+                          /* use_fd */ true);
+      EXPECT_GT(vdex_file1->GetLength(), 0u);
+    }
+
+    // Unquicken by running the verify compiler filter on the vdex file.
+    {
+      std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_file1->Fd());
+      std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file2->Fd());
+      GenerateOdexForTest(dex_location,
+                          odex_location2,
+                          CompilerFilter::kVerify,
+                          { input_vdex, output_vdex, "--compact-dex-level=none"},
+                          /* expect_success */ true,
+                          /* use_fd */ true);
+    }
+    ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file";
+    ASSERT_EQ(vdex_file2->FlushCloseOrErase(), 0) << "Could not flush and close vdex file";
+    CheckResult(dex_location, odex_location2);
+    ASSERT_TRUE(success_);
+  }
+
   void CheckResult(const std::string& dex_location, const std::string& odex_location) {
     std::string error_msg;
     std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
@@ -966,6 +1009,10 @@
   RunUnquickenMultiDex();
 }
 
+TEST_F(Dex2oatUnquickenTest, UnquickenMultiDexCDex) {
+  RunUnquickenMultiDexCDex();
+}
+
 class Dex2oatWatchdogTest : public Dex2oatTest {
  protected:
   void RunTest(bool expect_success, const std::vector<std::string>& extra_args = {}) {
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 01125a1..7d8065e 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -3373,15 +3373,19 @@
       CHECK(!update_input_vdex) << "Update input vdex should have empty dex container";
       DexContainer::Section* const section = dex_container_->GetDataSection();
       if (section->Size() > 0) {
-        const uint32_t shared_data_offset = vdex_size_;
         const off_t existing_offset = out->Seek(0, kSeekCurrent);
-        if (static_cast<uint32_t>(existing_offset) != shared_data_offset) {
-          LOG(ERROR) << "Expected offset " << shared_data_offset << " but got " << existing_offset;
+        if (static_cast<uint32_t>(existing_offset) != vdex_dex_shared_data_offset_) {
+          PLOG(ERROR) << "Expected offset " << vdex_dex_shared_data_offset_ << " but got "
+                      << existing_offset;
           return false;
         }
         shared_data_size = section->Size();
         if (!out->WriteFully(section->Begin(), shared_data_size)) {
-          LOG(ERROR) << "Failed to write shared data!";
+          PLOG(ERROR) << "Failed to write shared data!";
+          return false;
+        }
+        if (!out->Flush()) {
+          PLOG(ERROR) << "Failed to flush after writing shared dex section.";
           return false;
         }
         // Fix up the dex headers to have correct offsets to the data section.
@@ -3389,49 +3393,69 @@
           // Overwrite the header by reading it, updating the offset, and writing it back out.
           DexFile::Header header;
           if (!file->PreadFully(&header, sizeof(header), oat_dex_file.dex_file_offset_)) {
-            LOG(ERROR) << "Failed to read dex header for updating";
+            PLOG(ERROR) << "Failed to read dex header for updating";
             return false;
           }
           CHECK(CompactDexFile::IsMagicValid(header.magic_)) << "Must be compact dex";
-          CHECK_GT(shared_data_offset, oat_dex_file.dex_file_offset_);
+          CHECK_GT(vdex_dex_shared_data_offset_, oat_dex_file.dex_file_offset_);
           // Offset is from the dex file base.
-          header.data_off_ = shared_data_offset - oat_dex_file.dex_file_offset_;
+          header.data_off_ = vdex_dex_shared_data_offset_ - oat_dex_file.dex_file_offset_;
           // The size should already be what part of the data buffer may be used by the dex.
           CHECK_LE(header.data_size_, shared_data_size);
           if (!file->PwriteFully(&header, sizeof(header), oat_dex_file.dex_file_offset_)) {
-            LOG(ERROR) << "Failed to write dex header for updating";
+            PLOG(ERROR) << "Failed to write dex header for updating";
             return false;
           }
         }
         section->Clear();
-        if (!out->Flush()) {
-          PLOG(ERROR) << "Failed to flush after writing shared dex section.";
-          return false;
-        }
       }
       dex_container_.reset();
     } else {
-      if (update_input_vdex) {
-        for (OatDexFile& oat_dex_file : oat_dex_files_) {
-          DexFile::Header header;
-          if (!file->PreadFully(&header, sizeof(header), oat_dex_file.dex_file_offset_)) {
-            PLOG(ERROR) << "Failed to read dex header";
-            return false;
+      const uint8_t* data_begin = nullptr;
+      for (OatDexFile& oat_dex_file : oat_dex_files_) {
+        DexFile::Header header;
+        if (!file->PreadFully(&header, sizeof(header), oat_dex_file.dex_file_offset_)) {
+          PLOG(ERROR) << "Failed to read dex header";
+          return false;
+        }
+        if (!CompactDexFile::IsMagicValid(header.magic_)) {
+          // Non compact dex does not have shared data section.
+          continue;
+        }
+        const uint32_t expected_data_off = vdex_dex_shared_data_offset_ -
+            oat_dex_file.dex_file_offset_;
+        if (header.data_off_ != expected_data_off) {
+          PLOG(ERROR) << "Shared data section offset " << header.data_off_
+                      << " does not match expected value " << expected_data_off;
+          return false;
+        }
+        if (oat_dex_file.source_.IsRawData()) {
+          // Figure out the start of the shared data section so we can copy it below.
+          const uint8_t* cur_data_begin = oat_dex_file.source_.GetRawData() + header.data_off_;
+          if (data_begin != nullptr) {
+            CHECK_EQ(data_begin, cur_data_begin);
           }
-          if (!CompactDexFile::IsMagicValid(header.magic_)) {
-            // Non compact dex does not have shared data section.
-            continue;
-          }
-          const uint32_t expected_data_off = vdex_dex_shared_data_offset_ -
-              oat_dex_file.dex_file_offset_;
-          if (header.data_off_ != expected_data_off) {
-            PLOG(ERROR) << "Shared data section offset " << header.data_off_
-                        << " does not match expected value " << expected_data_off;
-            return false;
-          }
-          // The different dex files currently can have different data sizes since
-          // the dex writer writes them one at a time into the shared section.:w
-          shared_data_size = std::max(shared_data_size, header.data_size_);
+          data_begin = cur_data_begin;
+        }
+        // The different dex files currently can have different data sizes since
+        // the dex writer writes them one at a time into the shared section.:w
+        shared_data_size = std::max(shared_data_size, header.data_size_);
+      }
+      // If we are not updating the input vdex, write out the shared data section.
+      if (!update_input_vdex) {
+        const off_t existing_offset = out->Seek(0, kSeekCurrent);
+        if (static_cast<uint32_t>(existing_offset) != vdex_dex_shared_data_offset_) {
+          PLOG(ERROR) << "Expected offset " << vdex_dex_shared_data_offset_ << " but got "
+                      << existing_offset;
+          return false;
+        }
+        if (!out->WriteFully(data_begin, shared_data_size)) {
+          PLOG(ERROR) << "Failed to write shared data!";
+          return false;
+        }
+        if (!out->Flush()) {
+          PLOG(ERROR) << "Failed to flush after writing shared dex section.";
+          return false;
         }
       }
     }