Prepare to move ArtDexFileLoader to libdexfile

Move file_utils and friends to libartbase so that ArtDexFileLoader can
be moved to libdexfile.  This will clean up duplication and complexity
with zip file handling.

Bug: 78652467
Test: make -j 40 test-art-host-gtest
Change-Id: Ia5eac1f93caf3fa918b4b48803cbfd842035e29e
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 50abdd3..6cd1731 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -19,11 +19,13 @@
     defaults: ["art_defaults"],
     host_supported: true,
     srcs: [
+        "arch/instruction_set.cc",
         "base/allocator.cc",
         "base/arena_allocator.cc",
         "base/arena_bit_vector.cc",
         "base/bit_vector.cc",
         "base/file_magic.cc",
+        "base/file_utils.cc",
         "base/hex_dump.cc",
         "base/logging.cc",
         "base/malloc_arena_pool.cc",
@@ -81,6 +83,7 @@
     cmd: "$(location generate_operator_out) art/libartbase $(in) > $(out)",
     tools: ["generate_operator_out"],
     srcs: [
+        "arch/instruction_set.h",
         "base/allocator.h",
         "base/callee_save_type.h",
         "base/unix_file/fd_file.h",
@@ -142,12 +145,14 @@
         "art_gtest_defaults",
     ],
     srcs: [
+        "arch/instruction_set_test.cc",
         "base/arena_allocator_test.cc",
         "base/bit_field_test.cc",
         "base/bit_string_test.cc",
         "base/bit_struct_test.cc",
         "base/bit_utils_test.cc",
         "base/bit_vector_test.cc",
+        "base/file_utils_test.cc",
         "base/hash_set_test.cc",
         "base/hex_dump_test.cc",
         "base/histogram_test.cc",
diff --git a/libartbase/arch/instruction_set.cc b/libartbase/arch/instruction_set.cc
new file mode 100644
index 0000000..a187663
--- /dev/null
+++ b/libartbase/arch/instruction_set.cc
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+
+#include "instruction_set.h"
+
+#include "android-base/logging.h"
+#include "base/bit_utils.h"
+#include "base/globals.h"
+
+namespace art {
+
+void InstructionSetAbort(InstructionSet isa) {
+  switch (isa) {
+    case InstructionSet::kArm:
+    case InstructionSet::kThumb2:
+    case InstructionSet::kArm64:
+    case InstructionSet::kX86:
+    case InstructionSet::kX86_64:
+    case InstructionSet::kMips:
+    case InstructionSet::kMips64:
+    case InstructionSet::kNone:
+      LOG(FATAL) << "Unsupported instruction set " << isa;
+      UNREACHABLE();
+  }
+  LOG(FATAL) << "Unknown ISA " << isa;
+  UNREACHABLE();
+}
+
+const char* GetInstructionSetString(InstructionSet isa) {
+  switch (isa) {
+    case InstructionSet::kArm:
+    case InstructionSet::kThumb2:
+      return "arm";
+    case InstructionSet::kArm64:
+      return "arm64";
+    case InstructionSet::kX86:
+      return "x86";
+    case InstructionSet::kX86_64:
+      return "x86_64";
+    case InstructionSet::kMips:
+      return "mips";
+    case InstructionSet::kMips64:
+      return "mips64";
+    case InstructionSet::kNone:
+      return "none";
+  }
+  LOG(FATAL) << "Unknown ISA " << isa;
+  UNREACHABLE();
+}
+
+InstructionSet GetInstructionSetFromString(const char* isa_str) {
+  CHECK(isa_str != nullptr);
+
+  if (strcmp("arm", isa_str) == 0) {
+    return InstructionSet::kArm;
+  } else if (strcmp("arm64", isa_str) == 0) {
+    return InstructionSet::kArm64;
+  } else if (strcmp("x86", isa_str) == 0) {
+    return InstructionSet::kX86;
+  } else if (strcmp("x86_64", isa_str) == 0) {
+    return InstructionSet::kX86_64;
+  } else if (strcmp("mips", isa_str) == 0) {
+    return InstructionSet::kMips;
+  } else if (strcmp("mips64", isa_str) == 0) {
+    return InstructionSet::kMips64;
+  }
+
+  return InstructionSet::kNone;
+}
+
+size_t GetInstructionSetAlignment(InstructionSet isa) {
+  switch (isa) {
+    case InstructionSet::kArm:
+      // Fall-through.
+    case InstructionSet::kThumb2:
+      return kArmAlignment;
+    case InstructionSet::kArm64:
+      return kArm64Alignment;
+    case InstructionSet::kX86:
+      // Fall-through.
+    case InstructionSet::kX86_64:
+      return kX86Alignment;
+    case InstructionSet::kMips:
+      // Fall-through.
+    case InstructionSet::kMips64:
+      return kMipsAlignment;
+    case InstructionSet::kNone:
+      LOG(FATAL) << "ISA kNone does not have alignment.";
+      UNREACHABLE();
+  }
+  LOG(FATAL) << "Unknown ISA " << isa;
+  UNREACHABLE();
+}
+
+#if !defined(ART_STACK_OVERFLOW_GAP_arm) || !defined(ART_STACK_OVERFLOW_GAP_arm64) || \
+    !defined(ART_STACK_OVERFLOW_GAP_mips) || !defined(ART_STACK_OVERFLOW_GAP_mips64) || \
+    !defined(ART_STACK_OVERFLOW_GAP_x86) || !defined(ART_STACK_OVERFLOW_GAP_x86_64)
+#error "Missing defines for stack overflow gap"
+#endif
+
+static constexpr size_t kArmStackOverflowReservedBytes    = ART_STACK_OVERFLOW_GAP_arm;
+static constexpr size_t kArm64StackOverflowReservedBytes  = ART_STACK_OVERFLOW_GAP_arm64;
+static constexpr size_t kMipsStackOverflowReservedBytes   = ART_STACK_OVERFLOW_GAP_mips;
+static constexpr size_t kMips64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_mips64;
+static constexpr size_t kX86StackOverflowReservedBytes    = ART_STACK_OVERFLOW_GAP_x86;
+static constexpr size_t kX86_64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_x86_64;
+
+static_assert(IsAligned<kPageSize>(kArmStackOverflowReservedBytes), "ARM gap not page aligned");
+static_assert(IsAligned<kPageSize>(kArm64StackOverflowReservedBytes), "ARM64 gap not page aligned");
+static_assert(IsAligned<kPageSize>(kMipsStackOverflowReservedBytes), "Mips gap not page aligned");
+static_assert(IsAligned<kPageSize>(kMips64StackOverflowReservedBytes),
+              "Mips64 gap not page aligned");
+static_assert(IsAligned<kPageSize>(kX86StackOverflowReservedBytes), "X86 gap not page aligned");
+static_assert(IsAligned<kPageSize>(kX86_64StackOverflowReservedBytes),
+              "X86_64 gap not page aligned");
+
+#if !defined(ART_FRAME_SIZE_LIMIT)
+#error "ART frame size limit missing"
+#endif
+
+// TODO: Should we require an extra page (RoundUp(SIZE) + kPageSize)?
+static_assert(ART_FRAME_SIZE_LIMIT < kArmStackOverflowReservedBytes, "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kArm64StackOverflowReservedBytes,
+              "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kMipsStackOverflowReservedBytes,
+              "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kMips64StackOverflowReservedBytes,
+              "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kX86StackOverflowReservedBytes,
+              "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kX86_64StackOverflowReservedBytes,
+              "Frame size limit too large");
+
+size_t GetStackOverflowReservedBytes(InstructionSet isa) {
+  switch (isa) {
+    case InstructionSet::kArm:      // Intentional fall-through.
+    case InstructionSet::kThumb2:
+      return kArmStackOverflowReservedBytes;
+
+    case InstructionSet::kArm64:
+      return kArm64StackOverflowReservedBytes;
+
+    case InstructionSet::kMips:
+      return kMipsStackOverflowReservedBytes;
+
+    case InstructionSet::kMips64:
+      return kMips64StackOverflowReservedBytes;
+
+    case InstructionSet::kX86:
+      return kX86StackOverflowReservedBytes;
+
+    case InstructionSet::kX86_64:
+      return kX86_64StackOverflowReservedBytes;
+
+    case InstructionSet::kNone:
+      LOG(FATAL) << "kNone has no stack overflow size";
+      UNREACHABLE();
+  }
+  LOG(FATAL) << "Unknown instruction set" << isa;
+  UNREACHABLE();
+}
+
+}  // namespace art
diff --git a/libartbase/arch/instruction_set.h b/libartbase/arch/instruction_set.h
new file mode 100644
index 0000000..06bd53a
--- /dev/null
+++ b/libartbase/arch/instruction_set.h
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_
+#define ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "base/enums.h"
+#include "base/macros.h"
+
+namespace art {
+
+enum class InstructionSet {
+  kNone,
+  kArm,
+  kArm64,
+  kThumb2,
+  kX86,
+  kX86_64,
+  kMips,
+  kMips64,
+  kLast = kMips64
+};
+std::ostream& operator<<(std::ostream& os, const InstructionSet& rhs);
+
+#if defined(__arm__)
+static constexpr InstructionSet kRuntimeISA = InstructionSet::kArm;
+#elif defined(__aarch64__)
+static constexpr InstructionSet kRuntimeISA = InstructionSet::kArm64;
+#elif defined(__mips__) && !defined(__LP64__)
+static constexpr InstructionSet kRuntimeISA = InstructionSet::kMips;
+#elif defined(__mips__) && defined(__LP64__)
+static constexpr InstructionSet kRuntimeISA = InstructionSet::kMips64;
+#elif defined(__i386__)
+static constexpr InstructionSet kRuntimeISA = InstructionSet::kX86;
+#elif defined(__x86_64__)
+static constexpr InstructionSet kRuntimeISA = InstructionSet::kX86_64;
+#else
+static constexpr InstructionSet kRuntimeISA = InstructionSet::kNone;
+#endif
+
+// Architecture-specific pointer sizes
+static constexpr PointerSize kArmPointerSize = PointerSize::k32;
+static constexpr PointerSize kArm64PointerSize = PointerSize::k64;
+static constexpr PointerSize kMipsPointerSize = PointerSize::k32;
+static constexpr PointerSize kMips64PointerSize = PointerSize::k64;
+static constexpr PointerSize kX86PointerSize = PointerSize::k32;
+static constexpr PointerSize kX86_64PointerSize = PointerSize::k64;
+
+// ARM instruction alignment. ARM processors require code to be 4-byte aligned,
+// but ARM ELF requires 8..
+static constexpr size_t kArmAlignment = 8;
+
+// ARM64 instruction alignment. This is the recommended alignment for maximum performance.
+static constexpr size_t kArm64Alignment = 16;
+
+// MIPS instruction alignment.  MIPS processors require code to be 4-byte aligned,
+// but 64-bit literals must be 8-byte aligned.
+static constexpr size_t kMipsAlignment = 8;
+
+// X86 instruction alignment. This is the recommended alignment for maximum performance.
+static constexpr size_t kX86Alignment = 16;
+
+// Different than code alignment since code alignment is only first instruction of method.
+static constexpr size_t kThumb2InstructionAlignment = 2;
+static constexpr size_t kArm64InstructionAlignment = 4;
+static constexpr size_t kX86InstructionAlignment = 1;
+static constexpr size_t kX86_64InstructionAlignment = 1;
+static constexpr size_t kMipsInstructionAlignment = 4;
+static constexpr size_t kMips64InstructionAlignment = 4;
+
+const char* GetInstructionSetString(InstructionSet isa);
+
+// Note: Returns kNone when the string cannot be parsed to a known value.
+InstructionSet GetInstructionSetFromString(const char* instruction_set);
+
+// Fatal logging out of line to keep the header clean of logging.h.
+NO_RETURN void InstructionSetAbort(InstructionSet isa);
+
+constexpr PointerSize GetInstructionSetPointerSize(InstructionSet isa) {
+  switch (isa) {
+    case InstructionSet::kArm:
+      // Fall-through.
+    case InstructionSet::kThumb2:
+      return kArmPointerSize;
+    case InstructionSet::kArm64:
+      return kArm64PointerSize;
+    case InstructionSet::kX86:
+      return kX86PointerSize;
+    case InstructionSet::kX86_64:
+      return kX86_64PointerSize;
+    case InstructionSet::kMips:
+      return kMipsPointerSize;
+    case InstructionSet::kMips64:
+      return kMips64PointerSize;
+
+    case InstructionSet::kNone:
+      break;
+  }
+  InstructionSetAbort(isa);
+}
+
+constexpr size_t GetInstructionSetInstructionAlignment(InstructionSet isa) {
+  switch (isa) {
+    case InstructionSet::kArm:
+      // Fall-through.
+    case InstructionSet::kThumb2:
+      return kThumb2InstructionAlignment;
+    case InstructionSet::kArm64:
+      return kArm64InstructionAlignment;
+    case InstructionSet::kX86:
+      return kX86InstructionAlignment;
+    case InstructionSet::kX86_64:
+      return kX86_64InstructionAlignment;
+    case InstructionSet::kMips:
+      return kMipsInstructionAlignment;
+    case InstructionSet::kMips64:
+      return kMips64InstructionAlignment;
+
+    case InstructionSet::kNone:
+      break;
+  }
+  InstructionSetAbort(isa);
+}
+
+constexpr bool IsValidInstructionSet(InstructionSet isa) {
+  switch (isa) {
+    case InstructionSet::kArm:
+    case InstructionSet::kThumb2:
+    case InstructionSet::kArm64:
+    case InstructionSet::kX86:
+    case InstructionSet::kX86_64:
+    case InstructionSet::kMips:
+    case InstructionSet::kMips64:
+      return true;
+
+    case InstructionSet::kNone:
+      return false;
+  }
+  return false;
+}
+
+size_t GetInstructionSetAlignment(InstructionSet isa);
+
+constexpr bool Is64BitInstructionSet(InstructionSet isa) {
+  switch (isa) {
+    case InstructionSet::kArm:
+    case InstructionSet::kThumb2:
+    case InstructionSet::kX86:
+    case InstructionSet::kMips:
+      return false;
+
+    case InstructionSet::kArm64:
+    case InstructionSet::kX86_64:
+    case InstructionSet::kMips64:
+      return true;
+
+    case InstructionSet::kNone:
+      break;
+  }
+  InstructionSetAbort(isa);
+}
+
+constexpr PointerSize InstructionSetPointerSize(InstructionSet isa) {
+  return Is64BitInstructionSet(isa) ? PointerSize::k64 : PointerSize::k32;
+}
+
+constexpr size_t GetBytesPerGprSpillLocation(InstructionSet isa) {
+  switch (isa) {
+    case InstructionSet::kArm:
+      // Fall-through.
+    case InstructionSet::kThumb2:
+      return 4;
+    case InstructionSet::kArm64:
+      return 8;
+    case InstructionSet::kX86:
+      return 4;
+    case InstructionSet::kX86_64:
+      return 8;
+    case InstructionSet::kMips:
+      return 4;
+    case InstructionSet::kMips64:
+      return 8;
+
+    case InstructionSet::kNone:
+      break;
+  }
+  InstructionSetAbort(isa);
+}
+
+constexpr size_t GetBytesPerFprSpillLocation(InstructionSet isa) {
+  switch (isa) {
+    case InstructionSet::kArm:
+      // Fall-through.
+    case InstructionSet::kThumb2:
+      return 4;
+    case InstructionSet::kArm64:
+      return 8;
+    case InstructionSet::kX86:
+      return 8;
+    case InstructionSet::kX86_64:
+      return 8;
+    case InstructionSet::kMips:
+      return 4;
+    case InstructionSet::kMips64:
+      return 8;
+
+    case InstructionSet::kNone:
+      break;
+  }
+  InstructionSetAbort(isa);
+}
+
+size_t GetStackOverflowReservedBytes(InstructionSet isa);
+
+// The following definitions create return types for two word-sized entities that will be passed
+// in registers so that memory operations for the interface trampolines can be avoided. The entities
+// are the resolved method and the pointer to the code to be invoked.
+//
+// On x86, ARM32 and MIPS, this is given for a *scalar* 64bit value. The definition thus *must* be
+// uint64_t or long long int.
+//
+// On x86_64, ARM64 and MIPS64, structs are decomposed for allocation, so we can create a structs of
+// two size_t-sized values.
+//
+// We need two operations:
+//
+// 1) A flag value that signals failure. The assembly stubs expect the lower part to be "0".
+//    GetTwoWordFailureValue() will return a value that has lower part == 0.
+//
+// 2) A value that combines two word-sized values.
+//    GetTwoWordSuccessValue() constructs this.
+//
+// IMPORTANT: If you use this to transfer object pointers, it is your responsibility to ensure
+//            that the object does not move or the value is updated. Simple use of this is NOT SAFE
+//            when the garbage collector can move objects concurrently. Ensure that required locks
+//            are held when using!
+
+#if defined(__i386__) || defined(__arm__) || (defined(__mips__) && !defined(__LP64__))
+typedef uint64_t TwoWordReturn;
+
+// Encodes method_ptr==nullptr and code_ptr==nullptr
+static inline constexpr TwoWordReturn GetTwoWordFailureValue() {
+  return 0;
+}
+
+// Use the lower 32b for the method pointer and the upper 32b for the code pointer.
+static inline constexpr TwoWordReturn GetTwoWordSuccessValue(uintptr_t hi, uintptr_t lo) {
+  static_assert(sizeof(uint32_t) == sizeof(uintptr_t), "Unexpected size difference");
+  uint32_t lo32 = lo;
+  uint64_t hi64 = static_cast<uint64_t>(hi);
+  return ((hi64 << 32) | lo32);
+}
+
+#elif defined(__x86_64__) || defined(__aarch64__) || (defined(__mips__) && defined(__LP64__))
+
+// Note: TwoWordReturn can't be constexpr for 64-bit targets. We'd need a constexpr constructor,
+//       which would violate C-linkage in the entrypoint functions.
+
+struct TwoWordReturn {
+  uintptr_t lo;
+  uintptr_t hi;
+};
+
+// Encodes method_ptr==nullptr. Leaves random value in code pointer.
+static inline TwoWordReturn GetTwoWordFailureValue() {
+  TwoWordReturn ret;
+  ret.lo = 0;
+  return ret;
+}
+
+// Write values into their respective members.
+static inline TwoWordReturn GetTwoWordSuccessValue(uintptr_t hi, uintptr_t lo) {
+  TwoWordReturn ret;
+  ret.lo = lo;
+  ret.hi = hi;
+  return ret;
+}
+#else
+#error "Unsupported architecture"
+#endif
+
+}  // namespace art
+
+#endif  // ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_
diff --git a/libartbase/arch/instruction_set_test.cc b/libartbase/arch/instruction_set_test.cc
new file mode 100644
index 0000000..12a117d
--- /dev/null
+++ b/libartbase/arch/instruction_set_test.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "instruction_set.h"
+
+#include <gtest/gtest.h>
+
+#include "base/enums.h"
+
+namespace art {
+
+TEST(InstructionSetTest, GetInstructionSetFromString) {
+  EXPECT_EQ(InstructionSet::kArm, GetInstructionSetFromString("arm"));
+  EXPECT_EQ(InstructionSet::kArm64, GetInstructionSetFromString("arm64"));
+  EXPECT_EQ(InstructionSet::kX86, GetInstructionSetFromString("x86"));
+  EXPECT_EQ(InstructionSet::kX86_64, GetInstructionSetFromString("x86_64"));
+  EXPECT_EQ(InstructionSet::kMips, GetInstructionSetFromString("mips"));
+  EXPECT_EQ(InstructionSet::kMips64, GetInstructionSetFromString("mips64"));
+  EXPECT_EQ(InstructionSet::kNone, GetInstructionSetFromString("none"));
+  EXPECT_EQ(InstructionSet::kNone, GetInstructionSetFromString("random-string"));
+}
+
+TEST(InstructionSetTest, GetInstructionSetString) {
+  EXPECT_STREQ("arm", GetInstructionSetString(InstructionSet::kArm));
+  EXPECT_STREQ("arm", GetInstructionSetString(InstructionSet::kThumb2));
+  EXPECT_STREQ("arm64", GetInstructionSetString(InstructionSet::kArm64));
+  EXPECT_STREQ("x86", GetInstructionSetString(InstructionSet::kX86));
+  EXPECT_STREQ("x86_64", GetInstructionSetString(InstructionSet::kX86_64));
+  EXPECT_STREQ("mips", GetInstructionSetString(InstructionSet::kMips));
+  EXPECT_STREQ("mips64", GetInstructionSetString(InstructionSet::kMips64));
+  EXPECT_STREQ("none", GetInstructionSetString(InstructionSet::kNone));
+}
+
+TEST(InstructionSetTest, GetInstructionSetInstructionAlignment) {
+  EXPECT_EQ(GetInstructionSetInstructionAlignment(InstructionSet::kThumb2),
+            kThumb2InstructionAlignment);
+  EXPECT_EQ(GetInstructionSetInstructionAlignment(InstructionSet::kArm64),
+            kArm64InstructionAlignment);
+  EXPECT_EQ(GetInstructionSetInstructionAlignment(InstructionSet::kX86),
+            kX86InstructionAlignment);
+  EXPECT_EQ(GetInstructionSetInstructionAlignment(InstructionSet::kX86_64),
+            kX86_64InstructionAlignment);
+  EXPECT_EQ(GetInstructionSetInstructionAlignment(InstructionSet::kMips),
+            kMipsInstructionAlignment);
+  EXPECT_EQ(GetInstructionSetInstructionAlignment(InstructionSet::kMips64),
+            kMips64InstructionAlignment);
+}
+
+TEST(InstructionSetTest, TestRoundTrip) {
+  EXPECT_EQ(kRuntimeISA, GetInstructionSetFromString(GetInstructionSetString(kRuntimeISA)));
+}
+
+TEST(InstructionSetTest, PointerSize) {
+  EXPECT_EQ(kRuntimePointerSize, GetInstructionSetPointerSize(kRuntimeISA));
+}
+
+}  // namespace art
diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc
new file mode 100644
index 0000000..9450e1e
--- /dev/null
+++ b/libartbase/base/file_utils.cc
@@ -0,0 +1,282 @@
+/*
+ * 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.
+ */
+
+#include "file_utils.h"
+
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+// We need dladdr.
+#ifndef __APPLE__
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#define DEFINED_GNU_SOURCE
+#endif
+#include <dlfcn.h>
+#include <libgen.h>
+#ifdef DEFINED_GNU_SOURCE
+#undef _GNU_SOURCE
+#undef DEFINED_GNU_SOURCE
+#endif
+#endif
+
+
+#include <memory>
+
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+
+#include "base/bit_utils.h"
+#include "base/globals.h"
+#include "base/os.h"
+#include "base/stl_util.h"
+#include "base/unix_file/fd_file.h"
+
+#if defined(__APPLE__)
+#include <crt_externs.h>
+#include <sys/syscall.h>
+#include "AvailabilityMacros.h"  // For MAC_OS_X_VERSION_MAX_ALLOWED
+#endif
+
+#if defined(__linux__)
+#include <linux/unistd.h>
+#endif
+
+namespace art {
+
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
+static constexpr const char* kClassesDex = "classes.dex";
+
+bool ReadFileToString(const std::string& file_name, std::string* result) {
+  File file(file_name, O_RDONLY, false);
+  if (!file.IsOpened()) {
+    return false;
+  }
+
+  std::vector<char> buf(8 * KB);
+  while (true) {
+    int64_t n = TEMP_FAILURE_RETRY(read(file.Fd(), &buf[0], buf.size()));
+    if (n == -1) {
+      return false;
+    }
+    if (n == 0) {
+      return true;
+    }
+    result->append(&buf[0], n);
+  }
+}
+
+std::string GetAndroidRootSafe(std::string* error_msg) {
+  // Prefer ANDROID_ROOT if it's set.
+  const char* android_dir = getenv("ANDROID_ROOT");
+  if (android_dir != nullptr) {
+    if (!OS::DirectoryExists(android_dir)) {
+      *error_msg = StringPrintf("Failed to find ANDROID_ROOT directory %s", android_dir);
+      return "";
+    }
+    return android_dir;
+  }
+
+  // Check where libart is from, and derive from there. Only do this for non-Mac.
+#ifndef __APPLE__
+  {
+    Dl_info info;
+    if (dladdr(reinterpret_cast<const void*>(&GetAndroidRootSafe), /* out */ &info) != 0) {
+      // Make a duplicate of the fname so dirname can modify it.
+      UniqueCPtr<char> fname(strdup(info.dli_fname));
+
+      char* dir1 = dirname(fname.get());  // This is the lib directory.
+      char* dir2 = dirname(dir1);         // This is the "system" directory.
+      if (OS::DirectoryExists(dir2)) {
+        std::string tmp = dir2;  // Make a copy here so that fname can be released.
+        return tmp;
+      }
+    }
+  }
+#endif
+
+  // Try "/system".
+  if (!OS::DirectoryExists("/system")) {
+    *error_msg = "Failed to find ANDROID_ROOT directory /system";
+    return "";
+  }
+  return "/system";
+}
+
+std::string GetAndroidRoot() {
+  std::string error_msg;
+  std::string ret = GetAndroidRootSafe(&error_msg);
+  if (ret.empty()) {
+    LOG(FATAL) << error_msg;
+    UNREACHABLE();
+  }
+  return ret;
+}
+
+
+static const char* GetAndroidDirSafe(const char* env_var,
+                                     const char* default_dir,
+                                     std::string* error_msg) {
+  const char* android_dir = getenv(env_var);
+  if (android_dir == nullptr) {
+    if (OS::DirectoryExists(default_dir)) {
+      android_dir = default_dir;
+    } else {
+      *error_msg = StringPrintf("%s not set and %s does not exist", env_var, default_dir);
+      return nullptr;
+    }
+  }
+  if (!OS::DirectoryExists(android_dir)) {
+    *error_msg = StringPrintf("Failed to find %s directory %s", env_var, android_dir);
+    return nullptr;
+  }
+  return android_dir;
+}
+
+static const char* GetAndroidDir(const char* env_var, const char* default_dir) {
+  std::string error_msg;
+  const char* dir = GetAndroidDirSafe(env_var, default_dir, &error_msg);
+  if (dir != nullptr) {
+    return dir;
+  } else {
+    LOG(FATAL) << error_msg;
+    return nullptr;
+  }
+}
+
+const char* GetAndroidData() {
+  return GetAndroidDir("ANDROID_DATA", "/data");
+}
+
+const char* GetAndroidDataSafe(std::string* error_msg) {
+  return GetAndroidDirSafe("ANDROID_DATA", "/data", error_msg);
+}
+
+std::string GetDefaultBootImageLocation(std::string* error_msg) {
+  std::string android_root = GetAndroidRootSafe(error_msg);
+  if (android_root.empty()) {
+    return "";
+  }
+  return StringPrintf("%s/framework/boot.art", android_root.c_str());
+}
+
+void GetDalvikCache(const char* subdir, const bool create_if_absent, std::string* dalvik_cache,
+                    bool* have_android_data, bool* dalvik_cache_exists, bool* is_global_cache) {
+  CHECK(subdir != nullptr);
+  std::string error_msg;
+  const char* android_data = GetAndroidDataSafe(&error_msg);
+  if (android_data == nullptr) {
+    *have_android_data = false;
+    *dalvik_cache_exists = false;
+    *is_global_cache = false;
+    return;
+  } else {
+    *have_android_data = true;
+  }
+  const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data));
+  *dalvik_cache = dalvik_cache_root + subdir;
+  *dalvik_cache_exists = OS::DirectoryExists(dalvik_cache->c_str());
+  *is_global_cache = strcmp(android_data, "/data") == 0;
+  if (create_if_absent && !*dalvik_cache_exists && !*is_global_cache) {
+    // Don't create the system's /data/dalvik-cache/... because it needs special permissions.
+    *dalvik_cache_exists = ((mkdir(dalvik_cache_root.c_str(), 0700) == 0 || errno == EEXIST) &&
+                            (mkdir(dalvik_cache->c_str(), 0700) == 0 || errno == EEXIST));
+  }
+}
+
+std::string GetDalvikCache(const char* subdir) {
+  CHECK(subdir != nullptr);
+  const char* android_data = GetAndroidData();
+  const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data));
+  const std::string dalvik_cache = dalvik_cache_root + subdir;
+  if (!OS::DirectoryExists(dalvik_cache.c_str())) {
+    // TODO: Check callers. Traditional behavior is to not abort.
+    return "";
+  }
+  return dalvik_cache;
+}
+
+bool GetDalvikCacheFilename(const char* location, const char* cache_location,
+                            std::string* filename, std::string* error_msg) {
+  if (location[0] != '/') {
+    *error_msg = StringPrintf("Expected path in location to be absolute: %s", location);
+    return false;
+  }
+  std::string cache_file(&location[1]);  // skip leading slash
+  if (!android::base::EndsWith(location, ".dex") &&
+      !android::base::EndsWith(location, ".art") &&
+      !android::base::EndsWith(location, ".oat")) {
+    cache_file += "/";
+    cache_file += kClassesDex;
+  }
+  std::replace(cache_file.begin(), cache_file.end(), '/', '@');
+  *filename = StringPrintf("%s/%s", cache_location, cache_file.c_str());
+  return true;
+}
+
+std::string GetVdexFilename(const std::string& oat_location) {
+  return ReplaceFileExtension(oat_location, "vdex");
+}
+
+static void InsertIsaDirectory(const InstructionSet isa, std::string* filename) {
+  // in = /foo/bar/baz
+  // out = /foo/bar/<isa>/baz
+  size_t pos = filename->rfind('/');
+  CHECK_NE(pos, std::string::npos) << *filename << " " << isa;
+  filename->insert(pos, "/", 1);
+  filename->insert(pos + 1, GetInstructionSetString(isa));
+}
+
+std::string GetSystemImageFilename(const char* location, const InstructionSet isa) {
+  // location = /system/framework/boot.art
+  // filename = /system/framework/<isa>/boot.art
+  std::string filename(location);
+  InsertIsaDirectory(isa, &filename);
+  return filename;
+}
+
+std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension) {
+  const size_t last_ext = filename.find_last_of("./");
+  if (last_ext == std::string::npos || filename[last_ext] != '.') {
+    return filename + "." + new_extension;
+  } else {
+    return filename.substr(0, last_ext + 1) + new_extension;
+  }
+}
+
+bool LocationIsOnSystem(const char* path) {
+  UniqueCPtr<const char[]> full_path(realpath(path, nullptr));
+  return path != nullptr && android::base::StartsWith(full_path.get(), GetAndroidRoot().c_str());
+}
+
+bool LocationIsOnSystemFramework(const char* full_path) {
+  std::string error_msg;
+  std::string root_path = GetAndroidRootSafe(&error_msg);
+  if (root_path.empty()) {
+    // Could not find Android root.
+    // TODO(dbrazdil): change to stricter GetAndroidRoot() once b/76452688 is resolved.
+    return false;
+  }
+  std::string framework_path = root_path + "/framework/";
+  return android::base::StartsWith(full_path, framework_path);
+}
+
+}  // namespace art
diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h
new file mode 100644
index 0000000..063393b
--- /dev/null
+++ b/libartbase/base/file_utils.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_LIBARTBASE_BASE_FILE_UTILS_H_
+#define ART_LIBARTBASE_BASE_FILE_UTILS_H_
+
+#include <stdlib.h>
+
+#include <string>
+
+#include <android-base/logging.h>
+
+#include "arch/instruction_set.h"
+
+namespace art {
+
+bool ReadFileToString(const std::string& file_name, std::string* result);
+
+// Find $ANDROID_ROOT, /system, or abort.
+std::string GetAndroidRoot();
+// Find $ANDROID_ROOT, /system, or return an empty string.
+std::string GetAndroidRootSafe(std::string* error_msg);
+
+// Find $ANDROID_DATA, /data, or abort.
+const char* GetAndroidData();
+// Find $ANDROID_DATA, /data, or return null.
+const char* GetAndroidDataSafe(std::string* error_msg);
+
+// Returns the default boot image location (ANDROID_ROOT/framework/boot.art).
+// Returns an empty string if ANDROID_ROOT is not set.
+std::string GetDefaultBootImageLocation(std::string* error_msg);
+
+// Returns the dalvik-cache location, with subdir appended. Returns the empty string if the cache
+// could not be found.
+std::string GetDalvikCache(const char* subdir);
+
+// Return true if we found the dalvik cache and stored it in the dalvik_cache argument.
+// have_android_data will be set to true if we have an ANDROID_DATA that exists,
+// dalvik_cache_exists will be true if there is a dalvik-cache directory that is present.
+// The flag is_global_cache tells whether this cache is /data/dalvik-cache.
+void GetDalvikCache(const char* subdir, bool create_if_absent, std::string* dalvik_cache,
+                    bool* have_android_data, bool* dalvik_cache_exists, bool* is_global_cache);
+
+// Returns the absolute dalvik-cache path for a DexFile or OatFile. The path returned will be
+// rooted at cache_location.
+bool GetDalvikCacheFilename(const char* file_location, const char* cache_location,
+                            std::string* filename, std::string* error_msg);
+
+// Returns the system location for an image
+std::string GetSystemImageFilename(const char* location, InstructionSet isa);
+
+// Returns the vdex filename for the given oat filename.
+std::string GetVdexFilename(const std::string& oat_filename);
+
+// Returns `filename` with the text after the last occurrence of '.' replaced with
+// `extension`. If `filename` does not contain a period, returns a string containing `filename`,
+// a period, and `new_extension`.
+// Example: ReplaceFileExtension("foo.bar", "abc") == "foo.abc"
+//          ReplaceFileExtension("foo", "abc") == "foo.abc"
+std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension);
+
+// Return whether the location is on system (i.e. android root).
+bool LocationIsOnSystem(const char* location);
+
+// Return whether the location is on system/framework (i.e. android_root/framework).
+bool LocationIsOnSystemFramework(const char* location);
+
+}  // namespace art
+
+#endif  // ART_LIBARTBASE_BASE_FILE_UTILS_H_
diff --git a/libartbase/base/file_utils_test.cc b/libartbase/base/file_utils_test.cc
new file mode 100644
index 0000000..e74dfe5
--- /dev/null
+++ b/libartbase/base/file_utils_test.cc
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+#include "base/file_utils.h"
+
+#include <libgen.h>
+#include <stdlib.h>
+
+#include "base/stl_util.h"
+#include "common_runtime_test.h"
+
+namespace art {
+
+class FileUtilsTest : public CommonRuntimeTest {};
+
+TEST_F(FileUtilsTest, GetDalvikCacheFilename) {
+  std::string name;
+  std::string error;
+
+  EXPECT_TRUE(GetDalvikCacheFilename("/system/app/Foo.apk", "/foo", &name, &error)) << error;
+  EXPECT_EQ("/foo/system@app@Foo.apk@classes.dex", name);
+
+  EXPECT_TRUE(GetDalvikCacheFilename("/data/app/foo-1.apk", "/foo", &name, &error)) << error;
+  EXPECT_EQ("/foo/data@app@foo-1.apk@classes.dex", name);
+
+  EXPECT_TRUE(GetDalvikCacheFilename("/system/framework/core.jar", "/foo", &name, &error)) << error;
+  EXPECT_EQ("/foo/system@framework@core.jar@classes.dex", name);
+
+  EXPECT_TRUE(GetDalvikCacheFilename("/system/framework/boot.art", "/foo", &name, &error)) << error;
+  EXPECT_EQ("/foo/system@framework@boot.art", name);
+
+  EXPECT_TRUE(GetDalvikCacheFilename("/system/framework/boot.oat", "/foo", &name, &error)) << error;
+  EXPECT_EQ("/foo/system@framework@boot.oat", name);
+}
+
+TEST_F(FileUtilsTest, GetDalvikCache) {
+  EXPECT_STREQ("", GetDalvikCache("should-not-exist123").c_str());
+
+  EXPECT_STREQ((android_data_ + "/dalvik-cache/.").c_str(), GetDalvikCache(".").c_str());
+}
+
+
+TEST_F(FileUtilsTest, GetSystemImageFilename) {
+  EXPECT_STREQ("/system/framework/arm/boot.art",
+               GetSystemImageFilename("/system/framework/boot.art", InstructionSet::kArm).c_str());
+}
+
+TEST_F(FileUtilsTest, GetAndroidRootSafe) {
+  std::string error_msg;
+
+  // We don't expect null returns for most cases, so don't check and let std::string crash.
+
+  // CommonRuntimeTest sets ANDROID_ROOT, so expect this to be the same.
+  std::string android_root = GetAndroidRootSafe(&error_msg);
+  std::string android_root_env = getenv("ANDROID_ROOT");
+  EXPECT_EQ(android_root, android_root_env);
+
+  // Set ANDROID_ROOT to something else (but the directory must exist). So use dirname.
+  char* root_dup = strdup(android_root_env.c_str());
+  char* dir = dirname(root_dup);
+  ASSERT_EQ(0, setenv("ANDROID_ROOT", dir, 1 /* overwrite */));
+  std::string android_root2 = GetAndroidRootSafe(&error_msg);
+  EXPECT_STREQ(dir, android_root2.c_str());
+  free(root_dup);
+
+  // Set a bogus value for ANDROID_ROOT. This should be an error.
+  ASSERT_EQ(0, setenv("ANDROID_ROOT", "/this/is/obviously/bogus", 1 /* overwrite */));
+  EXPECT_TRUE(GetAndroidRootSafe(&error_msg) == nullptr);
+
+  // Unset ANDROID_ROOT and see that it still returns something (as libart code is running).
+  ASSERT_EQ(0, unsetenv("ANDROID_ROOT"));
+  std::string android_root3 = GetAndroidRootSafe(&error_msg);
+  // This should be the same as the other root (modulo realpath), otherwise the test setup is
+  // broken.
+  UniqueCPtr<char> real_root(realpath(android_root.c_str(), nullptr));
+  UniqueCPtr<char> real_root3(realpath(android_root3.c_str(), nullptr));
+  EXPECT_STREQ(real_root.get(), real_root3.get());
+
+
+  // Reset ANDROID_ROOT, as other things may depend on it.
+  ASSERT_EQ(0, setenv("ANDROID_ROOT", android_root_env.c_str(), 1 /* overwrite */));
+}
+
+TEST_F(FileUtilsTest, ReplaceFileExtension) {
+  EXPECT_EQ("/directory/file.vdex", ReplaceFileExtension("/directory/file.oat", "vdex"));
+  EXPECT_EQ("/.directory/file.vdex", ReplaceFileExtension("/.directory/file.oat", "vdex"));
+  EXPECT_EQ("/directory/file.vdex", ReplaceFileExtension("/directory/file", "vdex"));
+  EXPECT_EQ("/.directory/file.vdex", ReplaceFileExtension("/.directory/file", "vdex"));
+}
+
+}  // namespace art