Merge "Generate CFI for stackless methods."
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 735755f..533f61c 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -22,6 +22,12 @@
     "libnativeloader",
     "libopenjdkjvm",
     "libopenjdkjvmti",
+    // TODO(b/124439236): Clean up the following libraries once "required"
+    // dependencies work with APEX libraries.
+    "libdt_fd_forward",
+    "libdt_socket",
+    "libjdwp",
+    "libnpt",
 ]
 bionic_native_shared_libs = [
     "libc",
@@ -126,6 +132,16 @@
     private_key: "com.android.runtime.pem",
 }
 
+android_app_certificate {
+    name: "com.android.runtime.debug.certificate",
+    certificate: "com.android.runtime.debug",
+}
+
+android_app_certificate {
+    name: "com.android.runtime.release.certificate",
+    certificate: "com.android.runtime.release",
+}
+
 prebuilt_etc {
     name: "com.android.runtime.ld.config.txt",
     src: "ld.config.txt",
@@ -164,6 +180,7 @@
     prebuilts: art_runtime_data_file_prebuilts
         + ["com.android.runtime.ld.config.txt"],
     key: "com.android.runtime.key",
+    certificate: ":com.android.runtime.release.certificate",
 }
 
 // "Debug" version of the Runtime APEX module (containing both release and
@@ -198,6 +215,7 @@
     prebuilts: art_runtime_data_file_prebuilts
         + ["com.android.runtime.ld.config.txt"],
     key: "com.android.runtime.key",
+    certificate: ":com.android.runtime.debug.certificate",
 }
 
 // TODO: Do this better. art_apex will disable host builds when
diff --git a/build/apex/com.android.runtime.debug.pk8 b/build/apex/com.android.runtime.debug.pk8
new file mode 100644
index 0000000..5eec5d8
--- /dev/null
+++ b/build/apex/com.android.runtime.debug.pk8
Binary files differ
diff --git a/build/apex/com.android.runtime.debug.x509.pem b/build/apex/com.android.runtime.debug.x509.pem
new file mode 100644
index 0000000..73402f5
--- /dev/null
+++ b/build/apex/com.android.runtime.debug.x509.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF0DCCA7igAwIBAgIJALbaJLNNAiRDMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
+aWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRwwGgYDVQQD
+DBNjb20uYW5kcm9pZC5ydW50aW1lMCAXDTE5MDEyNTE3MTQ1NloYDzQ3NTYxMjIx
+MTcxNDU2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG
+A1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwH
+QW5kcm9pZDEcMBoGA1UEAwwTY29tLmFuZHJvaWQucnVudGltZTCCAiIwDQYJKoZI
+hvcNAQEBBQADggIPADCCAgoCggIBAN1Ns75c0ZdLKnUvEuEotzJ0xyOLzOHYP3y6
+RzcwFyPf84aADc7rQDtjbmEuf9g9QpJhZAxe7G2Jg/wxqaxMW6wCfkpuElW5CAcj
+XQ0i12hRVtqePs5Z5bjzJ/8C7luWh82Vb/s2YoRPoKNXVWFT16CB4RMnw2nW5Uyo
+RHZ98N4MgFSGilafIc6Z0DImreTTwlEvcyKihVUSuzeyPG8CRjshw0C1Hqxt4a8J
+rxAgfPpd84Xo4etYePpVr2K5+vNAVwLpUdD48Y7q9peOJ0tbL8DSohudkzvZsQUo
+CfEfrVBfZv7aPnt6ZJYhcFo1WRBMYczKP4jWb0KgmF963ee3zliU1pXtIYsNBNth
+Mdvy3ml301tI7CKE5A3Yevm40VVqo+IDt7FNxoV3rKPhnO9vi/YqzX/1xMvAto8E
+9A5NvMTqHmS2P0wt1pt9KSuXXjoIAWaHZOATDkVI+jLjDrYFNdhqXVgbAaVtI60j
+lRaSWxzBr4o+g2W8ks/JgM2mwJ6qaTNoDMzg823MKzy/t3935sxm5GhFs9AY9Qz/
+4B3exqYUEFJLN6dJLCVppCmFCdCONSxN7bXPo+3b9LlZuKAOP17N04+eKcwXVeYz
+Z3a7SfyMzq+DtLhAn/TSliSbbCAVUxiOZnVX1nM0Gs3/BYCs0TUh2tSqO48pwDrx
+Pw7z9+m5AgMBAAGjUzBRMB0GA1UdDgQWBBRT9s/tu4uqtrglUFjQbwY5p+17DjAf
+BgNVHSMEGDAWgBRT9s/tu4uqtrglUFjQbwY5p+17DjAPBgNVHRMBAf8EBTADAQH/
+MA0GCSqGSIb3DQEBCwUAA4ICAQBNY5giwZCM0sE93Dj2zEe8qCRwIaV4bvSe744X
+Y1+405vdrNEHKPUfFn1xLSnhiGU3loZrP15lexmWvxycLNEy0UxZgq3eR7HuW6xp
+GIm9ttYDZEP+pL9hwew3jiR38NRRR1Ur1MsBNkZnCELC1W8RFWIi77Fsb4fj2mGn
+2R+2voBvVS5kjkytW079CEIsZN9RVYfERiKPCfJDa87kk0xduqyh7sDegQl0B2Ot
+R9KnD1dJZjbii2GRkhpJ/Ig17CQH3J8PY/SIt9L+QAchnIEF051sjbBRUJuPK9gL
+eBEkZkwD1JLqGO6fxkcjNx7MIevTnIBjX2Epr8luyRy7eR3TdBT3aRQcCUqBCi3i
+WxAVR5sOZ90INTXftFbztoklitpQ9mxKXgFr+xggL6u3BdJk1Nt9BsYmRzh5Bg+6
+1eMDBumy3JEA7whE8p75X9cSnKTNrDQU3DA5XzpIhmI91XJArBhBfxgqGxaTf0uq
+SfZRDfnaO456ZsZdKUy62mry6Vg/hvzX52x/HxDlSQWbpYp5t03hshaWxtNE376q
+GdqOoGRRWCvyWi/UOYzabp6czTjwV1JH9IU379CsqIO5UNJ2MM2re4TDXofefU1C
+6eiYihy28xDfIiCdretLRlvFYFF/5X5xby/XWsDA9sGlL5OOiXC6o0Pl9vbek2+T
+Ibx3Nw==
+-----END CERTIFICATE-----
diff --git a/build/apex/com.android.runtime.release.pk8 b/build/apex/com.android.runtime.release.pk8
new file mode 100644
index 0000000..c63efb8
--- /dev/null
+++ b/build/apex/com.android.runtime.release.pk8
Binary files differ
diff --git a/build/apex/com.android.runtime.release.x509.pem b/build/apex/com.android.runtime.release.x509.pem
new file mode 100644
index 0000000..4a7607a
--- /dev/null
+++ b/build/apex/com.android.runtime.release.x509.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF0DCCA7igAwIBAgIJAMtsu/wrkZurMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
+aWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRwwGgYDVQQD
+DBNjb20uYW5kcm9pZC5ydW50aW1lMCAXDTE5MDEyNTE3MTU0MFoYDzQ3NTYxMjIx
+MTcxNTQwWjB8MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG
+A1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwH
+QW5kcm9pZDEcMBoGA1UEAwwTY29tLmFuZHJvaWQucnVudGltZTCCAiIwDQYJKoZI
+hvcNAQEBBQADggIPADCCAgoCggIBAL+aGSc+HU69vV1VbZb6WjXMDrE2Jo+JjXLU
+yVS3o8qlQeqN0RFbsbwnihnwg2xBnM6JiskAcuocz87dDuEt1zUEInC3Hpt/C2eg
+GUZepbq8po+v+b04YlX3aTaYTFqMjU0aQkjOqhnmVxg+KHpvudlvKB3VhH3D61al
+RReQzgM/Q6aUxcr4Z8XwvzV3i0K5NjiSuSt14K2yIaheh2OTbbwtvm3d+0sQDco6
+1gl0l4rM4e+GjxgWVqx8mfKhd4HTS3YIBDWXR6DFPBARzVBIpZu2QK4U6Jdsy2wI
+xg8+d5KWAoNQb7IQK6LQy7Fbw3PNJDo4Ph39G2wNgeMemz8uSQ9FZujc0AgxBom6
+J+ad7zlJBhYFC4UIKBYrRfJCHTN3GLuLvhu0p0jNMfdQXF6Tv/iG9g8JdZ0QjeWm
+/K+h1p6LUAIUV0UP7j8nIdp0j6NqMywkoeRDYlVQV/XdI7BiQe9Z8yNbF5Y3CxWT
+hMfN9iby11ImPilzpgv39ORVjDQdxxcwhJg2Xuu1752cBxcHu3ZcR8AiB7PCksXu
+EpUrjjOH8eVxoG1JJ/na5elUg/H35Or+JYYd8I8Ad1/GRkPrnIBAGzuyntOsNs4t
+2CEnhmV6EkEH8KP8miTdaa5NdPIwFRIHVBHcrqsqdmrINvoJhaVRH7YwmFjv48ak
+N4OyW3oLAgMBAAGjUzBRMB0GA1UdDgQWBBRqVJ0tsEOyqhKiZOrOfRD1+jQFMDAf
+BgNVHSMEGDAWgBRqVJ0tsEOyqhKiZOrOfRD1+jQFMDAPBgNVHRMBAf8EBTADAQH/
+MA0GCSqGSIb3DQEBCwUAA4ICAQAs+I1tRWRPmhA+FqcRdlAcY2Vy7NO12hjWXCT9
+hqenGk1/VnhH8aZT5lXZNaWeKonT5W7XydyrjiF09ndZBGew0rEZh6sMXH+G/drT
+9JxvfHsCQGrmX32V1XgAoRjV1VpUYIb2747fFWHHbl5frowNj955pkfseeKilSnF
+orUl5uGNxr6iNaVEUDfXBWkHwipYVyejAqdHkCQDhLtDBWsiskKpLWmmNKuy2QXQ
+uoyUyfeSR1Y+pT83qgmGb1LFLiOqL9ZKPrsIP+tG4lYB8L4SrYJf4MgfoJaKQ8ed
+2jsd42MegvOB2vdMyLgkf7EM/9DpE4BLpAy2mNd1AccL9YQ+0ezruxh6sYklJWGe
+2bHEbZk0duoNPsA87ZNKfFVV2cNVwSg/22HHjGieMUyPIwyGIzsHId8XiwXpZhLX
+VyacOVRd0SjTWK5Pxj6g21NrrcMXvFeCbveucf2ljKVxBVSbQKt67YlXxd9nLZjN
+zHnJWzDwlWXbyvxheLVVGEo0cqRbhxYMxXd9dM01EXJmIWqS8t0+aw90KKPFITNv
+qpxXnF5JJm1CzeBDtpmfepDupUR1bWansOd0sUuiDF/H1UcDiuBUC643RET1vjhv
+MllsShSeC6KGm3WwE0bhcvA9IdZC8CA3Btzw2J9aJO1gbVZ6vRkH+21cfR07so4N
+yXgprQ==
+-----END CERTIFICATE-----
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h
index bc816da..2fc81c9 100644
--- a/compiler/cfi_test.h
+++ b/compiler/cfi_test.h
@@ -51,14 +51,11 @@
     dwarf::WriteCIE(is64bit, dwarf::Reg(8), initial_opcodes, &debug_frame_data_);
     std::vector<uintptr_t> debug_frame_patches;
     dwarf::WriteFDE(is64bit,
-                    /* section_address= */ 0,
-                    /* cie_address= */ 0,
+                    /* cie_pointer= */ 0,
                     /* code_address= */ 0,
                     actual_asm.size(),
                     actual_cfi,
-                    /* buffer_address= */ 0,
-                    &debug_frame_data_,
-                    &debug_frame_patches);
+                    &debug_frame_data_);
     ReformatCfi(Objdump(false, "-W"), &lines);
     // Pretty-print assembly.
     const uint8_t* asm_base = actual_asm.data();
diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc
index 52717c3..212fd63 100644
--- a/compiler/debug/dwarf/dwarf_test.cc
+++ b/compiler/debug/dwarf/dwarf_test.cc
@@ -121,19 +121,13 @@
 
   DebugFrameOpCodeWriter<> initial_opcodes;
   WriteCIE(is64bit, Reg(is64bit ? 16 : 8), initial_opcodes, &debug_frame_data_);
-  std::vector<uintptr_t> debug_frame_patches;
-  std::vector<uintptr_t> expected_patches = { 28 };
   WriteFDE(is64bit,
-           0,
-           0,
+           /* cie_pointer= */ 0,
            0x01000000,
            0x01000000,
            ArrayRef<const uint8_t>(*opcodes.data()),
-           0,
-           &debug_frame_data_,
-           &debug_frame_patches);
+           &debug_frame_data_);
 
-  EXPECT_EQ(expected_patches, debug_frame_patches);
   CheckObjdumpOutput(is64bit, "-W");
 }
 
@@ -142,20 +136,14 @@
   DebugFrameOpCodeWriter<> initial_opcodes;
   WriteCIE(is64bit, Reg(16), initial_opcodes, &debug_frame_data_);
   DebugFrameOpCodeWriter<> opcodes;
-  std::vector<uintptr_t> debug_frame_patches;
-  std::vector<uintptr_t> expected_patches = { 32 };
   WriteFDE(is64bit,
-           0,
-           0,
+           /* cie_pointer= */ 0,
            0x0100000000000000,
            0x0200000000000000,
            ArrayRef<const uint8_t>(*opcodes.data()),
-           0,
-           &debug_frame_data_,
-           &debug_frame_patches);
+           &debug_frame_data_);
   DW_CHECK("FDE cie=00000000 pc=100000000000000..300000000000000");
 
-  EXPECT_EQ(expected_patches, debug_frame_patches);
   CheckObjdumpOutput(is64bit, "-W");
 }
 
@@ -186,16 +174,12 @@
   DW_CHECK_NEXT("DW_CFA_offset: r15 (r15)");
   DebugFrameOpCodeWriter<> initial_opcodes;
   WriteCIE(is64bit, Reg(16), initial_opcodes, &debug_frame_data_);
-  std::vector<uintptr_t> debug_frame_patches;
   WriteFDE(is64bit,
-           0,
-           0,
+           /* cie_pointer= */ 0,
            0x0100000000000000,
            0x0200000000000000,
            ArrayRef<const uint8_t>(*opcodes.data()),
-           0,
-           &debug_frame_data_,
-           &debug_frame_patches);
+           &debug_frame_data_);
 
   CheckObjdumpOutput(is64bit, "-W");
 }
@@ -250,12 +234,8 @@
   DW_CHECK_NEXT("Entry\tDir\tTime\tSize\tName");
   DW_CHECK_NEXT("1\t0\t1000\t2000\tfile.c");
 
-  std::vector<uintptr_t> debug_line_patches;
-  std::vector<uintptr_t> expected_patches = { 87 };
-  WriteDebugLineTable(include_directories, files, opcodes,
-                      0, &debug_line_data_, &debug_line_patches);
+  WriteDebugLineTable(include_directories, files, opcodes, &debug_line_data_);
 
-  EXPECT_EQ(expected_patches, debug_line_patches);
   CheckObjdumpOutput(is64bit, "-W");
 }
 
@@ -290,9 +270,7 @@
 
   std::vector<std::string> directories;
   std::vector<FileEntry> files = { { "file.c", 0, 1000, 2000 } };
-  std::vector<uintptr_t> debug_line_patches;
-  WriteDebugLineTable(directories, files, opcodes,
-                      0, &debug_line_data_, &debug_line_patches);
+  WriteDebugLineTable(directories, files, opcodes, &debug_line_data_);
 
   CheckObjdumpOutput(is64bit, "-W -WL");
 }
@@ -346,12 +324,8 @@
   DW_CHECK_NEXT("DW_AT_high_pc      DW_FORM_addr");
   DW_CHECK("3      DW_TAG_compile_unit    [no children]");
 
-  std::vector<uintptr_t> debug_info_patches;
-  std::vector<uintptr_t> expected_patches = { 16, 20, 29, 33, 42, 46 };
-  dwarf::WriteDebugInfoCU(/* debug_abbrev_offset= */ 0, info,
-                          0, &debug_info_data_, &debug_info_patches);
+  dwarf::WriteDebugInfoCU(/* debug_abbrev_offset= */ 0, info, &debug_info_data_);
 
-  EXPECT_EQ(expected_patches, debug_info_patches);
   CheckObjdumpOutput(is64bit, "-W");
 }
 
diff --git a/compiler/debug/dwarf/headers.h b/compiler/debug/dwarf/headers.h
index 119bfe9..869a2c2 100644
--- a/compiler/debug/dwarf/headers.h
+++ b/compiler/debug/dwarf/headers.h
@@ -67,26 +67,16 @@
 // Write frame description entry (FDE) to .debug_frame or .eh_frame section.
 inline
 void WriteFDE(bool is64bit,
-              uint64_t section_address,  // Absolute address of the section.
-              uint64_t cie_address,  // Absolute address of last CIE.
+              uint64_t cie_pointer,  // Offset of relevant CIE in debug_frame setcion.
               uint64_t code_address,
               uint64_t code_size,
               const ArrayRef<const uint8_t>& opcodes,
-              uint64_t buffer_address,  // Address of buffer in linked application.
-              std::vector<uint8_t>* buffer,
-              std::vector<uintptr_t>* patch_locations) {
-  CHECK_GE(cie_address, section_address);
-  CHECK_GE(buffer_address, section_address);
-
+              /*inout*/ std::vector<uint8_t>* buffer) {
   Writer<> writer(buffer);
   size_t fde_header_start = writer.data()->size();
   writer.PushUint32(0);  // Length placeholder.
-  uint32_t cie_pointer = cie_address - section_address;
   writer.PushUint32(cie_pointer);
   // Relocate code_address if it has absolute value.
-  if (patch_locations != nullptr) {
-    patch_locations->push_back(buffer_address + buffer->size() - section_address);
-  }
   if (is64bit) {
     writer.PushUint64(code_address);
     writer.PushUint64(code_size);
@@ -128,9 +118,7 @@
 template<typename Vector>
 void WriteDebugInfoCU(uint32_t debug_abbrev_offset,
                       const DebugInfoEntryWriter<Vector>& entries,
-                      size_t debug_info_offset,  // offset from start of .debug_info.
-                      std::vector<uint8_t>* debug_info,
-                      std::vector<uintptr_t>* debug_info_patches) {
+                      std::vector<uint8_t>* debug_info) {
   static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
 
   Writer<> writer(debug_info);
@@ -143,10 +131,6 @@
   DCHECK_EQ(entries_offset, DebugInfoEntryWriter<Vector>::kCompilationUnitHeaderSize);
   writer.PushData(entries.data());
   writer.UpdateUint32(start, writer.data()->size() - start - 4);
-  // Copy patch locations and make them relative to .debug_info section.
-  for (uintptr_t patch_location : entries.GetPatchLocations()) {
-    debug_info_patches->push_back(debug_info_offset + entries_offset + patch_location);
-  }
 }
 
 struct FileEntry {
@@ -161,9 +145,7 @@
 void WriteDebugLineTable(const std::vector<std::string>& include_directories,
                          const std::vector<FileEntry>& files,
                          const DebugLineOpCodeWriter<Vector>& opcodes,
-                         size_t debug_line_offset,  // offset from start of .debug_line.
-                         std::vector<uint8_t>* debug_line,
-                         std::vector<uintptr_t>* debug_line_patches) {
+                         std::vector<uint8_t>* debug_line) {
   static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
 
   Writer<> writer(debug_line);
@@ -194,13 +176,8 @@
   }
   writer.PushUint8(0);  // Terminate file list.
   writer.UpdateUint32(header_length_pos, writer.data()->size() - header_length_pos - 4);
-  size_t opcodes_offset = writer.data()->size();
   writer.PushData(opcodes.data());
   writer.UpdateUint32(header_start, writer.data()->size() - header_start - 4);
-  // Copy patch locations and make them relative to .debug_line section.
-  for (uintptr_t patch_location : opcodes.GetPatchLocations()) {
-    debug_line_patches->push_back(debug_line_offset + opcodes_offset + patch_location);
-  }
 }
 
 }  // namespace dwarf
diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h
index a6bbea4..c5fa647 100644
--- a/compiler/debug/elf_debug_frame_writer.h
+++ b/compiler/debug/elf_debug_frame_writer.h
@@ -169,8 +169,7 @@
 
 template<typename ElfTypes>
 void WriteCFISection(linker::ElfBuilder<ElfTypes>* builder,
-                     const ArrayRef<const MethodDebugInfo>& method_infos,
-                     bool write_oat_patches) {
+                     const ArrayRef<const MethodDebugInfo>& method_infos) {
   typedef typename ElfTypes::Addr Elf_Addr;
 
   // The methods can be written in any order.
@@ -197,11 +196,8 @@
       });
 
   std::vector<uint32_t> binary_search_table;
-  std::vector<uintptr_t> patch_locations;
   if (kWriteDebugFrameHdr) {
     binary_search_table.reserve(2 * sorted_method_infos.size());
-  } else {
-    patch_locations.reserve(sorted_method_infos.size());
   }
 
   // Write .debug_frame section.
@@ -209,11 +205,9 @@
   {
     cfi_section->Start();
     const bool is64bit = Is64BitInstructionSet(builder->GetIsa());
-    Elf_Addr buffer_address = 0;
     std::vector<uint8_t> buffer;  // Small temporary buffer.
     WriteCIE(builder->GetIsa(), &buffer);
     cfi_section->WriteFully(buffer.data(), buffer.size());
-    buffer_address += buffer.size();
     buffer.clear();
     for (const MethodDebugInfo* mi : sorted_method_infos) {
       DCHECK(!mi->deduped);
@@ -222,19 +216,15 @@
           (mi->is_code_address_text_relative ? builder->GetText()->GetAddress() : 0);
       if (kWriteDebugFrameHdr) {
         binary_search_table.push_back(dchecked_integral_cast<uint32_t>(code_address));
-        binary_search_table.push_back(dchecked_integral_cast<uint32_t>(buffer_address));
+        binary_search_table.push_back(cfi_section->GetPosition());
       }
       dwarf::WriteFDE(is64bit,
-                      0,
-                      0,
+                      /* cie_pointer= */ 0,
                       code_address,
                       mi->code_size,
                       mi->cfi,
-                      buffer_address,
-                      &buffer,
-                      &patch_locations);
+                      &buffer);
       cfi_section->WriteFully(buffer.data(), buffer.size());
-      buffer_address += buffer.size();
       buffer.clear();
     }
     cfi_section->End();
@@ -254,11 +244,6 @@
     header_section->WriteFully(header_buffer.data(), header_buffer.size());
     header_section->WriteFully(binary_search_table.data(), binary_search_table.size());
     header_section->End();
-  } else {
-    if (write_oat_patches) {
-      builder->WritePatches(".debug_frame.oat_patches",
-                            ArrayRef<const uintptr_t>(patch_locations));
-    }
   }
 }
 
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index a63f241..05a4a3e 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -68,12 +68,8 @@
     builder_->GetDebugInfo()->Start();
   }
 
-  void End(bool write_oat_patches) {
+  void End() {
     builder_->GetDebugInfo()->End();
-    if (write_oat_patches) {
-      builder_->WritePatches(".debug_info.oat_patches",
-                             ArrayRef<const uintptr_t>(debug_info_patches_));
-    }
     builder_->WriteSection(".debug_abbrev", &debug_abbrev_buffer_);
     if (!debug_loc_.empty()) {
       builder_->WriteSection(".debug_loc", &debug_loc_);
@@ -85,7 +81,6 @@
 
  private:
   linker::ElfBuilder<ElfTypes>* builder_;
-  std::vector<uintptr_t> debug_info_patches_;
   std::vector<uint8_t> debug_abbrev_buffer_;
   dwarf::DebugAbbrevWriter<> debug_abbrev_;
   std::vector<uint8_t> debug_loc_;
@@ -281,10 +276,9 @@
     CHECK_EQ(info_.Depth(), 0);
     std::vector<uint8_t> buffer;
     buffer.reserve(info_.data()->size() + KB);
-    const size_t offset = owner_->builder_->GetDebugInfo()->GetPosition();
     // All compilation units share single table which is at the start of .debug_abbrev.
     const size_t debug_abbrev_offset = 0;
-    WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_);
+    WriteDebugInfoCU(debug_abbrev_offset, info_, &buffer);
     owner_->builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size());
   }
 
@@ -446,10 +440,9 @@
     CHECK_EQ(info_.Depth(), 0);
     std::vector<uint8_t> buffer;
     buffer.reserve(info_.data()->size() + KB);
-    const size_t offset = owner_->builder_->GetDebugInfo()->GetPosition();
     // All compilation units share single table which is at the start of .debug_abbrev.
     const size_t debug_abbrev_offset = 0;
-    WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_);
+    WriteDebugInfoCU(debug_abbrev_offset, info_, &buffer);
     owner_->builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size());
   }
 
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index 0a13a92..f95912a 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -263,23 +263,17 @@
     }
     std::vector<uint8_t> buffer;
     buffer.reserve(opcodes.data()->size() + KB);
-    size_t offset = builder_->GetDebugLine()->GetPosition();
-    WriteDebugLineTable(directories, files, opcodes, offset, &buffer, &debug_line_patches_);
+    WriteDebugLineTable(directories, files, opcodes, &buffer);
     builder_->GetDebugLine()->WriteFully(buffer.data(), buffer.size());
     return buffer.size();
   }
 
-  void End(bool write_oat_patches) {
+  void End() {
     builder_->GetDebugLine()->End();
-    if (write_oat_patches) {
-      builder_->WritePatches(".debug_line.oat_patches",
-                             ArrayRef<const uintptr_t>(debug_line_patches_));
-    }
   }
 
  private:
   linker::ElfBuilder<ElfTypes>* builder_;
-  std::vector<uintptr_t> debug_line_patches_;
 };
 
 }  // namespace debug
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index 4bd0eac..fd638b7 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -44,13 +44,12 @@
 
 template <typename ElfTypes>
 void WriteDebugInfo(linker::ElfBuilder<ElfTypes>* builder,
-                    const DebugInfo& debug_info,
-                    bool write_oat_patches) {
+                    const DebugInfo& debug_info) {
   // Write .strtab and .symtab.
   WriteDebugSymbols(builder, /* mini-debug-info= */ false, debug_info);
 
   // Write .debug_frame.
-  WriteCFISection(builder, debug_info.compiled_methods, write_oat_patches);
+  WriteCFISection(builder, debug_info.compiled_methods);
 
   // Group the methods into compilation units based on class.
   std::unordered_map<const dex::ClassDef*, ElfCompilationUnit> class_to_compilation_unit;
@@ -95,7 +94,7 @@
     for (auto& compilation_unit : compilation_units) {
       line_writer.WriteCompilationUnit(compilation_unit);
     }
-    line_writer.End(write_oat_patches);
+    line_writer.End();
   }
 
   // Write .debug_info section.
@@ -106,7 +105,7 @@
       ElfCompilationUnitWriter<ElfTypes> cu_writer(&info_writer);
       cu_writer.Write(compilation_unit);
     }
-    info_writer.End(write_oat_patches);
+    info_writer.End();
   }
 }
 
@@ -136,7 +135,7 @@
     WriteDebugSymbols(builder.get(), /* mini-debug-info= */ true, debug_info);
   }
   if (!debug_info.compiled_methods.empty()) {
-    WriteCFISection(builder.get(), debug_info.compiled_methods, /* write_oat_patches= */ false);
+    WriteCFISection(builder.get(), debug_info.compiled_methods);
   }
   builder->End();
   CHECK(builder->Good());
@@ -195,9 +194,9 @@
     // The compression is great help for multiple methods but it is not worth it for a
     // single method due to the overheads so skip the compression here for performance.
     WriteDebugSymbols(builder.get(), /* mini-debug-info= */ true, debug_info);
-    WriteCFISection(builder.get(), debug_info.compiled_methods, /* write_oat_patches= */ false);
+    WriteCFISection(builder.get(), debug_info.compiled_methods);
   } else {
-    WriteDebugInfo(builder.get(), debug_info, /* write_oat_patches= */ false);
+    WriteDebugInfo(builder.get(), debug_info);
   }
   builder->End();
   CHECK(builder->Good());
@@ -283,14 +282,11 @@
                 return;
               }
               dwarf::WriteFDE(is64bit,
-                              /*section_address=*/ 0,
-                              /*cie_address=*/ 0,
+                              /* cie_pointer= */ 0,
                               addr,
                               size,
                               opcodes,
-                              debug_frame_buffer.size(),
-                              &debug_frame_buffer,
-                              /*patch_locations=*/ nullptr);
+                              &debug_frame_buffer);
           });
     }
     strtab->End();
@@ -359,7 +355,7 @@
   info_writer.Start();
   ElfCompilationUnitWriter<ElfTypes> cu_writer(&info_writer);
   cu_writer.Write(types);
-  info_writer.End(/* write_oat_patches= */ false);
+  info_writer.End();
 
   builder->End();
   CHECK(builder->Good());
@@ -369,12 +365,10 @@
 // Explicit instantiations
 template void WriteDebugInfo<ElfTypes32>(
     linker::ElfBuilder<ElfTypes32>* builder,
-    const DebugInfo& debug_info,
-    bool write_oat_patches);
+    const DebugInfo& debug_info);
 template void WriteDebugInfo<ElfTypes64>(
     linker::ElfBuilder<ElfTypes64>* builder,
-    const DebugInfo& debug_info,
-    bool write_oat_patches);
+    const DebugInfo& debug_info);
 
 }  // namespace debug
 }  // namespace art
diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h
index d5aa9cd..3cc38a2 100644
--- a/compiler/debug/elf_debug_writer.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -37,8 +37,7 @@
 template <typename ElfTypes>
 void WriteDebugInfo(
     linker::ElfBuilder<ElfTypes>* builder,
-    const DebugInfo& debug_info,
-    bool write_oat_patches);
+    const DebugInfo& debug_info);
 
 std::vector<uint8_t> MakeMiniDebugInfo(
     InstructionSet isa,
diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc
index d5ceafe..f978cc6 100644
--- a/compiler/exception_test.cc
+++ b/compiler/exception_test.cc
@@ -80,7 +80,7 @@
     stack_maps.BeginMethod(4 * sizeof(void*), 0u, 0u, 0u);
     stack_maps.BeginStackMapEntry(kDexPc, native_pc_offset);
     stack_maps.EndStackMapEntry();
-    stack_maps.EndMethod();
+    stack_maps.EndMethod(code_size);
     ScopedArenaVector<uint8_t> stack_map = stack_maps.Encode();
 
     const size_t stack_maps_size = stack_map.size();
diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h
index 54e07fc..33e1866 100644
--- a/compiler/linker/elf_builder.h
+++ b/compiler/linker/elf_builder.h
@@ -51,14 +51,10 @@
 //   .strtab                     - Names for .symtab.
 //   .symtab                     - Debug symbols.
 //   .debug_frame                - Unwind information (CFI).
-//   .debug_frame.oat_patches    - Addresses for relocation.
 //   .debug_info                 - Debug information.
-//   .debug_info.oat_patches     - Addresses for relocation.
 //   .debug_abbrev               - Decoding information for .debug_info.
 //   .debug_str                  - Strings for .debug_info.
 //   .debug_line                 - Line number tables.
-//   .debug_line.oat_patches     - Addresses for relocation.
-//   .text.oat_patches           - Addresses for relocation.
 //   .shstrtab                   - Names of ELF sections.
 //   Elf_Shdr[]                  - Section headers.
 //
@@ -575,29 +571,6 @@
   Section* GetDebugInfo() { return &debug_info_; }
   Section* GetDebugLine() { return &debug_line_; }
 
-  // Encode patch locations as LEB128 list of deltas between consecutive addresses.
-  // (exposed publicly for tests)
-  static void EncodeOatPatches(const ArrayRef<const uintptr_t>& locations,
-                               std::vector<uint8_t>* buffer) {
-    buffer->reserve(buffer->size() + locations.size() * 2);  // guess 2 bytes per ULEB128.
-    uintptr_t address = 0;  // relative to start of section.
-    for (uintptr_t location : locations) {
-      DCHECK_GE(location, address) << "Patch locations are not in sorted order";
-      EncodeUnsignedLeb128(buffer, dchecked_integral_cast<uint32_t>(location - address));
-      address = location;
-    }
-  }
-
-  void WritePatches(const char* name, const ArrayRef<const uintptr_t>& patch_locations) {
-    std::vector<uint8_t> buffer;
-    EncodeOatPatches(patch_locations, &buffer);
-    std::unique_ptr<Section> s(new Section(this, name, SHT_OAT_PATCH, 0, nullptr, 0, 1, 0));
-    s->Start();
-    s->WriteFully(buffer.data(), buffer.size());
-    s->End();
-    other_sections_.push_back(std::move(s));
-  }
-
   void WriteSection(const char* name, const std::vector<uint8_t>* buffer) {
     std::unique_ptr<Section> s(new Section(this, name, SHT_PROGBITS, 0, nullptr, 0, 1, 0));
     s->Start();
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 9e2f5cd..122f27b 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -440,7 +440,7 @@
   // Finalize instructions in assember;
   Finalize(allocator);
 
-  GetStackMapStream()->EndMethod();
+  GetStackMapStream()->EndMethod(GetAssembler()->CodeSize());
 }
 
 void CodeGenerator::Finalize(CodeAllocator* allocator) {
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index e8f8d32..4f43b71 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -1171,7 +1171,8 @@
 }
 
 static ScopedArenaVector<uint8_t> CreateJniStackMap(ScopedArenaAllocator* allocator,
-                                                    const JniCompiledMethod& jni_compiled_method) {
+                                                    const JniCompiledMethod& jni_compiled_method,
+                                                    size_t code_size) {
   // StackMapStream is quite large, so allocate it using the ScopedArenaAllocator
   // to stay clear of the frame size limit.
   std::unique_ptr<StackMapStream> stack_map_stream(
@@ -1181,7 +1182,7 @@
       jni_compiled_method.GetCoreSpillMask(),
       jni_compiled_method.GetFpSpillMask(),
       /* num_dex_registers= */ 0);
-  stack_map_stream->EndMethod();
+  stack_map_stream->EndMethod(code_size);
   return stack_map_stream->Encode();
 }
 
@@ -1239,8 +1240,8 @@
   MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiledNativeStub);
 
   ScopedArenaAllocator stack_map_allocator(&arena_stack);  // Will hold the stack map.
-  ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(&stack_map_allocator,
-                                                           jni_compiled_method);
+  ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(
+      &stack_map_allocator, jni_compiled_method, jni_compiled_method.GetCode().size());
   return CompiledMethod::SwapAllocCompiledMethod(
       GetCompilerDriver()->GetCompiledMethodStorage(),
       jni_compiled_method.GetInstructionSet(),
@@ -1290,8 +1291,8 @@
     ArenaStack arena_stack(runtime->GetJitArenaPool());
     // StackMapStream is large and it does not fit into this frame, so we need helper method.
     ScopedArenaAllocator stack_map_allocator(&arena_stack);  // Will hold the stack map.
-    ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(&stack_map_allocator,
-                                                             jni_compiled_method);
+    ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(
+        &stack_map_allocator, jni_compiled_method, jni_compiled_method.GetCode().size());
     uint8_t* stack_map_data = nullptr;
     uint8_t* roots_data = nullptr;
     uint32_t data_size = code_cache->ReserveData(self,
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 60ca61c..e87f3c8 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -54,9 +54,10 @@
   num_dex_registers_ = num_dex_registers;
 }
 
-void StackMapStream::EndMethod() {
+void StackMapStream::EndMethod(size_t code_size) {
   DCHECK(in_method_) << "Mismatched Begin/End calls";
   in_method_ = false;
+  packed_code_size_ = StackMap::PackNativePc(code_size, instruction_set_);
 
   // Read the stack masks now. The compiler might have updated them.
   for (size_t i = 0; i < lazy_stack_masks_.size(); i++) {
@@ -66,6 +67,10 @@
           stack_masks_.Dedup(stack_mask->GetRawStorage(), stack_mask->GetNumberOfBits());
     }
   }
+
+  for (size_t i = 0; i < stack_maps_.size(); i++) {
+    DCHECK_LE(stack_maps_[i][StackMap::kPackedNativePc], packed_code_size_);
+  }
 }
 
 void StackMapStream::BeginStackMapEntry(uint32_t dex_pc,
@@ -296,6 +301,7 @@
 
   ScopedArenaVector<uint8_t> buffer(allocator_->Adapter(kArenaAllocStackMapStream));
   BitMemoryWriter<ScopedArenaVector<uint8_t>> out(&buffer);
+  out.WriteVarint(packed_code_size_);
   out.WriteVarint(packed_frame_size_);
   out.WriteVarint(core_spill_mask_);
   out.WriteVarint(fp_spill_mask_);
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 01c6bf9..164e902 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -62,7 +62,7 @@
                    size_t core_spill_mask,
                    size_t fp_spill_mask,
                    uint32_t num_dex_registers);
-  void EndMethod();
+  void EndMethod(size_t code_size);
 
   void BeginStackMapEntry(uint32_t dex_pc,
                           uint32_t native_pc_offset,
@@ -99,6 +99,7 @@
 
   ScopedArenaAllocator* allocator_;
   const InstructionSet instruction_set_;
+  uint32_t packed_code_size_ = 0;
   uint32_t packed_frame_size_ = 0;
   uint32_t core_spill_mask_ = 0;
   uint32_t fp_spill_mask_ = 0;
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index d28f09f..cbd844f 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -61,7 +61,7 @@
   stream.AddDexRegisterEntry(Kind::kConstant, -2);       // Short location.
   stream.EndStackMapEntry();
 
-  stream.EndMethod();
+  stream.EndMethod(64 * kPcAlign);
   ScopedArenaVector<uint8_t> memory = stream.Encode();
 
   CodeInfo code_info(memory.data());
@@ -147,7 +147,7 @@
   stream.AddDexRegisterEntry(Kind::kInFpuRegisterHigh, 1);  // Short location.
   stream.EndStackMapEntry();
 
-  stream.EndMethod();
+  stream.EndMethod(256 * kPcAlign);
   ScopedArenaVector<uint8_t> memory = stream.Encode();
 
   CodeInfo code_info(memory.data());
@@ -317,7 +317,7 @@
   stream.EndInlineInfoEntry();
   stream.EndStackMapEntry();
 
-  stream.EndMethod();
+  stream.EndMethod(64 * kPcAlign);
   ScopedArenaVector<uint8_t> memory = stream.Encode();
 
   CodeInfo code_info(memory.data());
@@ -372,7 +372,7 @@
   stream.AddDexRegisterEntry(Kind::kConstant, -2);       // Large location.
   stream.EndStackMapEntry();
 
-  stream.EndMethod();
+  stream.EndMethod(64 * kPcAlign);
   ScopedArenaVector<uint8_t> memory = stream.Encode();
 
   CodeInfo code_info(memory.data());
@@ -431,7 +431,7 @@
   stream.AddDexRegisterEntry(Kind::kConstant, -2);   // Large location.
   stream.EndStackMapEntry();
 
-  stream.EndMethod();
+  stream.EndMethod(66 * kPcAlign);
   ScopedArenaVector<uint8_t> memory = stream.Encode();
 
   CodeInfo ci(memory.data());
@@ -479,7 +479,7 @@
   stream.AddDexRegisterEntry(Kind::kNone, 0);
   stream.EndStackMapEntry();
 
-  stream.EndMethod();
+  stream.EndMethod(68 * kPcAlign);
   ScopedArenaVector<uint8_t> memory = stream.Encode();
 
   CodeInfo code_info(memory.data());
@@ -578,7 +578,7 @@
 
   stream.EndStackMapEntry();
 
-  stream.EndMethod();
+  stream.EndMethod(78 * kPcAlign);
   ScopedArenaVector<uint8_t> memory = stream.Encode();
 
   CodeInfo ci(memory.data());
@@ -722,7 +722,7 @@
   stream.BeginStackMapEntry(0, 8 * kPcAlign, 0x3, &sp_mask);
   stream.EndStackMapEntry();
 
-  stream.EndMethod();
+  stream.EndMethod(8 * kPcAlign);
   ScopedArenaVector<uint8_t> memory = stream.Encode();
 
   CodeInfo code_info(memory.data());
@@ -746,7 +746,7 @@
   stream.AddDexRegisterEntry(Kind::kConstant, -2);
   stream.EndStackMapEntry();
 
-  stream.EndMethod();
+  stream.EndMethod(64 * kPcAlign);
   ScopedArenaVector<uint8_t> memory = stream.Encode();
 
   std::vector<uint8_t> out;
diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc
index 521bc78..7e0f29d 100644
--- a/dex2oat/linker/elf_writer_quick.cc
+++ b/dex2oat/linker/elf_writer_quick.cc
@@ -281,7 +281,7 @@
   // The Strip method expects debug info to be last (mini-debug-info is not stripped).
   if (!debug_info.Empty() && compiler_options_.GetGenerateDebugInfo()) {
     // Generate all the debug information we can.
-    debug::WriteDebugInfo(builder_.get(), debug_info, true /* write_oat_patches */);
+    debug::WriteDebugInfo(builder_.get(), debug_info);
   }
 }
 
diff --git a/dex2oat/linker/elf_writer_test.cc b/dex2oat/linker/elf_writer_test.cc
index b381765..e016f29 100644
--- a/dex2oat/linker/elf_writer_test.cc
+++ b/dex2oat/linker/elf_writer_test.cc
@@ -139,43 +139,5 @@
   }
 }
 
-TEST_F(ElfWriterTest, EncodeDecodeOatPatches) {
-  const std::vector<std::vector<uintptr_t>> test_data {
-      { 0, 4, 8, 15, 128, 200 },
-      { 8, 8 + 127 },
-      { 8, 8 + 128 },
-      { },
-  };
-  for (const auto& patch_locations : test_data) {
-    constexpr int32_t delta = 0x11235813;
-
-    // Encode patch locations.
-    std::vector<uint8_t> oat_patches;
-    ElfBuilder<ElfTypes32>::EncodeOatPatches(ArrayRef<const uintptr_t>(patch_locations),
-                                             &oat_patches);
-
-    // Create buffer to be patched.
-    std::vector<uint8_t> initial_data(256);
-    for (size_t i = 0; i < initial_data.size(); i++) {
-      initial_data[i] = i;
-    }
-
-    // Patch manually.
-    std::vector<uint8_t> expected = initial_data;
-    for (uintptr_t location : patch_locations) {
-      using UnalignedAddress __attribute__((__aligned__(1))) = uint32_t;
-      *reinterpret_cast<UnalignedAddress*>(expected.data() + location) += delta;
-    }
-
-    // Decode and apply patch locations.
-    std::vector<uint8_t> actual = initial_data;
-    ElfFileImpl32::ApplyOatPatches(
-        oat_patches.data(), oat_patches.data() + oat_patches.size(), delta,
-        actual.data(), actual.data() + actual.size());
-
-    EXPECT_EQ(expected, actual);
-  }
-}
-
 }  // namespace linker
 }  // namespace art
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index feb5e38..2289a9c 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -205,6 +205,8 @@
 cc_library_headers {
     name: "libdexfile_external_headers",
     host_supported: true,
+    vendor_available: true,
+    recovery_available: true,
     header_libs: ["libbase_headers"],
     export_header_lib_headers: ["libbase_headers"],
     export_include_dirs: ["external/include"],
@@ -245,23 +247,37 @@
 }
 
 // Support library with a C++ API for accessing the libdexfile API for external
-// (non-ART) users. They should link to their own instance of this (either
-// statically or through linker namespaces).
+// (non-ART) users.
+//
+// This library dlopens libdexfile_external on first use, so there is no build
+// time dependency on dex file logic. It is therefore safe to use from binaries
+// compiled without dex file support, given they won't encounter any dex file
+// stack frames.
 cc_library {
     name: "libdexfile_support",
     host_supported: true,
+    vendor_available: true,
+    recovery_available: true,
     srcs: [
         "external/dex_file_supp.cc",
     ],
+    required: ["libdexfile_external"],
+    shared_libs: ["liblog"],
     header_libs: ["libdexfile_external_headers"],
-    shared_libs: ["libdexfile_external"],
     export_header_lib_headers: ["libdexfile_external_headers"],
+    target: {
+        recovery: {
+            cflags: ["-DNO_DEXFILE_SUPPORT"],
+        },
+        vendor: {
+            cflags: ["-DNO_DEXFILE_SUPPORT"],
+        },
+    },
 }
 
 art_cc_test {
     name: "art_libdexfile_support_tests",
     host_supported: true,
-    test_per_src: true,  // For consistency with other ART gtests.
     srcs: [
         "external/dex_file_supp_test.cc",
     ],
@@ -272,3 +288,36 @@
         "libdexfile_support",
     ],
 }
+
+cc_library_static {
+    name: "libdexfile_support_static",
+    host_supported: true,
+    defaults: ["libdexfile_static_defaults"],
+    srcs: [
+        "external/dex_file_supp.cc",
+    ],
+    cflags: ["-DSTATIC_LIB"],
+    // Using whole_static_libs here only as a "poor man's transitivity" kludge.
+    whole_static_libs: [
+        "libbase",
+        "libdexfile",
+        "libdexfile_external",
+        "liblog",
+        "libz",
+        "libziparchive",
+    ],
+    header_libs: ["libdexfile_external_headers"],
+    export_header_lib_headers: ["libdexfile_external_headers"],
+}
+
+art_cc_test {
+    name: "art_libdexfile_support_static_tests",
+    host_supported: true,
+    srcs: [
+        "external/dex_file_supp_test.cc",
+    ],
+    static_libs: [
+        "libbase",
+        "libdexfile_support_static",
+    ],
+}
diff --git a/libdexfile/external/dex_file_supp.cc b/libdexfile/external/dex_file_supp.cc
index 5bd25fc..ba684fe 100644
--- a/libdexfile/external/dex_file_supp.cc
+++ b/libdexfile/external/dex_file_supp.cc
@@ -16,10 +16,74 @@
 
 #include "art_api/dex_file_support.h"
 
+#include <dlfcn.h>
+#include <mutex>
+
+#ifndef STATIC_LIB
+// Not used in the static lib, so avoid a dependency on this header in
+// libdexfile_support_static.
+#include <log/log.h>
+#endif
+
 namespace art_api {
 namespace dex {
 
-DexFile::~DexFile() { ExtDexFileFree(ext_dex_file_); }
+#ifdef STATIC_LIB
+#define DEFINE_DLFUNC_PTR(CLASS, DLFUNC) decltype(DLFUNC)* CLASS::g_##DLFUNC = DLFUNC
+#else
+#define DEFINE_DLFUNC_PTR(CLASS, DLFUNC) decltype(DLFUNC)* CLASS::g_##DLFUNC = nullptr
+#endif
+
+DEFINE_DLFUNC_PTR(DexString, ExtDexFileMakeString);
+DEFINE_DLFUNC_PTR(DexString, ExtDexFileGetString);
+DEFINE_DLFUNC_PTR(DexString, ExtDexFileFreeString);
+DEFINE_DLFUNC_PTR(DexFile, ExtDexFileOpenFromMemory);
+DEFINE_DLFUNC_PTR(DexFile, ExtDexFileOpenFromFd);
+DEFINE_DLFUNC_PTR(DexFile, ExtDexFileGetMethodInfoForOffset);
+DEFINE_DLFUNC_PTR(DexFile, ExtDexFileGetAllMethodInfos);
+DEFINE_DLFUNC_PTR(DexFile, ExtDexFileFree);
+
+#undef DEFINE_DLFUNC_PTR
+
+void LoadLibdexfileExternal() {
+#if defined(STATIC_LIB)
+  // Nothing to do here since all function pointers are initialised statically.
+#elif defined(NO_DEXFILE_SUPPORT)
+  LOG_FATAL("Dex file support not available.");
+#else
+  static std::once_flag dlopen_once;
+  std::call_once(dlopen_once, []() {
+    constexpr char kLibdexfileExternalLib[] = "libdexfile_external.so";
+    void* handle =
+        dlopen(kLibdexfileExternalLib, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE);
+    LOG_ALWAYS_FATAL_IF(handle == nullptr, "Failed to load %s: %s",
+                        kLibdexfileExternalLib, dlerror());
+
+#define SET_DLFUNC_PTR(CLASS, DLFUNC) \
+  do { \
+    CLASS::g_##DLFUNC = reinterpret_cast<decltype(DLFUNC)*>(dlsym(handle, #DLFUNC)); \
+    LOG_ALWAYS_FATAL_IF(CLASS::g_##DLFUNC == nullptr, \
+                        "Failed to find %s in %s: %s", \
+                        #DLFUNC, \
+                        kLibdexfileExternalLib, \
+                        dlerror()); \
+  } while (0)
+
+    SET_DLFUNC_PTR(DexString, ExtDexFileMakeString);
+    SET_DLFUNC_PTR(DexString, ExtDexFileGetString);
+    SET_DLFUNC_PTR(DexString, ExtDexFileFreeString);
+    SET_DLFUNC_PTR(DexFile, ExtDexFileOpenFromMemory);
+    SET_DLFUNC_PTR(DexFile, ExtDexFileOpenFromFd);
+    SET_DLFUNC_PTR(DexFile, ExtDexFileGetMethodInfoForOffset);
+    SET_DLFUNC_PTR(DexFile, ExtDexFileGetAllMethodInfos);
+    SET_DLFUNC_PTR(DexFile, ExtDexFileFree);
+
+#undef SET_DLFUNC_PTR
+  });
+#endif  // !defined(NO_DEXFILE_SUPPORT) && !defined(STATIC_LIB)
+}
+
+DexFile::~DexFile() { g_ExtDexFileFree(ext_dex_file_); }
 
 MethodInfo DexFile::AbsorbMethodInfo(const ExtDexFileMethodInfo& ext_method_info) {
   return {ext_method_info.offset, ext_method_info.len, DexString(ext_method_info.name)};
diff --git a/libdexfile/external/include/art_api/dex_file_support.h b/libdexfile/external/include/art_api/dex_file_support.h
index 24222af..a98ff0e 100644
--- a/libdexfile/external/include/art_api/dex_file_support.h
+++ b/libdexfile/external/include/art_api/dex_file_support.h
@@ -33,17 +33,22 @@
 namespace art_api {
 namespace dex {
 
+// Loads the libdexfile_external.so library and sets up function pointers.
+// Aborts with a fatal error on any error. For internal use by the classes
+// 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_ = ExtDexFileMakeString("", 0);
+    dex_str.ext_string_ = MakeExtDexFileString("", 0);
   }
   explicit DexString(const char* str = "")
-      : ext_string_(ExtDexFileMakeString(str, std::strlen(str))) {}
+      : ext_string_(MakeExtDexFileString(str, std::strlen(str))) {}
   explicit DexString(std::string_view str)
-      : ext_string_(ExtDexFileMakeString(str.data(), str.size())) {}
-  ~DexString() { ExtDexFileFreeString(ext_string_); }
+      : 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_);
@@ -52,36 +57,48 @@
 
   const char* data() const {
     size_t ignored;
-    return ExtDexFileGetString(ext_string_, &ignored);
+    return g_ExtDexFileGetString(ext_string_, &ignored);
   }
   const char* c_str() const { return data(); }
 
   size_t size() const {
     size_t len;
-    (void)ExtDexFileGetString(ext_string_, &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 = ExtDexFileGetString(ext_string_, &len);
+    const char* chars = g_ExtDexFileGetString(ext_string_, &len);
     return std::string_view(chars, len);
   }
 
  private:
+  friend void LoadLibdexfileExternal();
   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.
 
+  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 = ExtDexFileGetString(s1.ext_string_, &l1);
-  const char* str2 = ExtDexFileGetString(s2.ext_string_, &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);
 }
@@ -120,9 +137,14 @@
                                                  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 (ExtDexFileOpenFromMemory(addr, size, location.c_str(), &ext_error_msg, &ext_dex_file)) {
+    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));
@@ -138,9 +160,14 @@
                                              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 (ExtDexFileOpenFromFd(fd, offset, location.c_str(), &ext_error_msg, &ext_dex_file)) {
+    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));
@@ -154,10 +181,10 @@
   // class and method name only.
   MethodInfo GetMethodInfoForOffset(int64_t dex_offset, bool with_signature) {
     ExtDexFileMethodInfo ext_method_info;
-    if (ExtDexFileGetMethodInfoForOffset(ext_dex_file_,
-                                         dex_offset,
-                                         with_signature,
-                                         &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()};
@@ -168,14 +195,15 @@
   // gets the class and method name only.
   std::vector<MethodInfo> GetAllMethodInfos(bool with_signature) {
     MethodInfoVector res;
-    ExtDexFileGetAllMethodInfos(ext_dex_file_,
-                                with_signature,
-                                AddMethodInfoCallback,
-                                static_cast<void*>(&res));
+    g_ExtDexFileGetAllMethodInfos(ext_dex_file_,
+                                  with_signature,
+                                  AddMethodInfoCallback,
+                                  static_cast<void*>(&res));
     return res;
   }
 
  private:
+  friend void LoadLibdexfileExternal();
   explicit DexFile(ExtDexFile* ext_dex_file) : ext_dex_file_(ext_dex_file) {}
   ExtDexFile* ext_dex_file_;  // Owned instance. nullptr only in moved-from zombies.
 
@@ -184,6 +212,12 @@
   static MethodInfo AbsorbMethodInfo(const ExtDexFileMethodInfo& ext_method_info);
   static void AddMethodInfoCallback(const ExtDexFileMethodInfo* ext_method_info, void* user_data);
 
+  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;
+
   DISALLOW_COPY_AND_ASSIGN(DexFile);
 };
 
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index b1be646..c3abe6c 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -221,7 +221,7 @@
     debug::DebugInfo debug_info{};
     debug_info.compiled_methods = ArrayRef<const debug::MethodDebugInfo>(method_debug_infos_);
 
-    debug::WriteDebugInfo(builder_.get(), debug_info, /* write_oat_patches= */ true);
+    debug::WriteDebugInfo(builder_.get(), debug_info);
 
     builder_->End();
 
diff --git a/openjdkjvmti/jvmti_weak_table-inl.h b/openjdkjvmti/jvmti_weak_table-inl.h
index d9b8a84..5b28e45 100644
--- a/openjdkjvmti/jvmti_weak_table-inl.h
+++ b/openjdkjvmti/jvmti_weak_table-inl.h
@@ -77,7 +77,7 @@
 }
 
 template <typename T>
-bool JvmtiWeakTable<T>::GetTagSlowPath(art::Thread* self, art::mirror::Object* obj, T* result) {
+bool JvmtiWeakTable<T>::GetTagSlowPath(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, T* result) {
   // Under concurrent GC, there is a window between moving objects and sweeping of system
   // weaks in which mutators are active. We may receive a to-space object pointer in obj,
   // but still have from-space pointers in the table. Explicitly update the table once.
@@ -87,7 +87,7 @@
 }
 
 template <typename T>
-bool JvmtiWeakTable<T>::Remove(art::mirror::Object* obj, /* out */ T* tag) {
+bool JvmtiWeakTable<T>::Remove(art::ObjPtr<art::mirror::Object> obj, /* out */ T* tag) {
   art::Thread* self = art::Thread::Current();
   art::MutexLock mu(self, allow_disallow_lock_);
   Wait(self);
@@ -95,7 +95,7 @@
   return RemoveLocked(self, obj, tag);
 }
 template <typename T>
-bool JvmtiWeakTable<T>::RemoveLocked(art::mirror::Object* obj, T* tag) {
+bool JvmtiWeakTable<T>::RemoveLocked(art::ObjPtr<art::mirror::Object> obj, T* tag) {
   art::Thread* self = art::Thread::Current();
   allow_disallow_lock_.AssertHeld(self);
   Wait(self);
@@ -104,7 +104,7 @@
 }
 
 template <typename T>
-bool JvmtiWeakTable<T>::RemoveLocked(art::Thread* self, art::mirror::Object* obj, T* tag) {
+bool JvmtiWeakTable<T>::RemoveLocked(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, T* tag) {
   auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj));
   if (it != tagged_objects_.end()) {
     if (tag != nullptr) {
@@ -132,7 +132,7 @@
 }
 
 template <typename T>
-bool JvmtiWeakTable<T>::Set(art::mirror::Object* obj, T new_tag) {
+bool JvmtiWeakTable<T>::Set(art::ObjPtr<art::mirror::Object> obj, T new_tag) {
   art::Thread* self = art::Thread::Current();
   art::MutexLock mu(self, allow_disallow_lock_);
   Wait(self);
@@ -140,7 +140,7 @@
   return SetLocked(self, obj, new_tag);
 }
 template <typename T>
-bool JvmtiWeakTable<T>::SetLocked(art::mirror::Object* obj, T new_tag) {
+bool JvmtiWeakTable<T>::SetLocked(art::ObjPtr<art::mirror::Object> obj, T new_tag) {
   art::Thread* self = art::Thread::Current();
   allow_disallow_lock_.AssertHeld(self);
   Wait(self);
@@ -149,7 +149,7 @@
 }
 
 template <typename T>
-bool JvmtiWeakTable<T>::SetLocked(art::Thread* self, art::mirror::Object* obj, T new_tag) {
+bool JvmtiWeakTable<T>::SetLocked(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, T new_tag) {
   auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj));
   if (it != tagged_objects_.end()) {
     it->second = new_tag;
@@ -362,7 +362,7 @@
     }
 
     if (select) {
-      art::mirror::Object* obj = pair.first.template Read<art::kWithReadBarrier>();
+      art::ObjPtr<art::mirror::Object> obj = pair.first.template Read<art::kWithReadBarrier>();
       if (obj != nullptr) {
         count++;
         if (object_result_ptr != nullptr) {
@@ -386,14 +386,14 @@
 }
 
 template <typename T>
-art::mirror::Object* JvmtiWeakTable<T>::Find(T tag) {
+art::ObjPtr<art::mirror::Object> JvmtiWeakTable<T>::Find(T tag) {
   art::Thread* self = art::Thread::Current();
   art::MutexLock mu(self, allow_disallow_lock_);
   Wait(self);
 
   for (auto& pair : tagged_objects_) {
     if (tag == pair.second) {
-      art::mirror::Object* obj = pair.first.template Read<art::kWithReadBarrier>();
+      art::ObjPtr<art::mirror::Object> obj = pair.first.template Read<art::kWithReadBarrier>();
       if (obj != nullptr) {
         return obj;
       }
diff --git a/openjdkjvmti/jvmti_weak_table.h b/openjdkjvmti/jvmti_weak_table.h
index cba8ef0..ea0d023 100644
--- a/openjdkjvmti/jvmti_weak_table.h
+++ b/openjdkjvmti/jvmti_weak_table.h
@@ -60,25 +60,25 @@
 
   // Remove the mapping for the given object, returning whether such a mapping existed (and the old
   // value).
-  ALWAYS_INLINE bool Remove(art::mirror::Object* obj, /* out */ T* tag)
+  ALWAYS_INLINE bool Remove(art::ObjPtr<art::mirror::Object> obj, /* out */ T* tag)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(!allow_disallow_lock_);
-  ALWAYS_INLINE bool RemoveLocked(art::mirror::Object* obj, /* out */ T* tag)
+  ALWAYS_INLINE bool RemoveLocked(art::ObjPtr<art::mirror::Object> obj, /* out */ T* tag)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(allow_disallow_lock_);
 
   // Set the mapping for the given object. Returns true if this overwrites an already existing
   // mapping.
-  ALWAYS_INLINE virtual bool Set(art::mirror::Object* obj, T tag)
+  ALWAYS_INLINE virtual bool Set(art::ObjPtr<art::mirror::Object> obj, T tag)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(!allow_disallow_lock_);
-  ALWAYS_INLINE virtual bool SetLocked(art::mirror::Object* obj, T tag)
+  ALWAYS_INLINE virtual bool SetLocked(art::ObjPtr<art::mirror::Object> obj, T tag)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(allow_disallow_lock_);
 
   // Return the value associated with the given object. Returns true if the mapping exists, false
   // otherwise.
-  bool GetTag(art::mirror::Object* obj, /* out */ T* result)
+  bool GetTag(art::ObjPtr<art::mirror::Object> obj, /* out */ T* result)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(!allow_disallow_lock_) {
     art::Thread* self = art::Thread::Current();
@@ -87,7 +87,7 @@
 
     return GetTagLocked(self, obj, result);
   }
-  bool GetTagLocked(art::mirror::Object* obj, /* out */ T* result)
+  bool GetTagLocked(art::ObjPtr<art::mirror::Object> obj, /* out */ T* result)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(allow_disallow_lock_) {
     art::Thread* self = art::Thread::Current();
@@ -118,7 +118,7 @@
   ALWAYS_INLINE void Unlock() RELEASE(allow_disallow_lock_);
   ALWAYS_INLINE void AssertLocked() ASSERT_CAPABILITY(allow_disallow_lock_);
 
-  ALWAYS_INLINE art::mirror::Object* Find(T tag)
+  ALWAYS_INLINE art::ObjPtr<art::mirror::Object> Find(T tag)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(!allow_disallow_lock_);
 
@@ -132,16 +132,16 @@
 
  private:
   ALWAYS_INLINE
-  bool SetLocked(art::Thread* self, art::mirror::Object* obj, T tag)
+  bool SetLocked(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, T tag)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(allow_disallow_lock_);
 
   ALWAYS_INLINE
-  bool RemoveLocked(art::Thread* self, art::mirror::Object* obj, /* out */ T* tag)
+  bool RemoveLocked(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, /* out */ T* tag)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(allow_disallow_lock_);
 
-  bool GetTagLocked(art::Thread* self, art::mirror::Object* obj, /* out */ T* result)
+  bool GetTagLocked(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, /* out */ T* result)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(allow_disallow_lock_) {
     auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj));
@@ -165,7 +165,7 @@
   // Slow-path for GetTag. We didn't find the object, but we might be storing from-pointers and
   // are asked to retrieve with a to-pointer.
   ALWAYS_INLINE
-  bool GetTagSlowPath(art::Thread* self, art::mirror::Object* obj, /* out */ T* result)
+  bool GetTagSlowPath(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, /* out */ T* result)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(allow_disallow_lock_);
 
diff --git a/openjdkjvmti/object_tagging.cc b/openjdkjvmti/object_tagging.cc
index 0a51bf2..d52933a 100644
--- a/openjdkjvmti/object_tagging.cc
+++ b/openjdkjvmti/object_tagging.cc
@@ -71,7 +71,7 @@
       jvmti_env_, art::Thread::Current(), tag);
 }
 
-bool ObjectTagTable::Set(art::mirror::Object* obj, jlong new_tag) {
+bool ObjectTagTable::Set(art::ObjPtr<art::mirror::Object> obj, jlong new_tag) {
   if (new_tag == 0) {
     jlong tmp;
     return Remove(obj, &tmp);
@@ -79,7 +79,7 @@
   return JvmtiWeakTable<jlong>::Set(obj, new_tag);
 }
 
-bool ObjectTagTable::SetLocked(art::mirror::Object* obj, jlong new_tag) {
+bool ObjectTagTable::SetLocked(art::ObjPtr<art::mirror::Object> obj, jlong new_tag) {
   if (new_tag == 0) {
     jlong tmp;
     return RemoveLocked(obj, &tmp);
diff --git a/openjdkjvmti/object_tagging.h b/openjdkjvmti/object_tagging.h
index ca05a05..bd72ce3 100644
--- a/openjdkjvmti/object_tagging.h
+++ b/openjdkjvmti/object_tagging.h
@@ -61,21 +61,21 @@
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(!allow_disallow_lock_);
 
-  bool Set(art::mirror::Object* obj, jlong tag) override
+  bool Set(art::ObjPtr<art::mirror::Object> obj, jlong tag) override
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(!allow_disallow_lock_);
-  bool SetLocked(art::mirror::Object* obj, jlong tag) override
+  bool SetLocked(art::ObjPtr<art::mirror::Object> obj, jlong tag) override
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(allow_disallow_lock_);
 
-  jlong GetTagOrZero(art::mirror::Object* obj)
+  jlong GetTagOrZero(art::ObjPtr<art::mirror::Object> obj)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(!allow_disallow_lock_) {
     jlong tmp = 0;
     GetTag(obj, &tmp);
     return tmp;
   }
-  jlong GetTagOrZeroLocked(art::mirror::Object* obj)
+  jlong GetTagOrZeroLocked(art::ObjPtr<art::mirror::Object> obj)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(allow_disallow_lock_) {
     jlong tmp = 0;
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index eb4bada..e51ae4d 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -240,7 +240,7 @@
   art::ScopedObjectAccess soa(self);
   art::StackHandleScope<1> hs(self);
   art::ObjPtr<art::mirror::Object> obj(self->DecodeJObject(klass));
-  if (obj.IsNull()) {
+  if (obj.IsNull() || !obj->IsClass()) {
     return ERR(INVALID_CLASS);
   }
   art::Handle<art::mirror::Class> h_klass(hs.NewHandle(obj->AsClass()));
@@ -256,7 +256,7 @@
   art::ScopedObjectAccess soa(self);
   art::StackHandleScope<1> hs(self);
   art::ObjPtr<art::mirror::Object> obj(self->DecodeJObject(klass));
-  if (obj.IsNull()) {
+  if (obj.IsNull() || !obj->IsClass()) {
     return ERR(INVALID_CLASS);
   }
   art::Handle<art::mirror::Class> h_klass(hs.NewHandle(obj->AsClass()));
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index a4425ce..150fa78 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -44,6 +44,7 @@
 
 #include "arch/instruction_set.h"
 #include "base/aborting.h"
+#include "base/bit_utils.h"
 #include "base/file_utils.h"
 #include "base/memory_tool.h"
 #include "base/mutex.h"
@@ -51,6 +52,7 @@
 #include "base/unix_file/fd_file.h"
 #include "base/utils.h"
 #include "class_linker.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
 #include "oat_quick_method_header.h"
 #include "runtime.h"
 #include "thread-current-inl.h"
@@ -299,6 +301,11 @@
       class_linker->IsQuickToInterpreterBridge(entry_point)) {
     return false;
   }
+  // The backtrace library might have heuristically subracted instruction
+  // size from the pc, to pretend the pc is at the calling instruction.
+  if (reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) - pc <= 4) {
+    return false;
+  }
   uintptr_t code = reinterpret_cast<uintptr_t>(EntryPointToCodePointer(entry_point));
   uintptr_t code_size = reinterpret_cast<const OatQuickMethodHeader*>(code)[-1].GetCodeSize();
   return code <= pc && pc <= (code + code_size);
diff --git a/runtime/oat.h b/runtime/oat.h
index 88238d9..b824729 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -31,8 +31,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  // Last oat version changed reason: Partial boot image.
-  static constexpr uint8_t kOatVersion[] = { '1', '6', '6', '\0' };
+  // Last oat version changed reason: Add code size to CodeInfo.
+  static constexpr uint8_t kOatVersion[] = { '1', '6', '7', '\0' };
 
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDebuggableKey = "debuggable";
diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h
index 8798c69..6c123c4 100644
--- a/runtime/oat_quick_method_header.h
+++ b/runtime/oat_quick_method_header.h
@@ -35,6 +35,8 @@
                        uint32_t code_size)
       : vmap_table_offset_(vmap_table_offset),
         code_size_(code_size) {
+    DCHECK_NE(vmap_table_offset, 0u);
+    DCHECK_NE(code_size, 0u);
   }
 
   static OatQuickMethodHeader* FromCodePointer(const void* code_ptr) {
@@ -58,7 +60,7 @@
   }
 
   bool IsOptimized() const {
-    return GetCodeSize() != 0 && vmap_table_offset_ != 0;
+    return (code_size_ & kCodeSizeMask) != 0 && vmap_table_offset_ != 0;
   }
 
   const uint8_t* GetOptimizedCodeInfoPtr() const {
@@ -76,7 +78,11 @@
   }
 
   uint32_t GetCodeSize() const {
-    return code_size_ & kCodeSizeMask;
+    DCHECK(IsOptimized());
+    size_t code_size1 = code_size_ & kCodeSizeMask;
+    size_t code_size2 = CodeInfo::DecodeCodeSize(GetOptimizedCodeInfoPtr());
+    DCHECK_EQ(code_size1, code_size2);
+    return code_size2;
   }
 
   const uint32_t* GetCodeSizeAddr() const {
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index 62dec15..5d30b77 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -227,6 +227,7 @@
                     bool verbose,
                     InstructionSet instruction_set) const {
   vios->Stream() << "CodeInfo BitSize=" << size_in_bits_
+    << " CodeSize:" << StackMap::UnpackNativePc(packed_code_size_, instruction_set)
     << " FrameSize:" << packed_frame_size_ * kStackAlignment
     << " CoreSpillMask:" << std::hex << core_spill_mask_
     << " FpSpillMask:" << std::hex << fp_spill_mask_
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 87133cf..59da923 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -438,8 +438,15 @@
   // Accumulate code info size statistics into the given Stats tree.
   static void CollectSizeStats(const uint8_t* code_info, /*out*/ Stats* parent);
 
+  ALWAYS_INLINE static size_t DecodeCodeSize(const uint8_t* data,
+                                             InstructionSet isa = kRuntimeISA) {
+    uint32_t packed_code_size = BitMemoryReader(data).ReadVarint();
+    return StackMap::UnpackNativePc(packed_code_size, isa);
+  }
+
   ALWAYS_INLINE static QuickMethodFrameInfo DecodeFrameInfo(const uint8_t* data) {
     BitMemoryReader reader(data);
+    reader.ReadVarint();  // Skip code size.
     return QuickMethodFrameInfo(
         reader.ReadVarint() * kStackAlignment,  // Decode packed_frame_size_ and unpack.
         reader.ReadVarint(),  // core_spill_mask_.
@@ -461,6 +468,7 @@
   // Invokes the callback with member pointer of each header field.
   template<typename Callback>
   ALWAYS_INLINE static void ForEachHeaderField(Callback callback) {
+    callback(&CodeInfo::packed_code_size_);
     callback(&CodeInfo::packed_frame_size_);
     callback(&CodeInfo::core_spill_mask_);
     callback(&CodeInfo::fp_spill_mask_);
@@ -486,6 +494,7 @@
     callback(&CodeInfo::dex_register_catalog_);
   }
 
+  uint32_t packed_code_size_ = 0;  // The size of native PC range.
   uint32_t packed_frame_size_ = 0;  // Frame size in kStackAlignment units.
   uint32_t core_spill_mask_ = 0;
   uint32_t fp_spill_mask_ = 0;
diff --git a/test/905-object-free/expected.txt b/test/905-object-free/expected.txt
index c226df7..dfcd7b6 100644
--- a/test/905-object-free/expected.txt
+++ b/test/905-object-free/expected.txt
@@ -10,4 +10,4 @@
 ---
 []
 ---
-Free counts 100000 100000
+Free counts 200000 200000
diff --git a/test/905-object-free/src/art/Test905.java b/test/905-object-free/src/art/Test905.java
index dddd1aa..367da99 100644
--- a/test/905-object-free/src/art/Test905.java
+++ b/test/905-object-free/src/art/Test905.java
@@ -20,6 +20,7 @@
 import java.lang.ref.ReferenceQueue;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.function.BiConsumer;
 
 public class Test905 {
   // Taken from jdwp tests.
@@ -110,26 +111,59 @@
     System.out.println("---");
   }
 
-  private static void stressAllocate(int i) {
+  private static void stressAllocate(int i, BiConsumer<Integer, Object> saver) {
     Object obj = new Object();
     Main.setTag(obj, i);
     setTag2(obj, i + 1);
+    saver.accept(i, obj);
   }
 
   private static void stress() {
     getCollectedTags(0);
     getCollectedTags(1);
-    // Allocate objects.
-    for (int i = 1; i <= 100000; ++i) {
-      stressAllocate(i);
+    final int num_obj = 400000;
+    final Object[] saved = new Object[num_obj/2];
+    // Allocate objects, Save every other one. We want to be sure that it's only the deleted objects
+    // that get their tags cleared and non-deleted objects correctly keep track of their tags.
+    for (int i = 1; i <= num_obj; ++i) {
+      stressAllocate(i, (idx, obj) -> {
+        if ((idx.intValue() - 1) % 2 == 0) {
+          saved[(idx.intValue() - 1)/2] = obj;
+        }
+      });
     }
     gcAndWait();
     long[] freedTags1 = getCollectedTags(0);
     long[] freedTags2 = getCollectedTags(1);
+    // Sort the freedtags
+    Arrays.sort(freedTags1);
+    Arrays.sort(freedTags2);
+    // Make sure we freed all the ones we expect to and both envs agree on this.
     System.out.println("Free counts " + freedTags1.length + " " + freedTags2.length);
     for (int i = 0; i < freedTags1.length; ++i) {
       if (freedTags1[i] + 1 != freedTags2[i]) {
-        System.out.println("Mismatched tags " + freedTags1[i] + " " + freedTags2[i]);
+        System.out.println("Mismatched tags " + (freedTags1[i] + 1) + " " + freedTags2[i]);
+      }
+    }
+    // Make sure the saved-tags aren't present.
+    for (int i = 0; i < saved.length; i++) {
+      // index = (tag - 1)/2 --> (index * 2) + 1 = tag
+      long expectedTag1 = (i * 2) + 1;
+      if (Main.getTag(saved[i]) != expectedTag1) {
+        System.out.println("Saved object has unexpected tag in env 1. Expected "
+                           + expectedTag1 + " got " + Main.getTag(saved[i]));
+      }
+      if (getTag2(saved[i]) != 1 + expectedTag1) {
+        System.out.println("Saved object has unexpected tag in env 2. Expected "
+                           + (expectedTag1 + 1) + " got " + getTag2(saved[i]));
+      }
+      if (Arrays.binarySearch(freedTags1, expectedTag1) >= 0) {
+        System.out.println("Saved object was marked as deleted in env 1. Object was "
+                           + expectedTag1);
+      }
+      if (Arrays.binarySearch(freedTags2, expectedTag1 + 1) >= 0) {
+        System.out.println("Saved object was marked as deleted in env 2. Object was "
+                           + (expectedTag1 + 1));
       }
     }
   }
@@ -161,4 +195,5 @@
   private static native void enableFreeTracking(boolean enable);
   private static native long[] getCollectedTags(int index);
   private static native void setTag2(Object o, long tag);
+  private static native long getTag2(Object o);
 }
diff --git a/test/905-object-free/tracking_free.cc b/test/905-object-free/tracking_free.cc
index bf86c9a..d85d9d3 100644
--- a/test/905-object-free/tracking_free.cc
+++ b/test/905-object-free/tracking_free.cc
@@ -18,6 +18,7 @@
 
 #include <cstdio>
 #include <iostream>
+#include <mutex>
 #include <vector>
 
 #include "android-base/logging.h"
@@ -33,17 +34,23 @@
 namespace art {
 namespace Test905ObjectFree {
 
+// The ObjectFree functions aren't required to be called on any particular thread so use these
+// mutexs to control access to the collected_tags lists.
+std::mutex ct1_mutex;
 static std::vector<jlong> collected_tags1;
+std::mutex ct2_mutex;
 static std::vector<jlong> collected_tags2;
 
 jvmtiEnv* jvmti_env2;
 
 static void JNICALL ObjectFree1(jvmtiEnv* ti_env, jlong tag) {
+  std::lock_guard<std::mutex> mu(ct1_mutex);
   CHECK_EQ(ti_env, jvmti_env);
   collected_tags1.push_back(tag);
 }
 
 static void JNICALL ObjectFree2(jvmtiEnv* ti_env, jlong tag) {
+  std::lock_guard<std::mutex> mu(ct2_mutex);
   CHECK_EQ(ti_env, jvmti_env2);
   collected_tags2.push_back(tag);
 }
@@ -84,6 +91,7 @@
 
 extern "C" JNIEXPORT jlongArray JNICALL Java_art_Test905_getCollectedTags(
     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint index) {
+  std::lock_guard<std::mutex> mu((index == 0) ? ct1_mutex : ct2_mutex);
   std::vector<jlong>& tags = (index == 0) ? collected_tags1 : collected_tags2;
   jlongArray ret = env->NewLongArray(tags.size());
   if (ret == nullptr) {
@@ -96,6 +104,14 @@
   return ret;
 }
 
+extern "C" JNIEXPORT jlong JNICALL Java_art_Test905_getTag2(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject obj) {
+  jlong tag;
+  jvmtiError ret = jvmti_env2->GetTag(obj, &tag);
+  JvmtiErrorToException(env, jvmti_env, ret);
+  return tag;
+}
+
 extern "C" JNIEXPORT void JNICALL Java_art_Test905_setTag2(
     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject obj, jlong tag) {
   jvmtiError ret = jvmti_env2->SetTag(obj, tag);
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 5177919..9b0873e 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -234,12 +234,5 @@
     "libcore.libcore.icu.TimeZoneIntegrationTest#testTimeZoneDebugInfo",
     "libcore.libcore.icu.TimeZoneIntegrationTest#testTzDataSetVersions"
   ]
-},
-{
-  description: "Expected networking failure on host / old systems: we expect 97 (EAFNOSUPPORT), but we get 22 (EINVAL)",
-  result: EXEC_FAILED,
-  names: [
-    "libcore.libcore.io.OsTest#testCrossFamilyBindConnectSendto"
-  ]
 }
 ]