Compare the oat version of runtime, dex2oat and the compiler.

Ensure they match, otherwise we might get confusing crashes.

Bug: 125318570
Test: running ahat-test-dump after oat version bump triggers the check.
Change-Id: If4bc832af50c2cb851054be03c9887f9a3d4d04b
diff --git a/compiler/compiler.cc b/compiler/compiler.cc
index d044c2f..98d7339 100644
--- a/compiler/compiler.cc
+++ b/compiler/compiler.cc
@@ -22,6 +22,7 @@
 #include "base/utils.h"
 #include "dex/code_item_accessors-inl.h"
 #include "dex/dex_file.h"
+#include "oat.h"
 #include "optimizing/optimizing_compiler.h"
 
 namespace art {
@@ -29,6 +30,9 @@
 Compiler* Compiler::Create(const CompilerOptions& compiler_options,
                            CompiledMethodStorage* storage,
                            Compiler::Kind kind) {
+  // Check that oat version when runtime was compiled matches the oat version of the compiler.
+  constexpr std::array<uint8_t, 4> compiler_oat_version = OatHeader::kOatVersion;
+  OatHeader::CheckOatVersion(compiler_oat_version);
   switch (kind) {
     case kQuick:
       // TODO: Remove Quick in options.
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 897278f..c392177 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -2011,6 +2011,10 @@
 size_t OatWriter::InitOatHeader(uint32_t num_dex_files,
                                 SafeMap<std::string, std::string>* key_value_store) {
   TimingLogger::ScopedTiming split("InitOatHeader", timings_);
+  // Check that oat version when runtime was compiled matches the oat version
+  // when dex2oat was compiled. We have seen cases where they got out of sync.
+  constexpr std::array<uint8_t, 4> dex2oat_oat_version = OatHeader::kOatVersion;
+  OatHeader::CheckOatVersion(dex2oat_oat_version);
   oat_header_.reset(OatHeader::Create(GetCompilerOptions().GetInstructionSet(),
                                       GetCompilerOptions().GetInstructionSetFeatures(),
                                       num_dex_files,
diff --git a/runtime/oat.cc b/runtime/oat.cc
index b828987..db6cda5 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -29,8 +29,6 @@
 
 using android::base::StringPrintf;
 
-constexpr uint8_t OatHeader::kOatMagic[4];
-constexpr uint8_t OatHeader::kOatVersion[4];
 constexpr const char OatHeader::kTrueValue[];
 constexpr const char OatHeader::kFalseValue[];
 
@@ -86,8 +84,8 @@
   static_assert(sizeof(version_) == sizeof(kOatVersion),
                 "Oat version and version_ have different lengths.");
 
-  memcpy(magic_, kOatMagic, sizeof(kOatMagic));
-  memcpy(version_, kOatVersion, sizeof(kOatVersion));
+  magic_ = kOatMagic;
+  version_ = kOatVersion;
 
   CHECK_NE(instruction_set, InstructionSet::kNone);
 
@@ -96,10 +94,10 @@
 }
 
 bool OatHeader::IsValid() const {
-  if (memcmp(magic_, kOatMagic, sizeof(kOatMagic)) != 0) {
+  if (magic_ != kOatMagic) {
     return false;
   }
-  if (memcmp(version_, kOatVersion, sizeof(kOatVersion)) != 0) {
+  if (version_ != kOatVersion) {
     return false;
   }
   if (!IsAligned<kPageSize>(executable_offset_)) {
@@ -112,13 +110,13 @@
 }
 
 std::string OatHeader::GetValidationErrorMessage() const {
-  if (memcmp(magic_, kOatMagic, sizeof(kOatMagic)) != 0) {
+  if (magic_ != kOatMagic) {
     static_assert(sizeof(kOatMagic) == 4, "kOatMagic has unexpected length");
     return StringPrintf("Invalid oat magic, expected 0x%x%x%x%x, got 0x%x%x%x%x.",
                         kOatMagic[0], kOatMagic[1], kOatMagic[2], kOatMagic[3],
                         magic_[0], magic_[1], magic_[2], magic_[3]);
   }
-  if (memcmp(version_, kOatVersion, sizeof(kOatVersion)) != 0) {
+  if (version_ != kOatVersion) {
     static_assert(sizeof(kOatVersion) == 4, "kOatVersion has unexpected length");
     return StringPrintf("Invalid oat version, expected 0x%x%x%x%x, got 0x%x%x%x%x.",
                         kOatVersion[0], kOatVersion[1], kOatVersion[2], kOatVersion[3],
@@ -133,9 +131,20 @@
   return "";
 }
 
+// Do not move this into the header.  The method must be compiled in the runtime library,
+// so that we can check that the compile-time oat version matches the version in the caller.
+void OatHeader::CheckOatVersion(std::array<uint8_t, 4> version) {
+  constexpr std::array<uint8_t, 4> expected = kOatVersion;  // Runtime oat version.
+  if (version != kOatVersion) {
+    LOG(FATAL) << StringPrintf("Invalid oat version, expected 0x%x%x%x%x, got 0x%x%x%x%x.",
+                               expected[0], expected[1], expected[2], expected[3],
+                               version[0], version[1], version[2], version[3]);
+  }
+}
+
 const char* OatHeader::GetMagic() const {
   CHECK(IsValid());
-  return reinterpret_cast<const char*>(magic_);
+  return reinterpret_cast<const char*>(magic_.data());
 }
 
 uint32_t OatHeader::GetChecksum() const {
diff --git a/runtime/oat.h b/runtime/oat.h
index 3877f3e..15059a8 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -17,6 +17,7 @@
 #ifndef ART_RUNTIME_OAT_H_
 #define ART_RUNTIME_OAT_H_
 
+#include <array>
 #include <vector>
 
 #include "base/macros.h"
@@ -30,9 +31,9 @@
 
 class PACKED(4) OatHeader {
  public:
-  static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
+  static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
   // Last oat version changed reason: Remove unused trampoline entrypoints.
-  static constexpr uint8_t kOatVersion[] = { '1', '7', '0', '\0' };
+  static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '7', '0', '\0' } };
 
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDebuggableKey = "debuggable";
@@ -55,6 +56,7 @@
 
   bool IsValid() const;
   std::string GetValidationErrorMessage() const;
+  static void CheckOatVersion(std::array<uint8_t, 4> version);
   const char* GetMagic() const;
   uint32_t GetChecksum() const;
   void SetChecksum(uint32_t checksum);
@@ -111,8 +113,8 @@
 
   void Flatten(const SafeMap<std::string, std::string>* variable_data);
 
-  uint8_t magic_[4];
-  uint8_t version_[4];
+  std::array<uint8_t, 4> magic_;
+  std::array<uint8_t, 4> version_;
   uint32_t oat_checksum_;
 
   InstructionSet instruction_set_;