Add support for Dex version 37 in Runtime.

We are skipping version 36 of the dex file format due to a bug in
Dalvik dating back to ICS where dex files marked version 036 would
erroneously be accepted.

Bug: 27538761
Bug: 27809626

Change-Id: Ic053f7e25f5a8c3df83ff34b6656528824b2df12
diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h
index 2958dc6..fb37804 100644
--- a/compiler/utils/test_dex_file_builder.h
+++ b/compiler/utils/test_dex_file_builder.h
@@ -88,7 +88,7 @@
     std::memset(header_data.data, 0, sizeof(header_data.data));
     DexFile::Header* header = reinterpret_cast<DexFile::Header*>(&header_data.data);
     std::copy_n(DexFile::kDexMagic, 4u, header->magic_);
-    std::copy_n(DexFile::kDexMagicVersion, 4u, header->magic_ + 4u);
+    std::copy_n(DexFile::kDexMagicVersions[0], 4u, header->magic_ + 4u);
     header->header_size_ = sizeof(DexFile::Header);
     header->endian_tag_ = DexFile::kDexEndianConstant;
     header->link_size_ = 0u;  // Unused.
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 4a0a6fc..60caa73 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -62,7 +62,12 @@
 namespace art {
 
 const uint8_t DexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' };
-const uint8_t DexFile::kDexMagicVersion[] = { '0', '3', '5', '\0' };
+const uint8_t DexFile::kDexMagicVersions[DexFile::kNumDexVersions][DexFile::kDexVersionLen] = {
+  {'0', '3', '5', '\0'},
+  // Dex version 036 skipped because of an old dalvik bug on some versions of android where dex
+  // files with that version number would erroneously be accepted and run.
+  {'0', '3', '7', '\0'}
+};
 
 bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) {
   CHECK(checksum != nullptr);
@@ -493,7 +498,12 @@
 
 bool DexFile::IsVersionValid(const uint8_t* magic) {
   const uint8_t* version = &magic[sizeof(kDexMagic)];
-  return (memcmp(version, kDexMagicVersion, sizeof(kDexMagicVersion)) == 0);
+  for (uint32_t i = 0; i < kNumDexVersions; i++) {
+    if (memcmp(version, kDexMagicVersions[i], kDexVersionLen) == 0) {
+      return true;
+    }
+  }
+  return false;
 }
 
 uint32_t DexFile::GetVersion() const {
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index e497e9c..6849984 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -58,7 +58,10 @@
 class DexFile {
  public:
   static const uint8_t kDexMagic[];
-  static const uint8_t kDexMagicVersion[];
+  static constexpr size_t kNumDexVersions = 2;
+  static constexpr size_t kDexVersionLen = 4;
+  static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen];
+
   static constexpr size_t kSha1DigestSize = 20;
   static constexpr uint32_t kDexEndianConstant = 0x12345678;
 
@@ -71,7 +74,7 @@
   // The value of an invalid index.
   static const uint16_t kDexNoIndex16 = 0xFFFF;
 
-  // The separator charactor in MultiDex locations.
+  // The separator character in MultiDex locations.
   static constexpr char kMultiDexSeparator = ':';
 
   // A string version of the previous. This is a define so that we can merge string literals in the
diff --git a/test/370-dex-v37/build b/test/370-dex-v37/build
new file mode 100755
index 0000000..f472428
--- /dev/null
+++ b/test/370-dex-v37/build
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+./default-build "$@"
+
+if [[ $@ != *"--jvm"* ]]; then
+  # Change the generated dex file to have a v36 magic number if it is version 35
+  if test -f classes.dex && head -c 7 classes.dex | grep -q 035; then
+    # place ascii value '037' into the classes.dex file starting at byte 4.
+    printf '037' | dd status=none conv=notrunc of=classes.dex bs=1 seek=4 count=3
+    rm -f $TEST_NAME.jar
+    zip $TEST_NAME.jar classes.dex
+  fi
+fi
diff --git a/test/370-dex-v37/expected.txt b/test/370-dex-v37/expected.txt
new file mode 100644
index 0000000..af5626b
--- /dev/null
+++ b/test/370-dex-v37/expected.txt
@@ -0,0 +1 @@
+Hello, world!
diff --git a/test/370-dex-v37/info.txt b/test/370-dex-v37/info.txt
new file mode 100644
index 0000000..5ca9c76
--- /dev/null
+++ b/test/370-dex-v37/info.txt
@@ -0,0 +1 @@
+Print "Hello, World!" with a version 37 dex file.
diff --git a/test/370-dex-v37/src/Main.java b/test/370-dex-v37/src/Main.java
new file mode 100644
index 0000000..1ef6289
--- /dev/null
+++ b/test/370-dex-v37/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    System.out.println("Hello, world!");
+  }
+}