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