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_;