Write link data for dexlayout

Instead of not writing out the link data and leaving an invalid
link_offset, write it out. Fixes dex verifier failures for a few
APKs.

Added test.

Test: test-art-host
Bug: 69561363

Change-Id: Iec1c331f74f9fd58658d4c13465a3bcb6295ce24
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 61a4eae..8421774 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -396,6 +396,14 @@
     eagerly_assign_offsets_ = eagerly_assign_offsets;
   }
 
+  void SetLinkData(std::vector<uint8_t>&& link_data) {
+    link_data_ = std::move(link_data);
+  }
+
+  const std::vector<uint8_t>& LinkData() const {
+    return link_data_;
+  }
+
  private:
   EncodedValue* ReadEncodedValue(const DexFile& dex_file, const uint8_t** data);
   EncodedValue* ReadEncodedValue(const DexFile& dex_file,
@@ -452,6 +460,9 @@
 
   uint32_t map_list_offset_ = 0;
 
+  // Link data.
+  std::vector<uint8_t> link_data_;
+
   // If we eagerly assign offsets during IR building or later after layout. Must be false if
   // changing the layout is enabled.
   bool eagerly_assign_offsets_;
diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc
index 924dfe0..1fd963f 100644
--- a/dexlayout/dex_ir_builder.cc
+++ b/dexlayout/dex_ir_builder.cc
@@ -80,6 +80,11 @@
   // Sort the vectors by the map order (same order as the file).
   collections.SortVectorsByMapOrder();
 
+  // Load the link data if it exists.
+  collections.SetLinkData(std::vector<uint8_t>(
+      dex_file.Begin() + dex_file.GetHeader().link_off_,
+      dex_file.Begin() + dex_file.GetHeader().link_off_ + dex_file.GetHeader().link_size_));
+
   return header;
 }
 
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
index c85bca0..1fac235 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -878,7 +878,15 @@
     }
   }
 
-  // TODO: Write link data?
+  // Write link data if it exists.
+  const std::vector<uint8_t>& link_data = collection.LinkData();
+  if (link_data.size() > 0) {
+    CHECK_EQ(header_->LinkSize(), static_cast<uint32_t>(link_data.size()));
+    if (compute_offsets_) {
+      header_->SetLinkOffset(offset);
+    }
+    offset += Write(&link_data[0], link_data.size(), header_->LinkOffset());
+  }
 
   // Write header last.
   if (compute_offsets_) {
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index c4f7acc..19c9038 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -732,4 +732,45 @@
                             dexlayout_args));
 }
 
+// Test that link data is written out (or at least the header is updated).
+TEST_F(DexLayoutTest, LinkData) {
+  ScratchFile temp_dex;
+  size_t file_size = 0;
+  MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("ManyMethods"), [&] (DexFile* dex) {
+    DexFile::Header& header = const_cast<DexFile::Header&>(dex->GetHeader());
+    header.link_off_ = header.file_size_;
+    header.link_size_ = 16 * KB;
+    header.file_size_ += header.link_size_;
+    file_size = header.file_size_;
+  });
+  TEMP_FAILURE_RETRY(temp_dex.GetFile()->SetLength(file_size));
+
+  std::string error_msg;
+
+  ScratchFile tmp_file;
+  const std::string& tmp_name = tmp_file.GetFilename();
+  size_t tmp_last_slash = tmp_name.rfind('/');
+  std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
+  ScratchFile profile_file;
+
+  std::vector<std::string> dexlayout_args =
+      { "-i",
+        "-v",
+        "-w", tmp_dir,
+        "-o", tmp_name,
+        "-p", profile_file.GetFilename(),
+        temp_dex.GetFilename()
+      };
+  // -v makes sure that the layout did not corrupt the dex file.
+  ASSERT_TRUE(DexLayoutExec(&temp_dex,
+                            /*dex_filename*/ nullptr,
+                            &profile_file,
+                            dexlayout_args));
+
+  std::string output_dex = temp_dex.GetFilename() + ".new";
+  std::vector<std::string> rm_exec_argv =
+      { "/bin/rm", output_dex };
+  ASSERT_TRUE(::art::Exec(rm_exec_argv, &error_msg));
+}
+
 }  // namespace art