ART: Show invalid oat header cause in error message

Include the IsValid failure cause in the error message of opening
an oat file.

Make oat magic and version constexpr so we can have static_asserts
over the lengths.

Bug: 17187621
Change-Id: I8cef1c6cc9cb2faa052167a3437e81b4c2cbefa7
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 9307598..c223e2e 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -20,12 +20,13 @@
 #include <zlib.h>
 
 #include "arch/instruction_set_features.h"
+#include "base/stringprintf.h"
 #include "utils.h"
 
 namespace art {
 
-const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '5', '2', '\0' };
+constexpr uint8_t OatHeader::kOatMagic[4];
+constexpr uint8_t OatHeader::kOatVersion[4];
 
 static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
   size_t estimate = 0U;
@@ -67,6 +68,13 @@
                      uint32_t image_file_location_oat_checksum,
                      uint32_t image_file_location_oat_data_begin,
                      const SafeMap<std::string, std::string>* variable_data) {
+  // Don't want asserts in header as they would be checked in each file that includes it. But the
+  // fields are private, so we check inside a method.
+  static_assert(sizeof(magic_) == sizeof(kOatMagic),
+                "Oat magic and magic_ have different lengths.");
+  static_assert(sizeof(version_) == sizeof(kOatVersion),
+                "Oat version and version_ have different lengths.");
+
   memcpy(magic_, kOatMagic, sizeof(kOatMagic));
   memcpy(version_, kOatVersion, sizeof(kOatVersion));
   executable_offset_ = 0;
@@ -127,6 +135,28 @@
   return true;
 }
 
+std::string OatHeader::GetValidationErrorMessage() const {
+  if (memcmp(magic_, kOatMagic, sizeof(kOatMagic)) != 0) {
+    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) {
+    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],
+                        version_[0], version_[1], version_[2], version_[3]);
+  }
+  if (!IsAligned<kPageSize>(executable_offset_)) {
+    return "Executable offset not page-aligned.";
+  }
+  if (!IsAligned<kPageSize>(image_patch_delta_)) {
+    return "Image patch delta not page-aligned.";
+  }
+  return "";
+}
+
 const char* OatHeader::GetMagic() const {
   CHECK(IsValid());
   return reinterpret_cast<const char*>(magic_);
diff --git a/runtime/oat.h b/runtime/oat.h
index 6098fbd..f218482 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -31,8 +31,8 @@
 
 class PACKED(4) OatHeader {
  public:
-  static const uint8_t kOatMagic[4];
-  static const uint8_t kOatVersion[4];
+  static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
+  static constexpr uint8_t kOatVersion[] = { '0', '4', '5', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
@@ -47,6 +47,7 @@
                            const SafeMap<std::string, std::string>* variable_data);
 
   bool IsValid() const;
+  std::string GetValidationErrorMessage() const;
   const char* GetMagic() const;
   uint32_t GetChecksum() const;
   void UpdateChecksum(const void* data, size_t length);
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index aa85ff0..1c6cc8b 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -227,7 +227,9 @@
 
 bool OatFile::Setup(std::string* error_msg) {
   if (!GetOatHeader().IsValid()) {
-    *error_msg = StringPrintf("Invalid oat magic for '%s'", GetLocation().c_str());
+    std::string cause = GetOatHeader().GetValidationErrorMessage();
+    *error_msg = StringPrintf("Invalid oat header for '%s': %s", GetLocation().c_str(),
+                              cause.c_str());
     return false;
   }
   const uint8_t* oat = Begin();