diff options
author | 2011-09-29 00:53:55 -0700 | |
---|---|---|
committer | 2011-10-03 10:28:05 -0700 | |
commit | e24fa61603a60ade3797e4a0c8b3fccb346cb048 (patch) | |
tree | 9ec5ed942eb9ece6d4d261ffd21956c94f9968fe | |
parent | 06cbeb990e94f9c4576601e27145fe132e34a686 (diff) |
Separate oat from image
Change-Id: If2abdb99826ead14e3465d90ba2acffd89709389
43 files changed, 1644 insertions, 234 deletions
diff --git a/Android.mk b/Android.mk index f141d51a13..bd37967c3b 100644 --- a/Android.mk +++ b/Android.mk @@ -57,8 +57,8 @@ define run-host-tests-with $(foreach file,$(sort $(ART_HOST_TEST_EXECUTABLES)),$(1) $(file) &&) true endef -ART_HOST_TEST_DEPENDENCIES := $(ART_HOST_TEST_EXECUTABLES) $(ANDROID_HOST_OUT)/framework/core-hostdex.jar $(ART_TEST_OAT_FILES) -ART_TARGET_TEST_DEPENDENCIES := $(ART_TARGET_TEST_EXECUTABLES) $(ANDROID_PRODUCT_OUT)/system/framework/core.jar $(ART_TEST_OAT_FILES) +ART_HOST_TEST_DEPENDENCIES := $(ART_HOST_EXECUTABLES) $(ART_HOST_TEST_EXECUTABLES) $(ANDROID_HOST_OUT)/framework/core-hostdex.jar $(ART_TEST_OAT_FILES) +ART_TARGET_TEST_DEPENDENCIES := $(ART_TARGET_EXECUTABLES) $(ART_TARGET_TEST_EXECUTABLES) $(ANDROID_PRODUCT_OUT)/system/framework/core.jar $(ART_TEST_OAT_FILES) ART_TARGET_TEST_DEPENDENCIES += $(TARGET_OUT_EXECUTABLES)/oat_process $(TARGET_OUT_EXECUTABLES)/oat_processd @@ -88,7 +88,7 @@ tsan-art-host: $(ART_HOST_TEST_DEPENDENCIES) # "mm test-art-target" to build and run all target tests .PHONY: test-art-target -test-art-target: test-art-target-gtest test-art-target-oat +test-art-target: test-art-target-gtest test-art-target-oat test-art-target-run-test @echo test-art-target PASSED .PHONY: test-art-target-sync @@ -108,6 +108,15 @@ test-art-target-gtest: test-art-target-sync test-art-target-oat: $(ART_TEST_OAT_TARGETS) @echo test-art-target-oat PASSED +.PHONY: test-art-target-run-test +test-art-target-run-test: test-art-target-run-test-002 + @echo test-art-target-run-test PASSED + +.PHONY: test-art-target-run-test-002 +test-art-target-run-test-002: + art/test/run-test 002 + @echo test-art-target-run-test-002 PASSED + ######################################################################## # oat_process test targets @@ -125,7 +134,7 @@ $(eval $(call build-art-framework-oat,$(TARGET_OUT_JAVA_LIBRARIES)/am.jar)) test-art-target-oat-process-am: $(TARGET_OUT_JAVA_LIBRARIES)/am.oat test-art-target-sync adb remount adb sync - adb shell sh -c "export CLASSPATH=/system/framework/am.jar && oat_processd /system/bin/app_process -Xbootimage:/system/framework/boot.oat -Ximage:/system/framework/am.oat /system/bin com.android.commands.am.Am start http://android.com && touch /sdcard/test-art-target-process-am" + adb shell sh -c "export CLASSPATH=/system/framework/am.jar && oat_processd /system/bin/app_process -Xbootimage:/system/framework/boot.art -Ximage:/system/framework/am.oat /system/bin com.android.commands.am.Am start http://android.com && touch /sdcard/test-art-target-process-am" $(hide) (adb pull /sdcard/test-art-target-process-am /tmp/ && echo test-art-target-process-am PASSED) || echo test-art-target-process-am FAILED $(hide) rm /tmp/test-art-target-process-am @@ -149,7 +158,7 @@ test-art-target-oat-process-Calculator: $(TARGET_OUT_APPS)/Calculator.oat $(TARG sleep 30; \ fi adb shell kill `adb shell ps | fgrep com.android.calculator2 | sed -e 's/[^ ]* *\([0-9]*\).*/\1/'` - adb shell sh -c "export CLASSPATH=/system/framework/am.jar && oat_processd /system/bin/app_process -Xbootimage:/system/framework/boot.oat -Ximage:/system/framework/am.oat /system/bin com.android.commands.am.Am start -a android.intent.action.MAIN -n com.android.calculator2/.Calculator && touch /sdcard/test-art-target-process-Calculator" + adb shell sh -c "export CLASSPATH=/system/framework/am.jar && oat_processd /system/bin/app_process -Xbootimage:/system/framework/boot.art -Ximage:/system/framework/am.oat /system/bin com.android.commands.am.Am start -a android.intent.action.MAIN -n com.android.calculator2/.Calculator && touch /sdcard/test-art-target-process-Calculator" $(hide) (adb pull /sdcard/test-art-target-process-Calculator /tmp/ && echo test-art-target-process-Calculator PASSED) || echo test-art-target-process-Calculator FAILED $(hide) rm /tmp/test-art-target-process-Calculator @@ -161,17 +170,17 @@ dump-oat: dump-oat-core dump-oat-boot dump-oat-Calculator .PHONY: dump-oat-core dump-oat-core: $(TARGET_CORE_OAT) $(OATDUMP) - $(OATDUMP) $(addprefix --dex-file=,$(TARGET_CORE_DEX)) --image=$< --strip-prefix=$(PRODUCT_OUT) --output=/tmp/core.oatdump.txt + $(OATDUMP) $(addprefix --dex-file=,$(TARGET_CORE_DEX)) --oat=$(TARGET_CORE_OAT) --image=$(TARGET_CORE_IMG) --strip-prefix=$(PRODUCT_OUT) --output=/tmp/core.oatdump.txt @echo Output in /tmp/core.oatdump.txt .PHONY: dump-oat-boot dump-oat-boot: $(TARGET_BOOT_OAT) $(OATDUMP) - $(OATDUMP) $(addprefix --dex-file=,$(TARGET_BOOT_DEX)) --image=$< --strip-prefix=$(PRODUCT_OUT) --output=/tmp/boot.oatdump.txt + $(OATDUMP) $(addprefix --dex-file=,$(TARGET_BOOT_DEX)) --oat=$(TARGET_BOOT_OAT) --image=$(TARGET_BOOT_IMG) --strip-prefix=$(PRODUCT_OUT) --output=/tmp/boot.oatdump.txt @echo Output in /tmp/boot.oatdump.txt .PHONY: dump-oat-Calculator dump-oat-Calculator: $(TARGET_OUT_APPS)/Calculator.oat $(TARGET_BOOT_OAT) $(OATDUMP) - $(OATDUMP) --dex-file=$(TARGET_OUT_APPS)/Calculator.apk --image=$< $(addprefix --boot-dex-file=,$(TARGET_BOOT_DEX)) --boot=$(TARGET_BOOT_OAT) --strip-prefix=$(PRODUCT_OUT) --output=/tmp/Calculator.oatdump.txt + $(OATDUMP) --dex-file=$(TARGET_OUT_APPS)/Calculator.apk --oat=$< --image=$(patsubst %.oat,%.art,$<) $(addprefix --boot-dex-file=,$(TARGET_BOOT_DEX)) --boot-oat=$(TARGET_BOOT_OAT) --boot-image=$(TARGET_BOOT_IMG) --strip-prefix=$(PRODUCT_OUT) --output=/tmp/Calculator.oatdump.txt @echo Output in /tmp/Calculator.oatdump.txt diff --git a/build/Android.common.mk b/build/Android.common.mk index 1b65971840..481fbc051e 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -129,6 +129,9 @@ LIBART_COMMON_SRC_FILES := \ src/monitor.cc \ src/mspace.c \ src/mutex.cc \ + src/oat.cc \ + src/oat_file.cc \ + src/oat_writer.cc \ src/object.cc \ src/object_bitmap.cc \ src/offsets.cc \ @@ -187,6 +190,7 @@ TEST_COMMON_SRC_FILES := \ src/jni_compiler_test.cc \ src/managed_register_arm_test.cc \ src/managed_register_x86_test.cc \ + src/oat_test.cc \ src/object_test.cc \ src/reference_table_test.cc \ src/runtime_test.cc \ diff --git a/build/Android.oat.mk b/build/Android.oat.mk index 2ea132d3a7..52d56216a3 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -24,9 +24,9 @@ OATDUMPD := $(HOST_OUT_EXECUTABLES)/oatdumpd$(HOST_EXECUTABLE_SUFFIX) # TODO: for now, override with debug version for better error reporting OATDUMP := $(OATDUMPD) -# start of oat reserved address space -OAT_HOST_BASE_ADDRESS := 0x60000000 -OAT_TARGET_BASE_ADDRESS := 0x60000000 +# start of image reserved address space +IMG_HOST_BASE_ADDRESS := 0x60000000 +IMG_TARGET_BASE_ADDRESS := 0x60000000 ######################################################################## # A smaller libcore only oat file @@ -39,23 +39,27 @@ TARGET_CORE_DEX := $(foreach jar,$(TARGET_CORE_JARS),$(TARGET_OUT_JAVA_LIBRARIES HOST_CORE_OAT := $(HOST_OUT_JAVA_LIBRARIES)/core.oat TARGET_CORE_OAT := $(TARGET_OUT_JAVA_LIBRARIES)/core.oat +HOST_CORE_IMG := $(HOST_OUT_JAVA_LIBRARIES)/core.art +TARGET_CORE_IMG := $(TARGET_OUT_JAVA_LIBRARIES)/core.art + # TODO: change DEX2OATD to order-only prerequisite when output is stable $(HOST_CORE_OAT): $(HOST_CORE_DEX) $(DEX2OAT) @echo "host dex2oat: $@ ($<)" - $(hide) $(DEX2OAT) -Xms16m -Xmx16m $(addprefix --dex-file=,$(filter-out $(DEX2OAT),$^)) --image=$@ --base=$(OAT_HOST_BASE_ADDRESS) + $(hide) $(DEX2OAT) -Xms16m -Xmx16m $(addprefix --dex-file=,$(filter-out $(DEX2OAT),$^)) --oat=$@ --image=$(HOST_CORE_IMG) --base=$(IMG_HOST_BASE_ADDRESS) # TODO: change DEX2OATD to order-only prerequisite when output is stable $(TARGET_CORE_OAT): $(TARGET_CORE_DEX) $(DEX2OAT) @echo "target dex2oat: $@ ($<)" - $(hide) $(DEX2OAT) -Xms32m -Xmx32m $(addprefix --dex-file=,$(filter-out $(DEX2OAT),$^)) --image=$@ --base=$(OAT_TARGET_BASE_ADDRESS) --strip-prefix=$(PRODUCT_OUT) + $(hide) $(DEX2OAT) -Xms32m -Xmx32m $(addprefix --dex-file=,$(filter-out $(DEX2OAT),$^)) --oat=$@ --image=$(TARGET_CORE_IMG) --base=$(IMG_TARGET_BASE_ADDRESS) --strip-prefix=$(PRODUCT_OUT) ######################################################################## # The full system boot classpath TARGET_BOOT_JARS := $(subst :, ,$(DEXPREOPT_BOOT_JARS)) TARGET_BOOT_DEX := $(foreach jar,$(TARGET_BOOT_JARS),$(TARGET_OUT_JAVA_LIBRARIES)/$(jar).jar) TARGET_BOOT_OAT := $(TARGET_OUT_JAVA_LIBRARIES)/boot.oat +TARGET_BOOT_IMG := $(TARGET_OUT_JAVA_LIBRARIES)/boot.art # TODO: change DEX2OATD to order-only prerequisite when output is stable $(TARGET_BOOT_OAT): $(TARGET_BOOT_DEX) $(DEX2OAT) @echo "target dex2oat: $@ ($<)" - $(hide) $(DEX2OAT) -Xms256m -Xmx256m $(addprefix --dex-file=,$(filter-out $(DEX2OAT),$^)) --image=$@ --base=$(OAT_TARGET_BASE_ADDRESS) --strip-prefix=$(PRODUCT_OUT) + $(hide) $(DEX2OAT) -Xms256m -Xmx256m $(addprefix --dex-file=,$(filter-out $(DEX2OAT),$^)) --oat=$@ --image=$(TARGET_BOOT_IMG) --base=$(IMG_TARGET_BASE_ADDRESS) --strip-prefix=$(PRODUCT_OUT) diff --git a/build/Android.oattest.mk b/build/Android.oattest.mk index fe26a9bbeb..ce615b2bf7 100644 --- a/build/Android.oattest.mk +++ b/build/Android.oattest.mk @@ -40,7 +40,7 @@ define build-art-oat # TODO: change DEX2OATD (and perhaps $(2) boot oat) to order-only prerequisite when output is stable $(patsubst %.apk,%.oat,$(patsubst %.jar,%.oat,$(1))): $(1) $(2) $(DEX2OAT) @echo "target dex2oat: $$@ ($$<)" - $(hide) $(DEX2OAT) -Xms16m -Xmx16m $(addprefix --boot-dex-file=,$(3)) --boot=$(2) $(addprefix --dex-file=,$$<) --image=$$@ --strip-prefix=$(PRODUCT_OUT) + $(hide) $(DEX2OAT) -Xms16m -Xmx16m $(addprefix --boot-dex-file=,$(3)) --boot-oat=$(2) --boot-image=$(patsubst %.oat,%.art,$(2)) $(addprefix --dex-file=,$$<) --oat=$$@ --image=$$(patsubst %.oat,%.art,$$@) --strip-prefix=$(PRODUCT_OUT) endef ######################################################################## @@ -64,7 +64,7 @@ define declare-test-test-target test-art-target-oat-$(1): test-art-target-sync adb shell touch /sdcard/test-art-target-oat-$(1) adb shell rm /sdcard/test-art-target-oat-$(1) - adb shell sh -c "oatexecd -Xbootclasspath:/system/framework/core.jar -Xbootimage:/system/framework/core.oat -classpath /system/framework/art-test-dex-$(1).jar -Ximage:/system/framework/art-test-dex-$(1).oat $(1) $(2) && touch /sdcard/test-art-target-oat-$(1)" + adb shell sh -c "oatexecd -Xbootclasspath:/system/framework/core.jar -Xbootoat:/system/framework/core.oat -Xbootimage:/system/framework/core.art -classpath /system/framework/art-test-dex-$(1).jar -Xoat:/system/framework/art-test-dex-$(1).oat -Ximage:/system/framework/art-test-dex-$(1).art $(1) $(2) && touch /sdcard/test-art-target-oat-$(1)" $(hide) (adb pull /sdcard/test-art-target-oat-$(1) /tmp/ && echo test-art-target-oat-$(1) PASSED) || (echo test-art-target-oat-$(1) FAILED && exit 1) $(hide) rm /tmp/test-art-target-oat-$(1) diff --git a/src/common_test.h b/src/common_test.h index bbf2e2f239..8491dbbbb4 100644 --- a/src/common_test.h +++ b/src/common_test.h @@ -106,19 +106,21 @@ class CommonTest : public testing::Test { Runtime::Options options; options.push_back(std::make_pair("bootclasspath", &boot_class_path_)); options.push_back(std::make_pair("-Xcheck:jni", reinterpret_cast<void*>(NULL))); - options.push_back(std::make_pair("-Xms16m", reinterpret_cast<void*>(NULL))); - options.push_back(std::make_pair("-Xmx16m", reinterpret_cast<void*>(NULL))); + options.push_back(std::make_pair("-Xms64m", reinterpret_cast<void*>(NULL))); + options.push_back(std::make_pair("-Xmx64m", reinterpret_cast<void*>(NULL))); runtime_.reset(Runtime::Create(options, false)); ASSERT_TRUE(runtime_.get() != NULL); class_linker_ = runtime_->GetClassLinker(); #if defined(__i386__) runtime_->SetJniStubArray(JniCompiler::CreateJniStub(kX86)); + runtime_->SetAbstractMethodErrorStubArray(Compiler::CreateAbstractMethodErrorStub(kX86)); runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(kX86)); compiler_.reset(new Compiler(kX86)); #elif defined(__arm__) runtime_->SetJniStubArray(JniCompiler::CreateJniStub(kThumb2)); runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(kThumb2)); + runtime_->SetAbstractMethodErrorStubArray(Compiler::CreateAbstractMethodErrorStub(kThumb2)); compiler_.reset(new Compiler(kThumb2)); #endif diff --git a/src/compiler.cc b/src/compiler.cc index 34263da637..af5cdd6512 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -24,14 +24,22 @@ namespace x86 { ByteArray* CreateAbstractMethodErrorStub(); } +ByteArray* Compiler::CreateAbstractMethodErrorStub(InstructionSet instruction_set) { + switch (instruction_set) { + case kArm: + case kThumb2: + return arm::CreateAbstractMethodErrorStub(); + case kX86: + return x86::CreateAbstractMethodErrorStub(); + default: + LOG(FATAL) << "Unknown InstructionSet " << (int) instruction_set; + return NULL; + } +} + Compiler::Compiler(InstructionSet insns) : instruction_set_(insns), jni_compiler_(insns), verbose_(false) { CHECK(!Runtime::Current()->IsStarted()); - if (insns == kArm || insns == kThumb2) { - abstract_method_error_stub_ = arm::CreateAbstractMethodErrorStub(); - } else if (insns == kX86) { - abstract_method_error_stub_ = x86::CreateAbstractMethodErrorStub(); - } } void Compiler::CompileAll(const ClassLoader* class_loader) { @@ -273,17 +281,17 @@ void Compiler::CompileMethod(Method* method) { method->UnregisterNative(); } } else if (method->IsAbstract()) { - DCHECK(abstract_method_error_stub_ != NULL); + ByteArray* abstract_method_error_stub = Runtime::Current()->GetAbstractMethodErrorStubArray(); if (instruction_set_ == kX86) { - method->SetCode(abstract_method_error_stub_, kX86); + method->SetCodeArray(abstract_method_error_stub, kX86); } else { CHECK(instruction_set_ == kArm || instruction_set_ == kThumb2); - method->SetCode(abstract_method_error_stub_, kArm); + method->SetCodeArray(abstract_method_error_stub, kArm); } } else { oatCompileMethod(*this, method, kThumb2); } - CHECK(method->GetCode() != NULL); + CHECK(method->GetCode() != NULL) << PrettyMethod(method); if (instruction_set_ == kX86) { art::x86::X86CreateInvokeStub(method); diff --git a/src/compiler.h b/src/compiler.h index 563bbde359..f9a5c291ac 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -30,6 +30,10 @@ class Compiler { return verbose_; } + // Stub to throw AbstractMethodError + // TODO: remove from Compiler + static ByteArray* CreateAbstractMethodErrorStub(InstructionSet instruction_set); + private: // Attempt to resolve all type, methods, fields, and strings // referenced from code in the dex file following PathClassLoader @@ -55,7 +59,6 @@ class Compiler { InstructionSet instruction_set_; JniCompiler jni_compiler_; - ByteArray* abstract_method_error_stub_; bool verbose_; diff --git a/src/compiler/Frontend.cc b/src/compiler/Frontend.cc index eef68883d4..cb831438a7 100644 --- a/src/compiler/Frontend.cc +++ b/src/compiler/Frontend.cc @@ -904,7 +904,9 @@ bool oatCompileMethod(const Compiler& compiler, Method* method, art::Instruction memcpy(vmap_table->GetData(), reinterpret_cast<const int16_t*>(&cUnit.coreVmapTable[0]), vmap_table->GetLength() * sizeof(cUnit.coreVmapTable[0])); - method->SetCode(managed_code, art::kThumb2, mapping_table, vmap_table); + method->SetCodeArray(managed_code, art::kThumb2); + method->SetMappingTable(mapping_table); + method->SetVMapTable(vmap_table); method->SetFrameSizeInBytes(cUnit.frameSize); method->SetReturnPcOffsetInBytes(cUnit.frameSize - sizeof(intptr_t)); method->SetCoreSpillMask(cUnit.coreSpillMask); diff --git a/src/dex2oat.cc b/src/dex2oat.cc index 6c967ed6f4..78d36281d8 100644 --- a/src/dex2oat.cc +++ b/src/dex2oat.cc @@ -10,6 +10,7 @@ #include "class_loader.h" #include "compiler.h" #include "image_writer.h" +#include "oat_writer.h" #include "runtime.h" #include "stringpiece.h" @@ -25,7 +26,12 @@ static void usage() { " Example: --dex-file=/system/framework/core.jar\n" "\n"); fprintf(stderr, - " --image=<file>: specifies the required output image filename.\n" + " --image=<file.art>: specifies the required output image filename.\n" + " Example: --image=/system/framework/boot.art\n" + "\n"); + // TODO: remove this by inferring from --image + fprintf(stderr, + " --oat=<file.oat>: specifies the required oat filename.\n" " Example: --image=/system/framework/boot.oat\n" "\n"); fprintf(stderr, @@ -33,10 +39,15 @@ static void usage() { " Example: --base=0x50000000\n" "\n"); fprintf(stderr, - " --boot=<oat-file>: provide the oat file for the boot class path.\n" - " Example: --boot=/system/framework/boot.oat\n" + " --boot-image=<file.art>: provide the image file for the boot class path.\n" + " Example: --boot-image=/system/framework/boot.art\n" + "\n"); + // TODO: remove this by inferring from --boot-image + fprintf(stderr, + " --boot-oat=<file.oat>: provide the oat file for the boot class path.\n" + " Example: --boot-oat=/system/framework/boot.oat\n" "\n"); - // TODO: remove this by making boot image contain boot DexFile information? + // TODO: remove this by inderring from --boot-image or --boot-oat fprintf(stderr, " --boot-dex-file=<dex-file>: specifies a .dex file that is part of the boot\n" " image specified with --boot. \n" @@ -76,8 +87,10 @@ int dex2oat(int argc, char** argv) { std::vector<const char*> dex_filenames; std::vector<const char*> method_names; + std::string oat_filename; const char* image_filename = NULL; std::string boot_image_option; + std::string boot_oat_option; std::vector<const char*> boot_dex_filenames; uintptr_t image_base = 0; std::string strip_location_prefix; @@ -90,6 +103,8 @@ int dex2oat(int argc, char** argv) { dex_filenames.push_back(option.substr(strlen("--dex-file=")).data()); } else if (option.starts_with("--method=")) { method_names.push_back(option.substr(strlen("--method=")).data()); + } else if (option.starts_with("--oat=")) { + oat_filename = option.substr(strlen("--oat=")).data(); } else if (option.starts_with("--image=")) { image_filename = option.substr(strlen("--image=")).data(); } else if (option.starts_with("--base=")) { @@ -100,11 +115,16 @@ int dex2oat(int argc, char** argv) { fprintf(stderr, "Failed to parse hexadecimal value for option %s\n", option.data()); usage(); } - } else if (option.starts_with("--boot=")) { - const char* boot_image_filename = option.substr(strlen("--boot=")).data(); + } else if (option.starts_with("--boot-image=")) { + const char* boot_image_filename = option.substr(strlen("--boot-image=")).data(); boot_image_option.clear(); boot_image_option += "-Xbootimage:"; boot_image_option += boot_image_filename; + } else if (option.starts_with("--boot-oat=")) { + const char* boot_oat_filename = option.substr(strlen("--boot-oat=")).data(); + boot_oat_option.clear(); + boot_oat_option += "-Xbootoat:"; + boot_oat_option += boot_oat_filename; } else if (option.starts_with("--boot-dex-file=")) { boot_dex_filenames.push_back(option.substr(strlen("--boot-dex-file=")).data()); } else if (option.starts_with("--strip-prefix=")) { @@ -119,6 +139,11 @@ int dex2oat(int argc, char** argv) { } } + if (oat_filename == NULL) { + fprintf(stderr, "--oat file name not specified\n"); + return EXIT_FAILURE; + } + if (image_filename == NULL) { fprintf(stderr, "--image file name not specified\n"); return EXIT_FAILURE; @@ -129,6 +154,11 @@ int dex2oat(int argc, char** argv) { return EXIT_FAILURE; } + if (boot_image_option.empty() != boot_oat_option.empty()) { + fprintf(stderr, "--boot-image and --boat-oat must be specified together or not at all\n"); + return EXIT_FAILURE; + } + if (boot_image_option.empty()) { if (image_base == 0) { fprintf(stderr, "non-zero --base not specified\n"); @@ -136,7 +166,7 @@ int dex2oat(int argc, char** argv) { } } else { if (boot_dex_filenames.empty()) { - fprintf(stderr, "no --boot-dex-file values specified with --boot\n"); + fprintf(stderr, "no --boot-dex-file values specified with --boot-image\n"); return EXIT_FAILURE; } } @@ -153,6 +183,7 @@ int dex2oat(int argc, char** argv) { } else { options.push_back(std::make_pair("bootclasspath", &boot_dex_files)); options.push_back(std::make_pair(boot_image_option.c_str(), reinterpret_cast<void*>(NULL))); + options.push_back(std::make_pair(boot_oat_option.c_str(), reinterpret_cast<void*>(NULL))); } if (Xms != NULL) { options.push_back(std::make_pair(Xms, reinterpret_cast<void*>(NULL))); @@ -167,11 +198,12 @@ int dex2oat(int argc, char** argv) { } ClassLinker* class_linker = runtime->GetClassLinker(); - // If we have an existing boot image, position new space after it + // If we have an existing boot image, position new space after its oat file if (!boot_image_option.empty()) { Space* boot_space = Heap::GetBootSpace(); CHECK(boot_space != NULL); - image_base = RoundUp(reinterpret_cast<uintptr_t>(boot_space->GetLimit()), kPageSize); + byte* oat_limit_addr = boot_space->GetImageHeader().GetOatLimitAddr(); + image_base = RoundUp(reinterpret_cast<uintptr_t>(oat_limit_addr), kPageSize); } // ClassLoader creation needs to come after Runtime::Create @@ -185,11 +217,13 @@ int dex2oat(int argc, char** argv) { class_loader = PathClassLoader::Alloc(dex_files); } - // if we loaded an existing image, we will reuse its stub array. + // if we loaded an existing image, we will reuse values from the image roots. if (!runtime->HasJniStubArray()) { runtime->SetJniStubArray(JniCompiler::CreateJniStub(kThumb2)); } - // similarly for the callee save method + if (!runtime->HasAbstractMethodErrorStubArray()) { + runtime->SetAbstractMethodErrorStubArray(Compiler::CreateAbstractMethodErrorStub(kThumb2)); + } if (!runtime->HasCalleeSaveMethod()) { runtime->SetCalleeSaveMethod(runtime->CreateCalleeSaveMethod(kThumb2)); } @@ -239,9 +273,14 @@ int dex2oat(int argc, char** argv) { } } - ImageWriter writer; - if (!writer.Write(image_filename, image_base)) { - fprintf(stderr, "could not write image %s\n", image_filename); + if (!OatWriter::Create(oat_filename, class_loader)) { + fprintf(stderr, "Failed to create oat file %s\n", oat_filename.c_str()); + return EXIT_FAILURE; + } + + ImageWriter image_writer; + if (!image_writer.Write(image_filename, image_base, oat_filename, strip_location_prefix)) { + fprintf(stderr, "Failed to create image file %s\n", image_filename); return EXIT_FAILURE; } diff --git a/src/dex_file.cc b/src/dex_file.cc index c532134094..d437a3fc9c 100644 --- a/src/dex_file.cc +++ b/src/dex_file.cc @@ -475,17 +475,25 @@ void DexFile::InitIndex() { for (size_t i = 0; i < NumClassDefs(); ++i) { const ClassDef& class_def = GetClassDef(i); const char* descriptor = GetClassDescriptor(class_def); - index_[descriptor] = &class_def; + index_[descriptor] = i; } } -const DexFile::ClassDef* DexFile::FindClassDef(const StringPiece& descriptor) const { +bool DexFile::FindClassDefIndex(const StringPiece& descriptor, uint32_t& idx) const { Index::const_iterator it = index_.find(descriptor); if (it == index_.end()) { - return NULL; - } else { - return it->second; + return false; + } + idx = it->second; + return true; +} + +const DexFile::ClassDef* DexFile::FindClassDef(const StringPiece& descriptor) const { + uint32_t idx; + if (FindClassDefIndex(descriptor, idx)) { + return &GetClassDef(idx); } + return NULL; } // Materializes the method descriptor for a method prototype. Method diff --git a/src/dex_file.h b/src/dex_file.h index 4ad701f96b..e313d74bdb 100644 --- a/src/dex_file.h +++ b/src/dex_file.h @@ -359,6 +359,9 @@ class DexFile { return *header_; } + // Looks up a class definition index by its class descriptor. + bool FindClassDefIndex(const StringPiece& descriptor, uint32_t& idx) const; + // Looks up a class definition by its class descriptor. const ClassDef* FindClassDef(const StringPiece& descriptor) const; @@ -909,8 +912,8 @@ class DexFile { // Returns true if the header magic is of the expected value. bool IsMagicValid(); - // The index of descriptors to class definitions. - typedef std::map<const StringPiece, const DexFile::ClassDef*> Index; + // The index of descriptors to class definition indexes. + typedef std::map<const StringPiece, uint32_t> Index; Index index_; // The base address of the memory mapping. diff --git a/src/dex_file_test.cc b/src/dex_file_test.cc index 4271d0c826..3a9b6f4dc5 100644 --- a/src/dex_file_test.cc +++ b/src/dex_file_test.cc @@ -2,8 +2,6 @@ #include "dex_file.h" -#include <stdio.h> - #include "UniquePtr.h" #include "common_test.h" diff --git a/src/exception_test.cc b/src/exception_test.cc index ee6c1a0d05..2e9cf3feb6 100644 --- a/src/exception_test.cc +++ b/src/exception_test.cc @@ -75,19 +75,21 @@ class ExceptionTest : public CommonTest { ByteArray* fake_code = ByteArray::Alloc(12); ASSERT_TRUE(fake_code != NULL); IntArray* fake_mapping_data = IntArray::Alloc(2); - ASSERT_TRUE(fake_mapping_data!= NULL); + ASSERT_TRUE(fake_mapping_data != NULL); fake_mapping_data->Set(0, 3); // offset 3 fake_mapping_data->Set(1, 3); // maps to dex offset 3 method_f_ = my_klass_->FindVirtualMethod("f", "()I"); ASSERT_TRUE(method_f_ != NULL); method_f_->SetFrameSizeInBytes(kStackAlignment); method_f_->SetReturnPcOffsetInBytes(kStackAlignment-kPointerSize); - method_f_->SetCode(fake_code, kThumb2, fake_mapping_data); + method_f_->SetCodeArray(fake_code, kThumb2); + method_f_->SetMappingTable(fake_mapping_data); method_g_ = my_klass_->FindVirtualMethod("g", "(I)V"); ASSERT_TRUE(method_g_ != NULL); method_g_->SetFrameSizeInBytes(kStackAlignment); method_g_->SetReturnPcOffsetInBytes(kStackAlignment-kPointerSize); - method_g_->SetCode(fake_code, kThumb2, fake_mapping_data); + method_g_->SetCodeArray(fake_code, kThumb2); + method_g_->SetMappingTable(fake_mapping_data); } UniquePtr<const DexFile> dex_; diff --git a/src/globals.h b/src/globals.h index 2fb0225f38..e5fead698b 100644 --- a/src/globals.h +++ b/src/globals.h @@ -38,6 +38,9 @@ const int kStackAlignment = 16; // Required object alignment const int kObjectAlignment = 8; +// Required ARM instruction alignment +const int kArmAlignment = 4; + // System page size. Normally you're expected to get this from // sysconf(_SC_PAGESIZE) or some system-specific define (usually // PAGESIZE or PAGE_SIZE). If we use a simple compile-time constant diff --git a/src/heap.cc b/src/heap.cc index 015f6381f9..2051051ab5 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -74,7 +74,9 @@ void Heap::Init(size_t initial_size, size_t maximum_size, LOG(FATAL) << "Failed to create space from " << boot_image_file_name; } spaces_.push_back(boot_space); - requested_base = boot_space->GetBase() + RoundUp(boot_space->Size(), kPageSize); + byte* oat_limit_addr = boot_space->GetImageHeader().GetOatLimitAddr(); + requested_base = reinterpret_cast<byte*>(RoundUp(reinterpret_cast<uintptr_t>(oat_limit_addr), + kPageSize)); } std::vector<Space*> image_spaces; @@ -85,7 +87,9 @@ void Heap::Init(size_t initial_size, size_t maximum_size, } image_spaces.push_back(space); spaces_.push_back(space); - requested_base = space->GetBase() + RoundUp(space->Size(), kPageSize); + byte* oat_limit_addr = space->GetImageHeader().GetOatLimitAddr(); + requested_base = reinterpret_cast<byte*>(RoundUp(reinterpret_cast<uintptr_t>(oat_limit_addr), + kPageSize)); } Space* space = Space::Create(initial_size, maximum_size, requested_base); diff --git a/src/image.cc b/src/image.cc index 5bf7b10fcb..c610c8a131 100644 --- a/src/image.cc +++ b/src/image.cc @@ -4,7 +4,7 @@ namespace art { -const byte ImageHeader::kImageMagic[] = { 'o', 'a', 't', '\n' }; +const byte ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; const byte ImageHeader::kImageVersion[] = { '0', '0', '1', '\0' }; } // namespace art diff --git a/src/image.h b/src/image.h index fdf6443ad6..3d2f5f4d70 100644 --- a/src/image.h +++ b/src/image.h @@ -11,12 +11,25 @@ namespace art { // header of image files written by ImageWriter, read and validated by Space. -class ImageHeader { +class PACKED ImageHeader { public: ImageHeader() {} - ImageHeader(uint32_t base_addr, uint32_t image_roots) - : base_addr_(base_addr), image_roots_(image_roots) { + ImageHeader(uint32_t image_base_addr, + uint32_t image_roots, + uint32_t oat_checksum, + uint32_t oat_base_addr, + uint32_t oat_limit_addr) + : image_base_addr_(image_base_addr), + oat_checksum_(oat_checksum), + oat_base_addr_(oat_base_addr), + oat_limit_addr_(oat_limit_addr), + image_roots_(image_roots) { + CHECK_EQ(image_base_addr, RoundUp(image_base_addr, kPageSize)); + CHECK_EQ(oat_base_addr, RoundUp(oat_base_addr, kPageSize)); + CHECK_LT(image_base_addr, image_roots); + CHECK_LT(image_roots, oat_base_addr); + CHECK_LT(oat_base_addr, oat_limit_addr); memcpy(magic_, kImageMagic, sizeof(kImageMagic)); memcpy(version_, kImageVersion, sizeof(kImageVersion)); } @@ -36,13 +49,27 @@ class ImageHeader { return reinterpret_cast<const char*>(magic_); } - byte* GetBaseAddr() const { - return reinterpret_cast<byte*>(base_addr_); + byte* GetImageBaseAddr() const { + return reinterpret_cast<byte*>(image_base_addr_); + } + + uint32_t GetOatChecksum() const { + return oat_checksum_; + } + + byte* GetOatBaseAddr() const { + return reinterpret_cast<byte*>(oat_base_addr_); + } + + byte* GetOatLimitAddr() const { + return reinterpret_cast<byte*>(oat_limit_addr_); } enum ImageRoot { kJniStubArray, + kAbstractMethodErrorStubArray, kCalleeSaveMethod, + kOatLocation, kImageRootsMax, }; @@ -58,7 +85,16 @@ class ImageHeader { byte version_[4]; // required base address for mapping the image. - uint32_t base_addr_; + uint32_t image_base_addr_; + + // checksum of the oat file we link to for load time sanity check + uint32_t oat_checksum_; + + // required oat address expected by image Method::GetCode() pointers. + uint32_t oat_base_addr_; + + // end of oat address range for this image file, used for positioning a following image + uint32_t oat_limit_addr_; // absolute address of an Object[] of objects needed to reinitialize from an image uint32_t image_roots_; diff --git a/src/image_test.cc b/src/image_test.cc index 7ecdf3cb1f..2fd5c34169 100644 --- a/src/image_test.cc +++ b/src/image_test.cc @@ -7,6 +7,7 @@ #include "file.h" #include "image.h" #include "image_writer.h" +#include "oat_writer.h" #include "signal_catcher.h" #include "space.h" #include "utils.h" @@ -16,22 +17,19 @@ namespace art { class ImageTest : public CommonTest {}; TEST_F(ImageTest, WriteRead) { - // TODO: remove the touching of classes, call Compiler instead - for (size_t i = 0; i < java_lang_dex_file_->NumClassDefs(); i++) { - const DexFile::ClassDef& class_def = java_lang_dex_file_->GetClassDef(i); - const char* descriptor = java_lang_dex_file_->GetClassDescriptor(class_def); - Class* klass = class_linker_->FindSystemClass(descriptor); - ASSERT_TRUE(klass != NULL) << descriptor; - } + ScratchFile tmp_oat; + bool success_oat = OatWriter::Create(tmp_oat.GetFilename(), NULL); + ASSERT_TRUE(success_oat); ImageWriter writer; - ScratchFile tmp; + ScratchFile tmp_image; const uintptr_t image_base = 0x50000000; - bool success = writer.Write(tmp.GetFilename(), image_base); - ASSERT_TRUE(success); + bool success_image = writer.Write(tmp_image.GetFilename(), image_base, + std::string(tmp_oat.GetFilename()), ""); + ASSERT_TRUE(success_image); { - UniquePtr<File> file(OS::OpenFile(tmp.GetFilename(), false)); + UniquePtr<File> file(OS::OpenFile(tmp_image.GetFilename(), false)); ASSERT_TRUE(file.get() != NULL); ImageHeader image_header; file->ReadFully(&image_header, sizeof(image_header)); @@ -58,8 +56,11 @@ TEST_F(ImageTest, WriteRead) { Runtime::Options options; options.push_back(std::make_pair("bootclasspath", &boot_class_path)); + std::string boot_oat("-Xbootoat:"); + boot_oat.append(tmp_oat.GetFilename()); + options.push_back(std::make_pair(boot_oat.c_str(), reinterpret_cast<void*>(NULL))); std::string boot_image("-Xbootimage:"); - boot_image.append(tmp.GetFilename()); + boot_image.append(tmp_image.GetFilename()); options.push_back(std::make_pair(boot_image.c_str(), reinterpret_cast<void*>(NULL))); runtime_.reset(Runtime::Create(options, false)); diff --git a/src/image_writer.cc b/src/image_writer.cc index 23bc6cf8db..37eac2ebe4 100644 --- a/src/image_writer.cc +++ b/src/image_writer.cc @@ -23,7 +23,8 @@ namespace art { -bool ImageWriter::Write(const char* filename, uintptr_t image_base) { +bool ImageWriter::Write(const char* image_filename, uintptr_t image_base, + const std::string& oat_filename, const std::string& strip_location_prefix) { CHECK_NE(image_base, 0U); image_base_ = reinterpret_cast<byte*>(image_base); @@ -32,6 +33,12 @@ bool ImageWriter::Write(const char* filename, uintptr_t image_base) { CHECK_GE(spaces.size(), 1U); source_space_ = spaces[spaces.size()-1]; + oat_file_.reset(OatFile::Open(oat_filename, strip_location_prefix, NULL)); + if (oat_file_.get() == NULL) { + LOG(ERROR) << "Failed to open oat file " << oat_filename; + return false; + } + if (!Init()) { return false; } @@ -39,11 +46,17 @@ bool ImageWriter::Write(const char* filename, uintptr_t image_base) { CalculateNewObjectOffsets(); CopyAndFixupObjects(); - UniquePtr<File> file(OS::OpenFile(filename, true)); + UniquePtr<File> file(OS::OpenFile(image_filename, true)); if (file.get() == NULL) { + LOG(ERROR) << "Failed to open image file " << image_filename; return false; } - return file->WriteFully(image_->GetAddress(), image_top_); + bool success = file->WriteFully(image_->GetAddress(), image_top_); + if (!success) { + PLOG(ERROR) << "Failed to write image file " << image_filename; + return false; + } + return true; } bool ImageWriter::Init() { @@ -52,6 +65,7 @@ bool ImageWriter::Init() { size_t length = RoundUp(size, kPageSize); image_.reset(MemMap::Map(length, prot)); if (image_.get() == NULL) { + LOG(ERROR) << "Failed to allocate memory for image file generation"; return false; } return true; @@ -94,20 +108,29 @@ void ImageWriter::CalculateNewObjectOffsetsCallback(Object* obj, void* arg) { if (dex_cache != NULL) { image_writer->dex_caches_.insert(dex_cache); } else { - DCHECK(klass->IsArrayClass() || klass->IsPrimitive()); + DCHECK(klass->IsArrayClass() || klass->IsPrimitive()) << PrettyClass(klass); } } } -ObjectArray<Object>* CreateImageRoots() { +ObjectArray<Object>* ImageWriter::CreateImageRoots() const { // build a Object[] of the roots needed to restore the runtime Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); Class* object_array_class = class_linker->FindSystemClass("[Ljava/lang/Object;"); ObjectArray<Object>* image_roots = ObjectArray<Object>::Alloc(object_array_class, ImageHeader::kImageRootsMax); - image_roots->Set(ImageHeader::kJniStubArray, runtime->GetJniStubArray()); - image_roots->Set(ImageHeader::kCalleeSaveMethod, runtime->GetCalleeSaveMethod()); + image_roots->Set(ImageHeader::kJniStubArray, + runtime->GetJniStubArray()); + image_roots->Set(ImageHeader::kAbstractMethodErrorStubArray, + runtime->GetAbstractMethodErrorStubArray()); + image_roots->Set(ImageHeader::kCalleeSaveMethod, + runtime->GetCalleeSaveMethod()); + image_roots->Set(ImageHeader::kOatLocation, + String::AllocFromModifiedUtf8(oat_file_->GetLocation().c_str())); + for (int i = 0; i < ImageHeader::kImageRootsMax; i++) { + CHECK(image_roots->Get(i) != NULL); + } return image_roots; } @@ -125,12 +148,17 @@ void ImageWriter::CalculateNewObjectOffsets() { heap_bitmap->Walk(CalculateNewObjectOffsetsCallback, this); // TODO: add Space-limited Walk DCHECK_LT(image_top_, image_->GetLength()); + // Note that image_top_ is left at end of used space + oat_base_ = image_base_ + RoundUp(image_top_, kPageSize); + byte* oat_limit = oat_base_ + oat_file_->GetSize(); + // return to write header at start of image with future location of image_roots ImageHeader image_header(reinterpret_cast<uint32_t>(image_base_), - reinterpret_cast<uint32_t>(GetImageAddress(image_roots))); + reinterpret_cast<uint32_t>(GetImageAddress(image_roots)), + oat_file_->GetOatHeader().GetChecksum(), + reinterpret_cast<uint32_t>(oat_base_), + reinterpret_cast<uint32_t>(oat_limit)); memcpy(image_->GetAddress(), &image_header, sizeof(image_header)); - - // Note that top_ is left at end of used space } void ImageWriter::CopyAndFixupObjects() { @@ -199,14 +227,30 @@ const void* FixupCode(const ByteArray* copy_code_array, const void* orig_code) { void ImageWriter::FixupMethod(const Method* orig, Method* copy) { FixupInstanceFields(orig, copy); - copy->code_ = FixupCode(copy->code_array_, orig->code_); + + // OatWriter clears the code_array_ after writing the code. + // It replaces the code_ with an offset value we now adjust to be a pointer. + DCHECK(copy->code_array_ == NULL) + << PrettyMethod(orig) + << " orig_code_array_=" << orig->GetCodeArray() << " orig_code_=" << orig->GetCode() + << " copy_code_array_=" << copy->code_array_ << " orig_code_=" << copy->code_ + << " jni_stub=" << Runtime::Current()->GetJniStubArray() + << " ame_stub=" << Runtime::Current()->GetAbstractMethodErrorStubArray(); copy->invoke_stub_ = reinterpret_cast<Method::InvokeStub*>(FixupCode(copy->invoke_stub_array_, reinterpret_cast<void*>(orig->invoke_stub_))); if (orig->IsNative()) { ByteArray* orig_jni_stub_array_ = Runtime::Current()->GetJniStubArray(); ByteArray* copy_jni_stub_array_ = down_cast<ByteArray*>(GetImageAddress(orig_jni_stub_array_)); copy->native_method_ = copy_jni_stub_array_->GetData(); + copy->code_ = oat_base_ + orig->GetOatCodeOffset(); } else { - DCHECK(copy->native_method_ == NULL); + DCHECK(copy->native_method_ == NULL) << copy->native_method_; + if (orig->IsAbstract()) { + ByteArray* orig_ame_stub_array_ = Runtime::Current()->GetAbstractMethodErrorStubArray(); + ByteArray* copy_ame_stub_array_ = down_cast<ByteArray*>(GetImageAddress(orig_ame_stub_array_)); + copy->code_ = copy_ame_stub_array_->GetData(); + } else { + copy->code_ = oat_base_ + orig->GetOatCodeOffset(); + } } } diff --git a/src/image_writer.h b/src/image_writer.h index c1480ff329..8c4b308f8e 100644 --- a/src/image_writer.h +++ b/src/image_writer.h @@ -10,6 +10,7 @@ #include "UniquePtr.h" #include "dex_cache.h" #include "mem_map.h" +#include "oat_file.h" #include "object.h" #include "os.h" #include "space.h" @@ -21,7 +22,8 @@ class ImageWriter { public: ImageWriter() : source_space_(NULL), image_top_(0), image_base_(NULL) {}; - bool Write(const char* filename, uintptr_t image_base); + bool Write(const char* image_filename, uintptr_t image_base, + const std::string& oat_filename, const std::string& strip_location_prefix); ~ImageWriter() {}; private: @@ -82,6 +84,7 @@ class ImageWriter { } void CalculateNewObjectOffsets(); + ObjectArray<Object>* CreateImageRoots() const; static void CalculateNewObjectOffsetsCallback(Object* obj, void* arg); void CopyAndFixupObjects(); @@ -97,6 +100,9 @@ class ImageWriter { void FixupDexCaches(); void FixupDexCache(const DexCache* orig, DexCache* copy); + // oat file with code for this image + UniquePtr<OatFile> oat_file_; + // Space we are writing objects from const Space* source_space_; @@ -106,9 +112,12 @@ class ImageWriter { // Offset to the free space in image_ size_t image_top_; - // Target base address for the output image + // Target image base address for the output image byte* image_base_; + // Target oat base address for the pointers from the output image to its oat file + byte* oat_base_; + // DexCaches seen while scanning for fixing up CodeAndDirectMethods typedef std::tr1::unordered_set<DexCache*, DexCacheHash> Set; Set dex_caches_; diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc index 6c09f58866..c8cfc66f8e 100644 --- a/src/jni_compiler.cc +++ b/src/jni_compiler.cc @@ -430,7 +430,7 @@ void JniCompiler::Compile(Method* native_method) { CHECK(managed_code != NULL); MemoryRegion code(managed_code->GetData(), managed_code->GetLength()); __ FinalizeInstructions(code); - native_method->SetCode(managed_code, instruction_set_); + native_method->SetCodeArray(managed_code, instruction_set_); native_method->SetFrameSizeInBytes(frame_size); native_method->SetReturnPcOffsetInBytes(jni_conv->ReturnPcOffset()); native_method->SetCoreSpillMask(jni_conv->CoreSpillMask()); diff --git a/src/mem_map.cc b/src/mem_map.cc index e44a894767..2bd7bd02bb 100644 --- a/src/mem_map.cc +++ b/src/mem_map.cc @@ -87,9 +87,9 @@ void CheckMapRequest(byte* addr, size_t length) { std::string end_str = maps.substr(i+1+8, 8); uint32_t start = ParseHex(start_str); uint32_t end = ParseHex(end_str); - CHECK(!(base >= start && base < end) - && !(limit >= start && limit < end) - && !(base <= start && limit > end)) + CHECK(!(base >= start && base < end) // start of new within old + && !(limit > start && limit < end) // end of new within old + && !(base <= start && limit > end)) // start/end of new includes all of old << StringPrintf("Requested region %08x-%08x overlaps with existing map %08x-%08x\n", base, limit, start, end) << maps; diff --git a/src/oat.cc b/src/oat.cc new file mode 100644 index 0000000000..191f26fb3e --- /dev/null +++ b/src/oat.cc @@ -0,0 +1,68 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +#include "oat.h" + +#include <zlib.h> + +namespace art { + +const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '0', '1', '\0' }; + +OatHeader::OatHeader(const std::vector<const DexFile*>* dex_files) { + memcpy(magic_, kOatMagic, sizeof(kOatMagic)); + memcpy(version_, kOatVersion, sizeof(kOatVersion)); + adler32_checksum_ = adler32(0L, Z_NULL, 0); + dex_file_count_ = dex_files->size(); + UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_)); + executable_offset_ = 0; +} + +bool OatHeader::IsValid() const { + if (memcmp(magic_, kOatMagic, sizeof(kOatMagic) != 0)) { + return false; + } + if (memcmp(version_, kOatVersion, sizeof(kOatVersion) != 0)) { + return false; + } + return true; +} + +const char* OatHeader::GetMagic() const { + CHECK(IsValid()); + return reinterpret_cast<const char*>(magic_); +} + +uint32_t OatHeader::GetDexFileCount() const { + DCHECK(IsValid()); + return dex_file_count_; +} + +uint32_t OatHeader::GetChecksum() const { + CHECK(IsValid()); + return adler32_checksum_; +} + +void OatHeader::UpdateChecksum(const void* data, size_t length) { + DCHECK(IsValid()); + const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data); + adler32_checksum_ = adler32(adler32_checksum_, bytes, length); +} + +uint32_t OatHeader::GetExecutableOffset() const { + DCHECK(IsValid()); + DCHECK(IsAligned(executable_offset_, kPageSize)); + CHECK_GT(executable_offset_, sizeof(OatHeader)); + return executable_offset_; +} + +void OatHeader::SetExecutableOffset(uint32_t executable_offset) { + DCHECK(IsAligned(executable_offset, kPageSize)); + CHECK_GT(executable_offset, sizeof(OatHeader)); + DCHECK(IsValid()); + DCHECK_EQ(executable_offset_, 0U); + executable_offset_ = executable_offset; + UpdateChecksum(&executable_offset_, sizeof(executable_offset)); +} + +} // namespace art diff --git a/src/oat.h b/src/oat.h new file mode 100644 index 0000000000..ca3e9e0e8f --- /dev/null +++ b/src/oat.h @@ -0,0 +1,41 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +#ifndef ART_SRC_OAT_H_ +#define ART_SRC_OAT_H_ + +#include <vector> + +#include "dex_file.h" +#include "macros.h" + +namespace art { + +class PACKED OatHeader { + public: + OatHeader() {} + OatHeader(const std::vector<const DexFile*>* dex_files); + + bool IsValid() const; + const char* GetMagic() const; + uint32_t GetChecksum() const; + void UpdateChecksum(const void* data, size_t length); + uint32_t GetDexFileCount() const; + uint32_t GetExecutableOffset() const; + void SetExecutableOffset(uint32_t executable_offset); + + private: + static const uint8_t kOatMagic[4]; + static const uint8_t kOatVersion[4]; + + uint8_t magic_[4]; + uint8_t version_[4]; + uint32_t adler32_checksum_; + uint32_t dex_file_count_; + uint32_t executable_offset_; + + DISALLOW_COPY_AND_ASSIGN(OatHeader); +}; + +} // namespace art + +#endif // ART_SRC_OAT_H_ diff --git a/src/oat_file.cc b/src/oat_file.cc new file mode 100644 index 0000000000..62664941d0 --- /dev/null +++ b/src/oat_file.cc @@ -0,0 +1,167 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +#include "oat_file.h" + +#include <sys/mman.h> + +#include "file.h" +#include "os.h" +#include "stl_util.h" + +namespace art { + +OatFile* OatFile::Open(const std::string& filename, + const std::string& strip_location_prefix, + byte* requested_base) { + StringPiece location = filename; + if (!location.starts_with(strip_location_prefix)) { + LOG(ERROR) << filename << " does not start with " << strip_location_prefix; + return NULL; + } + location.remove_prefix(strip_location_prefix.size()); + + UniquePtr<OatFile> oat_file(new OatFile(location.ToString())); + bool success = oat_file->Read(filename, requested_base); + if (!success) { + return NULL; + } + return oat_file.release(); +} + +OatFile::OatFile(const std::string& filename) : location_(filename) {} + +OatFile::~OatFile() { + STLDeleteValues(&oat_dex_files_); +} + +bool OatFile::Read(const std::string& filename, byte* requested_base) { + UniquePtr<File> file(OS::OpenFile(filename.c_str(), false)); + if (file.get() == NULL) { + return false; + } + + OatHeader oat_header; + bool success = file->ReadFully(&oat_header, sizeof(oat_header)); + if (!success || !oat_header.IsValid()) { + LOG(WARNING) << "Invalid oat header " << filename; + return false; + } + + UniquePtr<MemMap> map(MemMap::Map(requested_base, + file->Length(), + PROT_READ, + MAP_PRIVATE | ((requested_base != NULL) ? MAP_FIXED : 0), + file->Fd(), + 0)); + if (map.get() == NULL) { + LOG(WARNING) << "Failed to map oat file " << filename; + return false; + } + CHECK(requested_base == 0 || requested_base == map->GetAddress()) << map->GetAddress(); + DCHECK_EQ(0, memcmp(&oat_header, map->GetAddress(), sizeof(OatHeader))); + + off_t code_offset = oat_header.GetExecutableOffset(); + if (code_offset < file->Length()) { + byte* code_address = map->GetAddress() + code_offset; + size_t code_length = file->Length() - code_offset; + if (mprotect(code_address, code_length, PROT_READ | PROT_EXEC) != 0) { + PLOG(ERROR) << "Failed to make oat code executable."; + return false; + } + } else { + // its possible to have no code if all the methods were abstract, native, etc + DCHECK_EQ(code_offset, RoundUp(file->Length(), kPageSize)); + } + + const byte* oat = map->GetAddress(); + oat += sizeof(OatHeader); + CHECK_LT(oat, map->GetLimit()); + for (size_t i = 0; i < oat_header.GetDexFileCount(); i++) { + size_t dex_file_location_size = *reinterpret_cast<const uint32_t*>(oat); + oat += sizeof(dex_file_location_size); + CHECK_LT(oat, map->GetLimit()); + + const char* dex_file_location_data = reinterpret_cast<const char*>(oat); + oat += dex_file_location_size; + CHECK_LT(oat, map->GetLimit()); + + std::string dex_file_location(dex_file_location_data, dex_file_location_size); + + uint32_t dex_file_checksum = *reinterpret_cast<const uint32_t*>(oat); + oat += sizeof(dex_file_checksum); + CHECK_LT(oat, map->GetLimit()); + + uint32_t classes_offset = *reinterpret_cast<const uint32_t*>(oat); + CHECK_GT(classes_offset, 0U); + CHECK_LT(classes_offset, static_cast<uint32_t>(file->Length())); + oat += sizeof(classes_offset); + CHECK_LT(oat, map->GetLimit()); + + uint32_t* classes_pointer = reinterpret_cast<uint32_t*>(map->GetAddress() + classes_offset); + + oat_dex_files_[dex_file_location] = new OatDexFile(this, + dex_file_location, + dex_file_checksum, + classes_pointer); + } + + mem_map_.reset(map.release()); + return true; +} + +const OatHeader& OatFile::GetOatHeader() const { + return *reinterpret_cast<const OatHeader*>(GetBase()); +} + +const byte* OatFile::GetBase() const { + CHECK(mem_map_->GetAddress() != NULL); + return mem_map_->GetAddress(); +} + +const byte* OatFile::GetLimit() const { + CHECK(mem_map_->GetLimit() != NULL); + return mem_map_->GetLimit(); +} + +const OatFile::OatDexFile& OatFile::GetOatDexFile(const DexFile& dex_file) { + Table::const_iterator it = oat_dex_files_.find(dex_file.GetLocation()); + if (it == oat_dex_files_.end()) { + LOG(FATAL) << "Failed to find OatDexFile for DexFile " << dex_file.GetLocation(); + } + return *it->second; +} + +OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, + std::string dex_file_location, + uint32_t dex_file_checksum, + uint32_t* classes_pointer) + : oat_file_(oat_file), + dex_file_location_(dex_file_location), + dex_file_checksum_(dex_file_checksum), + classes_pointer_(classes_pointer) {} + +OatFile::OatDexFile::~OatDexFile() {} + +const OatFile::OatClass OatFile::OatDexFile::GetOatClass(uint32_t class_def_index) const { + uint32_t methods_offset = classes_pointer_[class_def_index]; + const byte* methods_pointer = oat_file_->GetBase() + methods_offset; + CHECK_LT(methods_pointer, oat_file_->GetLimit()); + return OatClass(oat_file_, reinterpret_cast<const uint32_t*>(methods_pointer)); +} + +OatFile::OatClass::OatClass(const OatFile* oat_file, const uint32_t* methods_pointer) + : oat_file_(oat_file), methods_pointer_(methods_pointer) {} + +OatFile::OatClass::~OatClass() {} + +const void* OatFile::OatClass::GetMethodCode(uint32_t method_index) const { + uint32_t code_offset = methods_pointer_[method_index]; + if (code_offset == 0) { + return NULL; + } + const void* code_pointer = reinterpret_cast<const void*>(oat_file_->GetBase() + code_offset); + CHECK_LT(code_pointer, oat_file_->GetLimit()); + return code_pointer; +} + +} // namespace art diff --git a/src/oat_file.h b/src/oat_file.h new file mode 100644 index 0000000000..2528a6c5a1 --- /dev/null +++ b/src/oat_file.h @@ -0,0 +1,101 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +#ifndef ART_SRC_OAT_FILE_H_ +#define ART_SRC_OAT_FILE_H_ + +#include <vector> + +#include "dex_file.h" +#include "mem_map.h" +#include "oat.h" + +namespace art { + +class OatFile { + public: + + // Open an oat file. Returns NULL on failure. Requested base can + // optionally be used to request where the file should be loaded. + static OatFile* Open(const std::string& filename, + const std::string& strip_location_prefix, + byte* requested_base); + + ~OatFile(); + + const std::string& GetLocation() const { + return location_; + } + + const OatHeader& GetOatHeader() const; + + class OatDexFile; + + class OatClass { + public: + // get the code for the method based on its index into the class + // defintion. direct methods come first, followed by virtual + // methods. note that runtime created methods such as miranda + // methods are not included. + const void* GetMethodCode(uint32_t method_index) const; + ~OatClass(); + + private: + OatClass(const OatFile* oat_file, const uint32_t* methods_pointer); + + const OatFile* oat_file_; + const uint32_t* methods_pointer_; + + friend class OatDexFile; + }; + + class OatDexFile { + public: + const OatClass GetOatClass(uint32_t class_def_index) const; + ~OatDexFile(); + private: + OatDexFile(const OatFile* oat_file, + std::string dex_file_location, + uint32_t dex_file_checksum, + uint32_t* classes_pointer); + + const OatFile* oat_file_; + std::string dex_file_location_; + uint32_t dex_file_checksum_; + const uint32_t* classes_pointer_; + + friend class OatFile; + DISALLOW_COPY_AND_ASSIGN(OatDexFile); + }; + + const OatDexFile& GetOatDexFile(const DexFile& dex_file); + + size_t GetSize() const { + return GetLimit() - GetBase(); + } + + private: + OatFile(const std::string& filename); + bool Read(const std::string& filename, byte* requested_base); + + const byte* GetBase() const; + const byte* GetLimit() const; + + // The oat file name. + // + // The image will embed this to link its associated oat file. + const std::string location_; + + // backing memory map for oat file + UniquePtr<MemMap> mem_map_; + + typedef std::map<std::string, const OatDexFile*> Table; + Table oat_dex_files_; + + friend class OatClass; + friend class OatDexFile; + DISALLOW_COPY_AND_ASSIGN(OatFile); +}; + +} // namespace art + +#endif // ART_SRC_OAT_WRITER_H_ diff --git a/src/oat_test.cc b/src/oat_test.cc new file mode 100644 index 0000000000..b1453b35a6 --- /dev/null +++ b/src/oat_test.cc @@ -0,0 +1,79 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +#include "oat_file.h" +#include "oat_writer.h" + +#include "common_test.h" + +namespace art { + +class OatTest : public CommonTest {}; + +TEST_F(OatTest, WriteRead) { + const bool compile = false; // DISABLED_ due to the time to compile libcore + + const ClassLoader* class_loader = NULL; + if (compile) { + compiler_->CompileAll(class_loader); + } + + ScratchFile tmp; + bool success = OatWriter::Create(tmp.GetFilename(), class_loader); + ASSERT_TRUE(success); + + if (compile) { // OatWriter strips the code, regenerate to compare + compiler_->CompileAll(class_loader); + } + UniquePtr<OatFile> oat_file(OatFile::Open(std::string(tmp.GetFilename()), "", NULL)); + ASSERT_TRUE(oat_file.get() != NULL); + const OatHeader& oat_header = oat_file->GetOatHeader(); + ASSERT_EQ(1U, oat_header.GetDexFileCount()); + + const Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + ByteArray* jni_stub_array = runtime->GetJniStubArray(); + ByteArray* ame_stub_array = runtime->GetAbstractMethodErrorStubArray(); + + const DexFile& dex_file = *java_lang_dex_file_.get(); + const OatFile::OatDexFile& oat_dex_file = oat_file->GetOatDexFile(dex_file); + for (size_t i = 0; i < dex_file.NumClassDefs(); i++) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(i); + const byte* class_data = dex_file.GetClassData(class_def); + DexFile::ClassDataHeader header = dex_file.ReadClassDataHeader(&class_data); + size_t num_virtual_methods = header.virtual_methods_size_; + const char* descriptor = dex_file.GetClassDescriptor(class_def); + + const OatFile::OatClass oat_class = oat_dex_file.GetOatClass(i); + + Class* klass = class_linker->FindClass(descriptor, class_loader); + + size_t method_index = 0; + for (size_t i = 0; i < klass->NumDirectMethods(); i++, method_index++) { + Method* method = klass->GetDirectMethod(i); + const void* oat_code = oat_class.GetMethodCode(method_index); + uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(oat_code), 2); + oat_code = reinterpret_cast<const void*>(oat_code_aligned); + const ByteArray* code_array = method->GetCodeArray(); + if (code_array == NULL || code_array == jni_stub_array || code_array == ame_stub_array) { + ASSERT_TRUE(oat_code == NULL); + } else { + ASSERT_EQ(0, memcmp(oat_code, code_array->GetData(), code_array->GetLength())); + } + } + for (size_t i = 0; i < num_virtual_methods; i++, method_index++) { + Method* method = klass->GetVirtualMethod(i); + const void* oat_code = oat_class.GetMethodCode(method_index); + uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(oat_code), 2); + oat_code = reinterpret_cast<const void*>(oat_code_aligned); + const ByteArray* code_array = method->GetCodeArray(); + if (code_array == NULL || code_array == jni_stub_array || code_array == ame_stub_array) { + ASSERT_TRUE(oat_code == NULL); + } else { + ASSERT_TRUE(oat_code != NULL); + ASSERT_EQ(0, memcmp(oat_code, code_array->GetData(), code_array->GetLength())); + } + } + } +} + +} // namespace art diff --git a/src/oat_writer.cc b/src/oat_writer.cc new file mode 100644 index 0000000000..b8232d9d41 --- /dev/null +++ b/src/oat_writer.cc @@ -0,0 +1,438 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +#include "oat_writer.h" + +#include "class_linker.h" +#include "class_loader.h" +#include "file.h" +#include "os.h" +#include "stl_util.h" + +namespace art { + +bool OatWriter::Create(const std::string& filename, const ClassLoader* class_loader) { + const std::vector<const DexFile*>& dex_files = ClassLoader::GetClassPath(class_loader); + OatWriter oat_writer(dex_files, class_loader); + return oat_writer.Write(filename); +} + +OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, const ClassLoader* class_loader) { + class_loader_ = class_loader; + dex_files_ = &dex_files; + + size_t offset = InitOatHeader(); + offset = InitOatDexFiles(offset); + offset = InitOatClasses(offset); + offset = InitOatMethods(offset); + offset = InitOatCode(offset); + offset = InitOatCodeDexFiles(offset); + + CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); + CHECK_EQ(dex_files_->size(), oat_classes_.size()); +} + +size_t OatWriter::InitOatHeader() { + // create the OatHeader + oat_header_ = new OatHeader(dex_files_); + size_t offset = sizeof(*oat_header_); + return offset; +} + +size_t OatWriter::InitOatDexFiles(size_t offset) { + // create the OatDexFiles + for (size_t i = 0; i != dex_files_->size(); ++i) { + const DexFile* dex_file = (*dex_files_)[i]; + CHECK(dex_file != NULL); + OatDexFile* oat_dex_file = new OatDexFile(*dex_file); + oat_dex_files_.push_back(oat_dex_file); + offset += oat_dex_file->SizeOf(); + } + return offset; +} + +size_t OatWriter::InitOatClasses(size_t offset) { + // create the OatClasses + // calculate the offsets within OatDexFiles to OatClasses + for (size_t i = 0; i != dex_files_->size(); ++i) { + // set offset in OatDexFile to OatClasses + oat_dex_files_[i]->classes_offset_ = offset; + oat_dex_files_[i]->UpdateChecksum(*oat_header_); + + const DexFile* dex_file = (*dex_files_)[i]; + OatClasses* oat_classes = new OatClasses(*dex_file); + oat_classes_.push_back(oat_classes); + offset += oat_classes->SizeOf(); + } + return offset; +} + +size_t OatWriter::InitOatMethods(size_t offset) { + // create the OatMethods + // calculate the offsets within OatClasses to OatMethods + size_t class_index = 0; + for (size_t i = 0; i != dex_files_->size(); ++i) { + const DexFile* dex_file = (*dex_files_)[i]; + for (size_t class_def_index = 0; + class_def_index < dex_file->NumClassDefs(); + class_def_index++, class_index++) { + oat_classes_[i]->methods_offsets_[class_def_index] = offset; + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + const byte* class_data = dex_file->GetClassData(class_def); + DexFile::ClassDataHeader header = dex_file->ReadClassDataHeader(&class_data); + size_t num_direct_methods = header.direct_methods_size_; + size_t num_virtual_methods = header.virtual_methods_size_; + uint32_t num_methods = num_direct_methods + num_virtual_methods; + OatMethods* oat_methods = new OatMethods(num_methods); + oat_methods_.push_back(oat_methods); + offset += oat_methods->SizeOf(); + } + oat_classes_[i]->UpdateChecksum(*oat_header_); + } + return offset; +} + +size_t OatWriter::InitOatCode(size_t offset) { + // calculate the offsets within OatHeader to executable code + size_t old_offset = offset; + // required to be on a new page boundary + offset = RoundUp(offset, kPageSize); + oat_header_->SetExecutableOffset(offset); + executable_offset_padding_length_ = offset - old_offset; + return offset; +} + +size_t OatWriter::InitOatCodeDexFiles(size_t offset) { + // calculate the offsets within OatMethods + size_t oat_class_index = 0; + for (size_t i = 0; i != dex_files_->size(); ++i) { + const DexFile* dex_file = (*dex_files_)[i]; + CHECK(dex_file != NULL); + offset = InitOatCodeDexFile(offset, oat_class_index, *dex_file); + } + return offset; +} + +size_t OatWriter::InitOatCodeDexFile(size_t offset, + size_t& oat_class_index, + const DexFile& dex_file) { + for (size_t class_def_index = 0; + class_def_index < dex_file.NumClassDefs(); + class_def_index++, oat_class_index++) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); + offset = InitOatCodeClassDef(offset, oat_class_index, dex_file, class_def); + oat_methods_[oat_class_index]->UpdateChecksum(*oat_header_); + } + return offset; +} + +size_t OatWriter::InitOatCodeClassDef(size_t offset, + size_t oat_class_index, + const DexFile& dex_file, + const DexFile::ClassDef& class_def) { + const byte* class_data = dex_file.GetClassData(class_def); + DexFile::ClassDataHeader header = dex_file.ReadClassDataHeader(&class_data); + size_t num_virtual_methods = header.virtual_methods_size_; + const char* descriptor = dex_file.GetClassDescriptor(class_def); + + // TODO: remove code ByteArrays from Class/Method (and therefore ClassLoader) + // TODO: don't write code for shared stubs + Class* klass = Runtime::Current()->GetClassLinker()->FindClass(descriptor, class_loader_); + CHECK(klass != NULL) << descriptor; + CHECK_EQ(klass->GetClassLoader(), class_loader_); + CHECK_EQ(oat_methods_[oat_class_index]->method_offsets_.size(), + klass->NumDirectMethods() + num_virtual_methods); + // Note that we leave the offset to the code in Method::code_ + size_t class_def_method_index = 0; + for (size_t i = 0; i < klass->NumDirectMethods(); i++, class_def_method_index++) { + Method* method = klass->GetDirectMethod(i); + CHECK(method != NULL) << descriptor << " direct " << i; + offset = InitOatCodeMethod(offset, oat_class_index, class_def_method_index, method); + } + // note that num_virtual_methods != klass->NumVirtualMethods() because of miranda methods + for (size_t i = 0; i < num_virtual_methods; i++, class_def_method_index++) { + Method* method = klass->GetVirtualMethod(i); + CHECK(method != NULL) << descriptor << " virtual " << i; + offset = InitOatCodeMethod(offset, oat_class_index, class_def_method_index, method); + } + return offset; +} + +size_t OatWriter::InitOatCodeMethod(size_t offset, + size_t oat_class_index, + size_t class_def_method_index, + Method* method) { + Runtime* runtime = Runtime::Current(); + ByteArray* jni_stub_array = runtime->GetJniStubArray(); + ByteArray* ame_stub_array = runtime->GetAbstractMethodErrorStubArray(); + + const ByteArray* code_array = method->GetCodeArray(); + if (code_array == NULL || code_array == jni_stub_array || code_array == ame_stub_array) { + oat_methods_[oat_class_index]->method_offsets_[class_def_method_index] = 0; + method->SetOatCodeOffset(0); + } else { + offset = RoundUp(offset, kArmAlignment); + uint32_t thumb_offset = (reinterpret_cast<const int8_t*>(method->GetCode()) + - code_array->GetData()); + uint32_t code_offset = offset + thumb_offset; + oat_methods_[oat_class_index]->method_offsets_[class_def_method_index] = code_offset; + method->SetOatCodeOffset(code_offset); + offset += code_array->GetLength(); + oat_header_->UpdateChecksum(code_array->GetData(), code_array->GetLength()); + } + return offset; +} + +bool OatWriter::Write(const std::string& filename) { + + UniquePtr<File> file(OS::OpenFile(filename.c_str(), true)); + if (file.get() == NULL) { + return false; + } + + if (!file->WriteFully(oat_header_, sizeof(*oat_header_))) { + PLOG(ERROR) << "Failed to write oat header to " << filename; + return false; + } + + if (!WriteTables(file.get())) { + LOG(ERROR) << "Failed to write oat tables to " << filename; + return false; + } + + size_t code_offset = WriteCode(file.get()); + if (code_offset == 0) { + LOG(ERROR) << "Failed to write oat code to " << filename; + return false; + } + + code_offset = WriteCodeDexFiles(file.get(), code_offset); + if (code_offset == 0) { + LOG(ERROR) << "Failed to write oat code for dex files to " << filename; + return false; + } + + return true; +} + +bool OatWriter::WriteTables(File* file) { + for (size_t i = 0; i != oat_dex_files_.size(); ++i) { + if (!oat_dex_files_[i]->Write(file)) { + PLOG(ERROR) << "Failed to write oat dex information"; + return false; + } + } + for (size_t i = 0; i != oat_classes_.size(); ++i) { + if (!oat_classes_[i]->Write(file)) { + PLOG(ERROR) << "Failed to write oat classes information"; + return false; + } + } + for (size_t i = 0; i != oat_methods_.size(); ++i) { + if (!oat_methods_[i]->Write(file)) { + PLOG(ERROR) << "Failed to write oat methods information"; + return false; + } + } + return true; +} + +size_t OatWriter::WriteCode(File* file) { + uint32_t code_offset = oat_header_->GetExecutableOffset(); + off_t new_offset = lseek(file->Fd(), executable_offset_padding_length_, SEEK_CUR); + if (static_cast<uint32_t>(new_offset) != code_offset) { + PLOG(ERROR) << "Failed to seek to oat code section"; + return 0; + } + return code_offset; +} + +size_t OatWriter::WriteCodeDexFiles(File* file, size_t code_offset) { + for (size_t i = 0; i != oat_classes_.size(); ++i) { + const DexFile* dex_file = (*dex_files_)[i]; + CHECK(dex_file != NULL); + code_offset = WriteCodeDexFile(file, code_offset, *dex_file); + if (code_offset == 0) { + return 0; + } + } + return code_offset; +} + +size_t OatWriter::WriteCodeDexFile(File* file, + size_t code_offset, + const DexFile& dex_file) { + for (size_t class_def_index = 0; + class_def_index < dex_file.NumClassDefs(); + class_def_index++) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); + code_offset = WriteCodeClassDef(file, code_offset, dex_file, class_def); + if (code_offset == 0) { + return 0; + } + } + return code_offset; +} + +size_t OatWriter::WriteCodeClassDef(File* file, + size_t code_offset, + const DexFile& dex_file, + const DexFile::ClassDef& class_def) { + const Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + ByteArray* ame_stub_array = runtime->GetAbstractMethodErrorStubArray(); + + const byte* class_data = dex_file.GetClassData(class_def); + DexFile::ClassDataHeader header = dex_file.ReadClassDataHeader(&class_data); + size_t num_virtual_methods = header.virtual_methods_size_; + const char* descriptor = dex_file.GetClassDescriptor(class_def); + Class* klass = class_linker->FindClass(descriptor, class_loader_); + + // TODO: deduplicate code arrays + // Note that we clear the code array here, image_writer will use GetCodeOffset to find it + for (size_t i = 0; i < klass->NumDirectMethods(); i++) { + Method* method = klass->GetDirectMethod(i); + code_offset = WriteCodeMethod(file, code_offset, method); + if (code_offset == 0) { + return 0; + } + } + // note that num_virtual_methods != klass->NumVirtualMethods() because of miranda methods + for (size_t i = 0; i < num_virtual_methods; i++) { + Method* method = klass->GetVirtualMethod(i); + code_offset = WriteCodeMethod(file, code_offset, method); + if (code_offset == 0) { + return 0; + } + } + for (size_t i = num_virtual_methods; i < klass->NumVirtualMethods(); i++) { + Method* method = klass->GetVirtualMethod(i); + const ByteArray* code_array = method->GetCodeArray(); + CHECK(code_array == NULL // if compiler not run + || code_array == ame_stub_array) // otherwise + << PrettyMethod(method) << " " << code_array; + method->SetCodeArray(NULL, kNone); + } + return code_offset; +} + +size_t OatWriter::WriteCodeMethod(File* file, + size_t code_offset, + Method* method) { + const Runtime* runtime = Runtime::Current(); + ByteArray* jni_stub_array = runtime->GetJniStubArray(); + ByteArray* ame_stub_array = runtime->GetAbstractMethodErrorStubArray(); + + const ByteArray* code_array = method->GetCodeArray(); + if (code_array != NULL && code_array != jni_stub_array && code_array != ame_stub_array) { + uint32_t aligned_code_offset = RoundUp(code_offset, kArmAlignment); + uint32_t aligned_code_delta = aligned_code_offset - code_offset; + if (aligned_code_delta != 0) { + off_t new_offset = lseek(file->Fd(), aligned_code_delta, SEEK_CUR); + if (static_cast<uint32_t>(new_offset) != aligned_code_offset) { + PLOG(ERROR) << "Failed to seek to align oat code"; + return false; + } + code_offset += aligned_code_delta; + } + if (!file->WriteFully(code_array->GetData(), code_array->GetLength())) { + PLOG(ERROR) << "Failed to write method code for " << PrettyMethod(method); + return false; + } + code_offset += code_array->GetLength(); + } + // preserve code offset around code clearing + uint32_t offset = method->GetOatCodeOffset(); + method->SetCodeArray(NULL, kNone); + method->SetOatCodeOffset(offset); + return code_offset; +} + +OatWriter::~OatWriter() { + delete oat_header_; + STLDeleteElements(&oat_dex_files_); + STLDeleteElements(&oat_classes_); + STLDeleteElements(&oat_methods_); +} + +OatWriter::OatDexFile::OatDexFile(const DexFile& dex_file) { + const std::string& location = dex_file.GetLocation(); + dex_file_location_size_ = location.size(); + dex_file_location_data_ = reinterpret_cast<const uint8_t*>(location.data()); + dex_file_checksum_ = dex_file.GetHeader().checksum_; +} + +size_t OatWriter::OatDexFile::SizeOf() const { + return sizeof(dex_file_location_size_) + + dex_file_location_size_ + + sizeof(dex_file_checksum_) + + sizeof(classes_offset_); +} + +void OatWriter::OatDexFile::UpdateChecksum(OatHeader& oat_header) const { + oat_header.UpdateChecksum(&dex_file_location_size_, sizeof(dex_file_location_size_)); + oat_header.UpdateChecksum(dex_file_location_data_, dex_file_location_size_); + oat_header.UpdateChecksum(&dex_file_checksum_, sizeof(dex_file_checksum_)); + oat_header.UpdateChecksum(&classes_offset_, sizeof(classes_offset_)); +} + +bool OatWriter::OatDexFile::Write(File* file) const { + if (!file->WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) { + PLOG(ERROR) << "Failed to write dex file location length"; + return false; + } + if (!file->WriteFully(dex_file_location_data_, dex_file_location_size_)) { + PLOG(ERROR) << "Failed to write dex file location data"; + return false; + } + if (!file->WriteFully(&dex_file_checksum_, sizeof(dex_file_checksum_))) { + PLOG(ERROR) << "Failed to write dex file checksum"; + return false; + } + if (!file->WriteFully(&classes_offset_, sizeof(classes_offset_))) { + PLOG(ERROR) << "Failed to write classes offset"; + return false; + } + return true; +} + +OatWriter::OatClasses::OatClasses(const DexFile& dex_file) { + methods_offsets_.resize(dex_file.NumClassDefs()); +} + +size_t OatWriter::OatClasses::SizeOf() const { + return (sizeof(methods_offsets_[0]) * methods_offsets_.size()); +} + +void OatWriter::OatClasses::UpdateChecksum(OatHeader& oat_header) const { + oat_header.UpdateChecksum(&methods_offsets_[0], SizeOf()); +} + +bool OatWriter::OatClasses::Write(File* file) const { + if (!file->WriteFully(&methods_offsets_[0], SizeOf())) { + PLOG(ERROR) << "Failed to methods offsets"; + return false; + } + return true; +} + +OatWriter::OatMethods::OatMethods(uint32_t methods_count) { + method_offsets_.resize(methods_count); +} + +size_t OatWriter::OatMethods::SizeOf() const { + return (sizeof(method_offsets_[0]) * method_offsets_.size()); +} + +void OatWriter::OatMethods::UpdateChecksum(OatHeader& oat_header) const { + oat_header.UpdateChecksum(&method_offsets_[0], SizeOf()); +} + +bool OatWriter::OatMethods::Write(File* file) const { + if (!file->WriteFully(&method_offsets_[0], SizeOf())) { + PLOG(ERROR) << "Failed to method offsets"; + return false; + } + return true; +} + +} // namespace art diff --git a/src/oat_writer.h b/src/oat_writer.h new file mode 100644 index 0000000000..7cef892381 --- /dev/null +++ b/src/oat_writer.h @@ -0,0 +1,155 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +#ifndef ART_SRC_OAT_WRITER_H_ +#define ART_SRC_OAT_WRITER_H_ + +#include <stdint.h> + +#include <cstddef> + +#include "UniquePtr.h" +#include "dex_cache.h" +#include "mem_map.h" +#include "oat.h" +#include "object.h" +#include "os.h" +#include "space.h" + +namespace art { + +// OatHeader fixed length with count of D OatDexFiles +// +// OatDexFile[0] each fixed length with offset to variable sized OatClasses +// OatDexFile[1] +// ... +// OatDexFile[D] +// +// OatClasses[0] one variable sized OatClasses for each OatDexFile +// OatClasses[1] contains DexFile::NumClassDefs offsets to OatMethods for each ClassDef +// ... +// OatClasses[D] +// +// OatMethods[0] one variable sized OatMethods for each of C DexFile::ClassDefs +// OatMethods[1] contains offsets to code +// ... +// OatMethods[C] +// +// padding if necessary so that the follow code will be page aligned +// +// Method::GetCode() one variable sized code blob for each Method::GetCode() value +// Method::GetCode() +// Method::GetCode() +// Method::GetCode() +// Method::GetCode() +// Method::GetCode() +// ... +// Method::GetCode() +// +class OatWriter { + public: + // Write an oat file. Returns true on success, false on failure. + static bool Create(const std::string& filename, const ClassLoader* class_loader); + + private: + + OatWriter(const std::vector<const DexFile*>& dex_files, const ClassLoader* class_loader); + ~OatWriter(); + + size_t InitOatHeader(); + size_t InitOatDexFiles(size_t offset); + size_t InitOatClasses(size_t offset); + size_t InitOatMethods(size_t offset); + size_t InitOatCode(size_t offset); + size_t InitOatCodeDexFiles(size_t offset); + size_t InitOatCodeDexFile(size_t offset, + size_t& oat_class_index, + const DexFile& dex_file); + size_t InitOatCodeClassDef(size_t offset, + size_t oat_class_index, + const DexFile& dex_file, + const DexFile::ClassDef& class_def); + size_t InitOatCodeMethod(size_t offset, + size_t oat_class_index, + size_t class_def_method_index, + Method* method); + + bool Write(const std::string& filename); + bool WriteTables(File* file); + size_t WriteCode(File* file); + size_t WriteCodeDexFiles(File* file, + size_t offset); + size_t WriteCodeDexFile(File* file, + size_t offset, + const DexFile& dex_file); + size_t WriteCodeClassDef(File* file, + size_t offset, + const DexFile& dex_file, + const DexFile::ClassDef& class_def); + size_t WriteCodeMethod(File* file, + size_t offset, + Method* method); + + class OatDexFile { + public: + OatDexFile(const DexFile& dex_file); + size_t SizeOf() const; + void UpdateChecksum(OatHeader& oat_header) const; + bool Write(File* file) const; + + // data to write + uint32_t dex_file_location_size_; + const uint8_t* dex_file_location_data_; + uint32_t dex_file_checksum_; + uint32_t classes_offset_; + + private: + DISALLOW_COPY_AND_ASSIGN(OatDexFile); + }; + + class OatClasses { + public: + OatClasses(const DexFile& dex_file); + size_t SizeOf() const; + void UpdateChecksum(OatHeader& oat_header) const; + bool Write(File* file) const; + + // data to write + std::vector<uint32_t> methods_offsets_; + + private: + DISALLOW_COPY_AND_ASSIGN(OatClasses); + }; + + class OatMethods { + public: + OatMethods(uint32_t methods_count); + size_t SizeOf() const; + void UpdateChecksum(OatHeader& oat_header) const; + bool Write(File* file) const; + + // data to write + std::vector<uint32_t> method_offsets_; + + private: + DISALLOW_COPY_AND_ASSIGN(OatMethods); + }; + + // TODO: remove the ClassLoader when the code storage moves out of Method + const ClassLoader* class_loader_; + + // note OatFile does not take ownership of the DexFiles + const std::vector<const DexFile*>* dex_files_; + + // data to write + OatHeader* oat_header_; + std::vector<OatDexFile*> oat_dex_files_; + std::vector<OatClasses*> oat_classes_; + std::vector<OatMethods*> oat_methods_; + uint32_t executable_offset_padding_length_; + + DISALLOW_COPY_AND_ASSIGN(OatWriter); +}; + +} // namespace art + +#endif // ART_SRC_OAT_WRITER_H_ diff --git a/src/oatdump.cc b/src/oatdump.cc index fe3cf61178..bec8d10669 100644 --- a/src/oatdump.cc +++ b/src/oatdump.cc @@ -22,24 +22,34 @@ namespace art { static void usage() { fprintf(stderr, "Usage: oatdump [options] ...\n" - " Example: oatdump --dex-file=$ANDROID_PRODUCT_OUT/system/framework/core.jar --image=$ANDROID_PRODUCT_OUT/system/framework/boot.oat --strip-prefix=$ANDROID_PRODUCT_OUT\n" - " Example: adb shell oatdump --dex-file=/system/framework/core.jar --image=/system/framework/boot.oat\n" + " Example: oatdump --dex-file=$ANDROID_PRODUCT_OUT/system/framework/core.jar --oat=$ANDROID_PRODUCT_OUT/system/framework/boot.oat --image=$ANDROID_PRODUCT_OUT/system/framework/boot.art --strip-prefix=$ANDROID_PRODUCT_OUT\n" + " Example: adb shell oatdump --dex-file=/system/framework/core.jar --oat=/system/framework/boot.oat --image=/system/framework/boot.art\n" "\n"); - // TODO: remove this by making image contain boot DexFile information? + // TODO: remove this by inferring from --image or --oat fprintf(stderr, " --dex-file=<dex-file>: specifies a .dex file location. At least one .dex\n" " file must be specified. \n" " Example: --dex-file=/system/framework/core.jar\n" "\n"); fprintf(stderr, - " --image=<file>: specifies the required input image filename.\n" + " --image=<file.art>: specifies the required input image filename.\n" + " Example: --image=/system/framework/boot.art\n" + "\n"); + // TODO: remove this by inferring from --image + fprintf(stderr, + " --oat=<file.oat>: specifies the required input oat filename.\n" " Example: --image=/system/framework/boot.oat\n" "\n"); fprintf(stderr, - " --boot=<oat-file>: provide the oat file for the boot class path.\n" - " Example: --boot=/system/framework/boot.oat\n" + " --boot-image=<file.art>: provide the image file for the boot class path.\n" + " Example: --boot-image=/system/framework/boot.art\n" + "\n"); + // TODO: remove this by inferring from --boot-image + fprintf(stderr, + " --boot-oat=<file.oat>: provide the oat file for the boot class path.\n" + " Example: --boot-oat=/system/framework/boot.oat\n" "\n"); - // TODO: remove this by making boot image contain boot DexFile information? + // TODO: remove this by inderring from --boot-image or --boot-oat fprintf(stderr, " --boot-dex-file=<dex-file>: specifies a .dex file that is part of the boot\n" " image specified with --boot. \n" @@ -59,7 +69,9 @@ static void usage() { const char* image_roots_descriptions_[] = { "kJniStubArray", - "kCalleeSaveMethod" + "kAbstractMethodErrorStubArray", + "kCalleeSaveMethod", + "kOatLocation", }; class OatDump { @@ -72,8 +84,11 @@ class OatDump { os << "MAGIC:\n"; os << image_header.GetMagic() << "\n\n"; - os << "BASE:\n"; - os << reinterpret_cast<void*>(image_header.GetBaseAddr()) << "\n\n"; + os << "IMAGE BASE:\n"; + os << reinterpret_cast<void*>(image_header.GetImageBaseAddr()) << "\n\n"; + + os << "OAT BASE:\n"; + os << reinterpret_cast<void*>(image_header.GetOatBaseAddr()) << "\n\n"; os << "ROOTS:\n"; CHECK_EQ(arraysize(image_roots_descriptions_), size_t(ImageHeader::kImageRootsMax)); @@ -94,7 +109,10 @@ class OatDump { os << "STATS:\n" << std::flush; UniquePtr<File> file(OS::OpenFile(image_filename.c_str(), false)); state.stats_.file_bytes = file->Length(); - state.stats_.header_bytes = sizeof(ImageHeader); + size_t header_bytes = sizeof(ImageHeader); + state.stats_.header_bytes = header_bytes; + size_t alignment_bytes = RoundUp(header_bytes, kObjectAlignment) - header_bytes; + state.stats_.alignment_bytes += alignment_bytes; state.stats_.Dump(os); os << std::flush; @@ -167,6 +185,8 @@ class OatDump { } else { state->stats_.managed_code_bytes += code_bytes; } + } else { + code_base = reinterpret_cast<const int8_t*>(method->GetCode()); } StringAppendF(&summary, "\tCODE %p-%p\n", code_base, code_limit); @@ -351,7 +371,9 @@ int oatdump(int argc, char** argv) { std::vector<const char*> dex_filenames; const char* image_filename = NULL; + const char* oat_filename = NULL; const char* boot_image_filename = NULL; + const char* boot_oat_filename = NULL; std::vector<const char*> boot_dex_filenames; std::string strip_location_prefix; std::ostream* os = &std::cout; @@ -363,8 +385,12 @@ int oatdump(int argc, char** argv) { dex_filenames.push_back(option.substr(strlen("--dex-file=")).data()); } else if (option.starts_with("--image=")) { image_filename = option.substr(strlen("--image=")).data(); - } else if (option.starts_with("--boot=")) { - boot_image_filename = option.substr(strlen("--boot=")).data(); + } else if (option.starts_with("--oat=")) { + oat_filename = option.substr(strlen("--oat=")).data(); + } else if (option.starts_with("--boot-image=")) { + boot_image_filename = option.substr(strlen("--boot-image=")).data(); + } else if (option.starts_with("--boot-oat=")) { + boot_oat_filename = option.substr(strlen("--boot-oat=")).data(); } else if (option.starts_with("--boot-dex-file=")) { boot_dex_filenames.push_back(option.substr(strlen("--boot-dex-file=")).data()); } else if (option.starts_with("--strip-prefix=")) { @@ -388,11 +414,21 @@ int oatdump(int argc, char** argv) { return EXIT_FAILURE; } + if (oat_filename == NULL) { + fprintf(stderr, "--oat file name not specified\n"); + return EXIT_FAILURE; + } + if (dex_filenames.empty()) { fprintf(stderr, "no --dex-file values specified\n"); return EXIT_FAILURE; } + if ((boot_image_filename != NULL) != (boot_oat_filename != NULL)) { + fprintf(stderr, "--boot-image and --boat-oat must be specified together or not at all\n"); + return EXIT_FAILURE; + } + if (boot_image_filename != NULL && boot_dex_filenames.empty()) { fprintf(stderr, "no --boot-dex-file values specified with --boot\n"); return EXIT_FAILURE; @@ -406,22 +442,31 @@ int oatdump(int argc, char** argv) { Runtime::Options options; std::string image_option; + std::string oat_option; std::string boot_image_option; + std::string boot_oat_option; if (boot_image_filename == NULL) { // if we don't have multiple images, pass the main one as the boot to match dex2oat boot_image_filename = image_filename; + boot_oat_filename = oat_filename; boot_dex_files = dex_files; dex_files.clear(); } else { image_option += "-Ximage:"; image_option += image_filename; + oat_option += "-Xoat:"; + oat_option += oat_filename; options.push_back(std::make_pair("classpath", &dex_files)); options.push_back(std::make_pair(image_option.c_str(), reinterpret_cast<void*>(NULL))); + options.push_back(std::make_pair(oat_option.c_str(), reinterpret_cast<void*>(NULL))); } boot_image_option += "-Xbootimage:"; boot_image_option += boot_image_filename; + boot_oat_option += "-Xbootoat:"; + boot_oat_option += boot_oat_filename; options.push_back(std::make_pair("bootclasspath", &boot_dex_files)); options.push_back(std::make_pair(boot_image_option.c_str(), reinterpret_cast<void*>(NULL))); + options.push_back(std::make_pair(boot_oat_option.c_str(), reinterpret_cast<void*>(NULL))); UniquePtr<Runtime> runtime(Runtime::Create(options, false)); if (runtime.get() == NULL) { diff --git a/src/object.cc b/src/object.cc index 4a6e649163..8fc4108f7e 100644 --- a/src/object.cc +++ b/src/object.cc @@ -623,7 +623,9 @@ uint32_t Method::ToDexPC(const uintptr_t pc) const { } size_t mapping_table_length = mapping_table->GetLength(); uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(GetCode()); - CHECK_LT(sought_offset, static_cast<uint32_t>(GetCodeArray()->GetLength())); + if (GetCodeArray() != NULL) { + CHECK_LT(sought_offset, static_cast<uint32_t>(GetCodeArray()->GetLength())); + } uint32_t best_offset = 0; uint32_t best_dex_offset = 0; for (size_t i = 0; i < mapping_table_length; i += 2) { @@ -653,7 +655,9 @@ uintptr_t Method::ToNativePC(const uint32_t dex_pc) const { uint32_t map_offset = mapping_table->Get(i); uint32_t map_dex_offset = mapping_table->Get(i + 1); if (map_dex_offset == dex_pc) { - DCHECK_LT(map_offset, static_cast<uint32_t>(GetCodeArray()->GetLength())); + if (GetCodeArray() != NULL) { + DCHECK_LT(map_offset, static_cast<uint32_t>(GetCodeArray()->GetLength())); + } return reinterpret_cast<uintptr_t>(GetCode()) + map_offset; } } @@ -686,25 +690,27 @@ uint32_t Method::FindCatchBlock(Class* exception_type, uint32_t dex_pc) const { return DexFile::kDexNoIndex; } -void Method::SetCode(ByteArray* code_array, InstructionSet instruction_set, - IntArray* mapping_table, ShortArray* vmap_table) { +void Method::SetCodeArray(ByteArray* code_array, InstructionSet instruction_set) { +// TODO: restore this check or warning when compile time code storage is moved out of Method // CHECK(GetCode() == NULL || IsNative()) << PrettyMethod(this); - if (GetCode() != NULL && !IsNative()) { - LOG(WARNING) << "Calling SetCode more than once for " << PrettyMethod(this); - } +// if (GetCode() != NULL && !IsNative()) { +// LOG(WARNING) << "Calling SetCode more than once for " << PrettyMethod(this); +// } SetFieldPtr<ByteArray*>(OFFSET_OF_OBJECT_MEMBER(Method, code_array_), code_array, false); - SetFieldPtr<IntArray*>(OFFSET_OF_OBJECT_MEMBER(Method, mapping_table_), - mapping_table, false); - SetFieldPtr<ShortArray*>(OFFSET_OF_OBJECT_MEMBER(Method, vmap_table_), - vmap_table, false); - int8_t* code = code_array->GetData(); - uintptr_t address = reinterpret_cast<uintptr_t>(code); - if (instruction_set == kThumb2) { - // Set the low-order bit so a BLX will switch to Thumb mode - address |= 0x1; + + void* code; + if (code_array != NULL) { + code = code_array->GetData(); + if (instruction_set == kThumb2) { + uintptr_t address = reinterpret_cast<uintptr_t>(code); + // Set the low-order bit so a BLX will switch to Thumb mode + address |= 0x1; + code = reinterpret_cast<void*>(address); + } + } else { + code = NULL; } - SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, code_), - reinterpret_cast<const void*>(address), false); + SetCode(code); } bool Method::IsWithinCode(uintptr_t pc) const { @@ -717,6 +723,9 @@ bool Method::IsWithinCode(uintptr_t pc) const { #if defined(__arm__) pc &= ~0x1; // clear any possible thumb instruction mode bit #endif + if (GetCodeArray() == NULL) { + return true; + } uint32_t rel_offset = pc - reinterpret_cast<uintptr_t>(GetCodeArray()->GetData()); // Strictly the following test should be a less-than, however, if the last // instruction is a call to an exception throw we may see return addresses diff --git a/src/object.h b/src/object.h index 51d7b84172..763480701c 100644 --- a/src/object.h +++ b/src/object.h @@ -817,8 +817,21 @@ class MANAGED Method : public AccessibleObject { return GetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, code_), false); } - void SetCode(ByteArray* code_array, InstructionSet instruction_set, - IntArray* mapping_table = NULL, ShortArray* vmap_table = NULL); + void SetCode(void* code) { + SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, code_), code, false); + } + + uint32_t GetOatCodeOffset() const { + CHECK(!Runtime::Current()->IsStarted()); + return reinterpret_cast<uint32_t>(GetCode()); + } + + void SetOatCodeOffset(uint32_t code_offset) { + CHECK(!Runtime::Current()->IsStarted()); + SetCode(reinterpret_cast<void*>(code_offset)); + } + + void SetCodeArray(ByteArray* code_array, InstructionSet instruction_set); static MemberOffset GetCodeOffset() { return OFFSET_OF_OBJECT_MEMBER(Method, code_); @@ -828,14 +841,21 @@ class MANAGED Method : public AccessibleObject { bool IsWithinCode(uintptr_t pc) const; IntArray* GetMappingTable() const { - return GetFieldObject<IntArray*>( - OFFSET_OF_OBJECT_MEMBER(Method, mapping_table_), false); + return GetFieldObject<IntArray*>(OFFSET_OF_OBJECT_MEMBER(Method, mapping_table_), false); + } + + void SetMappingTable(IntArray* mapping_table) { + SetFieldPtr<IntArray*>(OFFSET_OF_OBJECT_MEMBER(Method, mapping_table_), mapping_table, false); } ShortArray* GetVMapTable() const { return GetFieldObject<ShortArray*>(OFFSET_OF_OBJECT_MEMBER(Method, vmap_table_), false); } + void SetVMapTable(ShortArray* vmap_table) { + SetFieldPtr<ShortArray*>(OFFSET_OF_OBJECT_MEMBER(Method, vmap_table_), vmap_table, false); + } + size_t GetFrameSizeInBytes() const { DCHECK(sizeof(size_t) == sizeof(uint32_t)); size_t result = GetField32( @@ -1300,8 +1320,7 @@ class MANAGED Class : public StaticStorageBase { uint32_t GetAccessFlags() const; void SetAccessFlags(uint32_t new_access_flags) { - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), new_access_flags, - false); + SetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), new_access_flags, false); } // Returns true if the class is an interface. @@ -1362,8 +1381,7 @@ class MANAGED Class : public StaticStorageBase { void SetPrimitiveType(PrimitiveType new_type) { CHECK(sizeof(PrimitiveType) == sizeof(int32_t)); - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), new_type, - false); + SetField32(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), new_type, false); } // Returns true if the class is a primitive type. @@ -1421,8 +1439,7 @@ class MANAGED Class : public StaticStorageBase { void SetComponentType(Class* new_component_type) { DCHECK(GetComponentType() == NULL); DCHECK(new_component_type != NULL); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, component_type_), - new_component_type, false); + SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, component_type_), new_component_type, false); } size_t GetComponentSize() const { @@ -1483,15 +1500,13 @@ class MANAGED Class : public StaticStorageBase { void SetObjectSize(size_t new_object_size) { DCHECK(!IsVariableSize()); CHECK(sizeof(size_t) == sizeof(int32_t)); - return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), - new_object_size, false); + return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), new_object_size, false); } // Returns true if this class is in the same packages as that class. bool IsInSamePackage(const Class* that) const; - static bool IsInSamePackage(const String* descriptor1, - const String* descriptor2); + static bool IsInSamePackage(const String* descriptor1, const String* descriptor2); // Returns true if this class can access that class. bool CanAccess(const Class* that) const { @@ -1572,8 +1587,7 @@ class MANAGED Class : public StaticStorageBase { OFFSET_OF_OBJECT_MEMBER(Class, super_class_), false); DCHECK(old_super_class == NULL || old_super_class == new_super_class); DCHECK(new_super_class != NULL); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, super_class_), - new_super_class, false); + SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, super_class_), new_super_class, false); } bool HasSuperClass() const { @@ -1587,8 +1601,7 @@ class MANAGED Class : public StaticStorageBase { } void SetSuperClassTypeIdx(int32_t new_super_class_idx) { - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, super_class_type_idx_), - new_super_class_idx, false); + SetField32(OFFSET_OF_OBJECT_MEMBER(Class, super_class_type_idx_), new_super_class_idx, false); } const ClassLoader* GetClassLoader() const; @@ -1761,8 +1774,7 @@ class MANAGED Class : public StaticStorageBase { void SetInterfaces(ObjectArray<Class>* new_interfaces) { DCHECK(NULL == GetFieldObject<Object*>( OFFSET_OF_OBJECT_MEMBER(Class, interfaces_), false)); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, interfaces_), - new_interfaces, false); + SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, interfaces_), new_interfaces, false); } void SetInterface(uint32_t i, Class* f) { // TODO: uint16_t @@ -1799,15 +1811,13 @@ class MANAGED Class : public StaticStorageBase { // Get instance fields ObjectArray<Field>* GetIFields() const { DCHECK(IsLoaded() || IsErroneous()); - return GetFieldObject<ObjectArray<Field>*>( - OFFSET_OF_OBJECT_MEMBER(Class, ifields_), false); + return GetFieldObject<ObjectArray<Field>*>(OFFSET_OF_OBJECT_MEMBER(Class, ifields_), false); } void SetIFields(ObjectArray<Field>* new_ifields) { DCHECK(NULL == GetFieldObject<ObjectArray<Field>*>( OFFSET_OF_OBJECT_MEMBER(Class, ifields_), false)); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, ifields_), - new_ifields, false); + SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, ifields_), new_ifields, false); } size_t NumInstanceFields() const { @@ -1829,27 +1839,23 @@ class MANAGED Class : public StaticStorageBase { size_t NumReferenceInstanceFields() const { DCHECK(IsResolved() || IsErroneous()); DCHECK(sizeof(size_t) == sizeof(int32_t)); - return GetField32( - OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), false); + return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), false); } size_t NumReferenceInstanceFieldsDuringLinking() const { DCHECK(IsLoaded() || IsErroneous()); DCHECK(sizeof(size_t) == sizeof(int32_t)); - return GetField32( - OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), false); + return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), false); } void SetNumReferenceInstanceFields(size_t new_num) { DCHECK(sizeof(size_t) == sizeof(int32_t)); - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), - new_num, false); + SetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), new_num, false); } uint32_t GetReferenceInstanceOffsets() const { DCHECK(IsResolved() || IsErroneous()); - return GetField32( - OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_), false); + return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_), false); } void SetReferenceInstanceOffsets(uint32_t new_reference_offsets); @@ -1863,34 +1869,29 @@ class MANAGED Class : public StaticStorageBase { size_t NumReferenceStaticFields() const { DCHECK(IsResolved() || IsErroneous()); DCHECK(sizeof(size_t) == sizeof(int32_t)); - return GetField32( - OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), false); + return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), false); } size_t NumReferenceStaticFieldsDuringLinking() const { DCHECK(IsLoaded() || IsErroneous()); DCHECK(sizeof(size_t) == sizeof(int32_t)); - return GetField32( - OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), false); + return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), false); } void SetNumReferenceStaticFields(size_t new_num) { DCHECK(sizeof(size_t) == sizeof(int32_t)); - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), - new_num, false); + SetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), new_num, false); } ObjectArray<Field>* GetSFields() const { DCHECK(IsLoaded() || IsErroneous()); - return GetFieldObject<ObjectArray<Field>*>( - OFFSET_OF_OBJECT_MEMBER(Class, sfields_), false); + return GetFieldObject<ObjectArray<Field>*>(OFFSET_OF_OBJECT_MEMBER(Class, sfields_), false); } void SetSFields(ObjectArray<Field>* new_sfields) { DCHECK(NULL == GetFieldObject<ObjectArray<Field>*>( OFFSET_OF_OBJECT_MEMBER(Class, sfields_), false)); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, sfields_), - new_sfields, false); + SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, sfields_), new_sfields, false); } size_t NumStaticFields() const { @@ -1908,8 +1909,7 @@ class MANAGED Class : public StaticStorageBase { } uint32_t GetReferenceStaticOffsets() const { - return GetField32( - OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_), false); + return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_), false); } void SetReferenceStaticOffsets(uint32_t new_reference_offsets); @@ -1930,19 +1930,16 @@ class MANAGED Class : public StaticStorageBase { } void SetClinitThreadId(pid_t new_clinit_thread_id) { - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), - new_clinit_thread_id, false); + SetField32(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), new_clinit_thread_id, false); } Class* GetVerifyErrorClass() const { // DCHECK(IsErroneous()); - return GetFieldObject<Class*>( - OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), false); + return GetFieldObject<Class*>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), false); } void SetVerifyErrorClass(Class* klass) { - klass->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), - klass, false); + klass->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false); } String* GetSourceFile() const; diff --git a/src/runtime.cc b/src/runtime.cc index 0dc52296a7..6cde08227b 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -10,9 +10,13 @@ #include "UniquePtr.h" #include "class_linker.h" #include "heap.h" +#include "image.h" #include "intern_table.h" #include "jni_internal.h" +#include "oat_file.h" #include "signal_catcher.h" +#include "space.h" +#include "stl_util.h" #include "thread.h" #include "thread_list.h" @@ -32,6 +36,7 @@ Runtime::Runtime() signal_catcher_(NULL), java_vm_(NULL), jni_stub_array_(NULL), + abstract_method_error_stub_array_(NULL), callee_save_method_(NULL), started_(false), vfprintf_(NULL), @@ -48,6 +53,7 @@ Runtime::~Runtime() { // Make sure all other non-daemon threads have terminated, and all daemon threads are suspended. delete thread_list_; + STLDeleteElements(&oat_files_); delete class_linker_; Heap::Destroy(); delete intern_table_; @@ -186,6 +192,7 @@ void CreateClassPath(const std::string& class_path, Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, bool ignore_unrecognized) { UniquePtr<ParsedOptions> parsed(new ParsedOptions()); parsed->boot_image_ = NULL; + parsed->boot_oat_ = NULL; #ifdef NDEBUG // -Xcheck:jni is off by default for regular builds... parsed->check_jni_ = false; @@ -244,10 +251,15 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b const StringPiece& value = options[i].first; parsed->class_path_string_ = value.data(); } else if (option.starts_with("-Xbootimage:")) { - // TODO: remove when intern_addr_ is removed, just use -Ximage: + // TODO: remove and just use -Ximage: parsed->boot_image_ = option.substr(strlen("-Xbootimage:")).data(); + } else if (option.starts_with("-Xbootoat:")) { + // TODO: remove and just use -Xoat: + parsed->boot_oat_ = option.substr(strlen("-Xbootoat:")).data(); } else if (option.starts_with("-Ximage:")) { parsed->images_.push_back(option.substr(strlen("-Ximage:")).data()); + } else if (option.starts_with("-Xoat:")) { + parsed->oats_.push_back(option.substr(strlen("-Xoat:")).data()); } else if (option.starts_with("-Xcheck:jni")) { parsed->check_jni_ = true; } else if (option.starts_with("-Xms")) { @@ -402,7 +414,7 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { UniquePtr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized)); if (options.get() == NULL) { - LOG(WARNING) << "Failed to parse options"; + LOG(ERROR) << "Failed to parse options"; return false; } verbose_startup_ = options->IsVerbose("startup"); @@ -424,7 +436,7 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { intern_table_ = new InternTable; Heap::Init(options->heap_initial_size_, options->heap_maximum_size_, - options->boot_image_, options->images_); + options->boot_image_, options->images_); BlockSignals(); @@ -439,7 +451,19 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { class_linker_ = ClassLinker::Create(options->boot_class_path_, options->class_path_, intern_table_, - Heap::GetBootSpace()); + Heap::GetBootSpace() != NULL); + if (Heap::GetBootSpace() != NULL) { + if (!OpenOat(Heap::GetBootSpace(), options->boot_oat_)) { + LOG(ERROR) << "Failed to open boot oat " << options->boot_oat_; + return false; + } + } + for (size_t i = 0; i < options->oats_.size(); i++) { + if (!OpenOat(Heap::GetSpaces()[i+1], options->oats_[i])) { + LOG(ERROR) << "Failed to open oat " << options->oats_[i]; + return false; + } + } if (IsVerboseStartup()) { LOG(INFO) << "Runtime::Init exiting"; @@ -447,6 +471,49 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { return true; } +bool Runtime::OpenOat(const Space* space, const char* oat) { + if (IsVerboseStartup()) { + LOG(INFO) << "Runtime::OpenOat entering " << oat; + } + // TODO: check in ParsedOptions? + if (space == NULL) { + LOG(ERROR) << "oat specified without image"; + return false; + } + if (oat == NULL) { + LOG(ERROR) << "image specified without oat"; + return false; + } + const ImageHeader& image_header = space->GetImageHeader(); + String* oat_location = image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString(); + std::string oat_filename = oat_location->ToModifiedUtf8(); + if (!StringPiece(oat).ends_with(oat_filename)) { + LOG(ERROR) << "oat file name " << oat + << " does not match value found in image: " << oat_filename; + return false; + } + // TODO: we should be able to just use oat_filename instead of oat + // if we passed the prefix argument to find it on the host during cross compilation. + OatFile* oat_file = OatFile::Open(std::string(oat), "", image_header.GetOatBaseAddr()); + if (oat_file == NULL) { + LOG(ERROR) << "Failed to open oat file " << oat_filename << " referenced from image"; + return false; + } + uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum(); + uint32_t image_oat_checksum = image_header.GetOatChecksum(); + if (oat_checksum != image_oat_checksum) { + LOG(ERROR) << "Failed to match oat filechecksum " << std::hex << oat_checksum + << " to expected oat checksum " << std::hex << oat_checksum + << " in image"; + return false; + } + oat_files_.push_back(oat_file); + if (IsVerboseStartup()) { + LOG(INFO) << "Runtime::OpenOat exiting"; + } + return true; +} + void Runtime::InitNativeMethods() { if (IsVerboseStartup()) { LOG(INFO) << "Runtime::InitNativeMethods entering"; @@ -598,13 +665,58 @@ void Runtime::DetachCurrentThread() { thread_list_->Unregister(); } +void Runtime::VisitRoots(Heap::RootVisitor* visitor, void* arg) const { + class_linker_->VisitRoots(visitor, arg); + intern_table_->VisitRoots(visitor, arg); + java_vm_->VisitRoots(visitor, arg); + thread_list_->VisitRoots(visitor, arg); + visitor(jni_stub_array_, arg); + visitor(abstract_method_error_stub_array_, arg); + visitor(callee_save_method_, arg); + + //(*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg); + //(*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg); + //(*visitor)(&gDvm.noClassDefFoundErrorObj, 0, ROOT_VM_INTERNAL, arg); + UNIMPLEMENTED(WARNING) << "some roots not marked"; +} + +bool Runtime::HasJniStubArray() const { + return jni_stub_array_ != NULL; +} + +ByteArray* Runtime::GetJniStubArray() const { + CHECK(jni_stub_array_ != NULL); + return jni_stub_array_; +} + +void Runtime::SetJniStubArray(ByteArray* jni_stub_array) { + CHECK(jni_stub_array != NULL); + CHECK(jni_stub_array_ == NULL || jni_stub_array_ == jni_stub_array); + jni_stub_array_ = jni_stub_array; +} + +bool Runtime::HasAbstractMethodErrorStubArray() const { + return abstract_method_error_stub_array_ != NULL; +} + +ByteArray* Runtime::GetAbstractMethodErrorStubArray() const { + CHECK(abstract_method_error_stub_array_ != NULL); + return abstract_method_error_stub_array_; +} + +void Runtime::SetAbstractMethodErrorStubArray(ByteArray* abstract_method_error_stub_array) { + CHECK(abstract_method_error_stub_array != NULL); + CHECK(abstract_method_error_stub_array_ == NULL || abstract_method_error_stub_array_ == abstract_method_error_stub_array); + abstract_method_error_stub_array_ = abstract_method_error_stub_array; +} + Method* Runtime::CreateCalleeSaveMethod(InstructionSet insns) { Class* method_class = Method::GetMethodClass(); Method* method = down_cast<Method*>(method_class->AllocObject()); method->SetDeclaringClass(method_class); method->SetName(intern_table_->InternStrong("$$$callee_save_method$$$")); method->SetSignature(intern_table_->InternStrong("()V")); - method->SetCode(NULL, insns, NULL); + method->SetCodeArray(NULL, insns); if ((insns == kThumb2) || (insns == kArm)) { size_t frame_size = (12 /* gprs */ + 32 /* fprs */ + 4 /* data */) * kPointerSize; method->SetFrameSizeInBytes(frame_size); @@ -667,18 +779,19 @@ Method* Runtime::CreateCalleeSaveMethod(InstructionSet insns) { return method; } -void Runtime::VisitRoots(Heap::RootVisitor* visitor, void* arg) const { - class_linker_->VisitRoots(visitor, arg); - intern_table_->VisitRoots(visitor, arg); - java_vm_->VisitRoots(visitor, arg); - thread_list_->VisitRoots(visitor, arg); - visitor(jni_stub_array_, arg); - visitor(callee_save_method_, arg); +bool Runtime::HasCalleeSaveMethod() const { + return callee_save_method_ != NULL; +} - //(*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg); - //(*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg); - //(*visitor)(&gDvm.noClassDefFoundErrorObj, 0, ROOT_VM_INTERNAL, arg); - UNIMPLEMENTED(WARNING) << "some roots not marked"; +// Returns a special method that describes all callee saves being spilled to the stack. +Method* Runtime::GetCalleeSaveMethod() const { + CHECK(callee_save_method_ != NULL); + return callee_save_method_; } +void Runtime::SetCalleeSaveMethod(Method* method) { + callee_save_method_ = method; +} + + } // namespace art diff --git a/src/runtime.h b/src/runtime.h index ca0496c3c2..bae5643d80 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -30,6 +30,7 @@ class Heap; class InternTable; class JavaVMExt; class Method; +class OatFile; class SignalCatcher; class String; class ThreadList; @@ -49,7 +50,9 @@ class Runtime { std::string class_path_string_; std::vector<const DexFile*> class_path_; const char* boot_image_; + const char* boot_oat_; std::vector<const char*> images_; + std::vector<const char*> oats_; bool check_jni_; std::string jni_trace_; size_t heap_initial_size_; @@ -145,35 +148,19 @@ class Runtime { void VisitRoots(Heap::RootVisitor* visitor, void* arg) const; - bool HasJniStubArray() const { - return jni_stub_array_ != NULL; - } - - ByteArray* GetJniStubArray() const { - CHECK(jni_stub_array_ != NULL); - return jni_stub_array_; - } - - void SetJniStubArray(ByteArray* jni_stub_array) { - CHECK(jni_stub_array != NULL); - CHECK(jni_stub_array_ == NULL || jni_stub_array_ == jni_stub_array); - jni_stub_array_ = jni_stub_array; - } - - Method* CreateCalleeSaveMethod(InstructionSet insns); + bool HasJniStubArray() const; + ByteArray* GetJniStubArray() const; + void SetJniStubArray(ByteArray* jni_stub_array); - bool HasCalleeSaveMethod() const { - return callee_save_method_ != NULL; - } + bool HasAbstractMethodErrorStubArray() const; + ByteArray* GetAbstractMethodErrorStubArray() const; + void SetAbstractMethodErrorStubArray(ByteArray* abstract_method_error_stub_array); // Returns a special method that describes all callee saves being spilled to the stack. - Method* GetCalleeSaveMethod() const { - return callee_save_method_; - } - - void SetCalleeSaveMethod(Method* method) { - callee_save_method_ = method; - } + Method* CreateCalleeSaveMethod(InstructionSet insns); + bool HasCalleeSaveMethod() const; + Method* GetCalleeSaveMethod() const; + void SetCalleeSaveMethod(Method* method); int32_t GetStat(int kind); @@ -195,6 +182,7 @@ class Runtime { void BlockSignals(); bool Init(const Options& options, bool ignore_unrecognized); + bool OpenOat(const Space* space, const char* boot_oat_); void InitNativeMethods(); void RegisterRuntimeNativeMethods(JNIEnv*); void StartDaemonThreads(); @@ -214,12 +202,16 @@ class Runtime { ClassLinker* class_linker_; + std::vector<const OatFile*> oat_files_; + SignalCatcher* signal_catcher_; JavaVMExt* java_vm_; ByteArray* jni_stub_array_; + ByteArray* abstract_method_error_stub_array_; + Method* callee_save_method_; bool started_; diff --git a/src/space.cc b/src/space.cc index 9efe5995ae..09eb0c9bb9 100644 --- a/src/space.cc +++ b/src/space.cc @@ -114,7 +114,7 @@ bool Space::InitFromImage(const char* image_file_name) { LOG(WARNING) << "Invalid image header " << image_file_name; return false; } - UniquePtr<MemMap> map(MemMap::Map(image_header.GetBaseAddr(), + UniquePtr<MemMap> map(MemMap::Map(image_header.GetImageBaseAddr(), file->Length(), // TODO: selectively PROT_EXEC when image contains a code space PROT_READ | PROT_WRITE | PROT_EXEC, @@ -125,13 +125,16 @@ bool Space::InitFromImage(const char* image_file_name) { LOG(WARNING) << "Failed to map " << image_file_name; return false; } - CHECK_EQ(image_header.GetBaseAddr(), map->GetAddress()); + CHECK_EQ(image_header.GetImageBaseAddr(), map->GetAddress()); image_header_ = reinterpret_cast<ImageHeader*>(map->GetAddress()); DCHECK_EQ(0, memcmp(&image_header, image_header_, sizeof(ImageHeader))); Object* jni_stub_array = image_header.GetImageRoot(ImageHeader::kJniStubArray); Runtime::Current()->SetJniStubArray(down_cast<ByteArray*>(jni_stub_array)); + Object* ame_stub_array = image_header.GetImageRoot(ImageHeader::kAbstractMethodErrorStubArray); + Runtime::Current()->SetAbstractMethodErrorStubArray(down_cast<ByteArray*>(ame_stub_array)); + Object* callee_save_method = image_header.GetImageRoot(ImageHeader::kCalleeSaveMethod); Runtime::Current()->SetCalleeSaveMethod(down_cast<Method*>(callee_save_method)); diff --git a/src/stub_arm.cc b/src/stub_arm.cc index a35bfe2622..75d60f553c 100644 --- a/src/stub_arm.cc +++ b/src/stub_arm.cc @@ -36,6 +36,7 @@ ByteArray* CreateAbstractMethodErrorStub() { size_t cs = assembler->CodeSize(); ByteArray* abstract_stub = ByteArray::Alloc(cs); CHECK(abstract_stub != NULL); + CHECK(abstract_stub->GetClass()->GetDescriptor()); MemoryRegion code(abstract_stub->GetData(), abstract_stub->GetLength()); assembler->FinalizeInstructions(code); diff --git a/src/utils.cc b/src/utils.cc index b47ee2cd1c..b5063ffeb6 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -48,6 +48,9 @@ std::string GetIsoDate() { } std::string PrettyDescriptor(const String* java_descriptor) { + if (java_descriptor == NULL) { + return "null"; + } std::string descriptor(java_descriptor->ToModifiedUtf8()); // Count the number of '['s to get the dimensionality. diff --git a/test/003-omnibus-opcodes/build b/test/003-omnibus-opcodes/build index 5a8a1a0549..bbd392f5c4 100644 --- a/test/003-omnibus-opcodes/build +++ b/test/003-omnibus-opcodes/build @@ -27,7 +27,9 @@ zip ${ANDROID_PRODUCT_OUT}/system/framework/test.jar classes.dex dex2oatd -Xms16m -Xmx16m \ --boot-dex-file=${ANDROID_PRODUCT_OUT}/system/framework/core.jar \ - --boot=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \ + --boot-oat=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \ + --boot-image=${ANDROID_PRODUCT_OUT}/system/framework/core.art \ --dex-file=${ANDROID_PRODUCT_OUT}/system/framework/test.jar \ - --image=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \ + --oat=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \ + --image=${ANDROID_PRODUCT_OUT}/system/framework/test.art \ --strip-prefix=${ANDROID_PRODUCT_OUT} diff --git a/test/023-many-interfaces/build b/test/023-many-interfaces/build index a2b7eea268..118e178031 100644 --- a/test/023-many-interfaces/build +++ b/test/023-many-interfaces/build @@ -29,7 +29,9 @@ zip ${ANDROID_PRODUCT_OUT}/system/framework/test.jar classes.dex dex2oatd -Xms16m -Xmx16m \ --boot-dex-file=${ANDROID_PRODUCT_OUT}/system/framework/core.jar \ - --boot=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \ + --boot-oat=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \ + --boot-image=${ANDROID_PRODUCT_OUT}/system/framework/core.art \ --dex-file=${ANDROID_PRODUCT_OUT}/system/framework/test.jar \ - --image=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \ + --oat=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \ + --image=${ANDROID_PRODUCT_OUT}/system/framework/test.art \ --strip-prefix=${ANDROID_PRODUCT_OUT} diff --git a/test/056-const-string-jumbo/build b/test/056-const-string-jumbo/build index 98f27b8db6..c39779b268 100644 --- a/test/056-const-string-jumbo/build +++ b/test/056-const-string-jumbo/build @@ -48,7 +48,9 @@ zip ${ANDROID_PRODUCT_OUT}/system/framework/test.jar classes.dex dex2oatd -Xms16m -Xmx16m \ --boot-dex-file=${ANDROID_PRODUCT_OUT}/system/framework/core.jar \ - --boot=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \ + --boot-oat=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \ + --boot-image=${ANDROID_PRODUCT_OUT}/system/framework/core.art \ --dex-file=${ANDROID_PRODUCT_OUT}/system/framework/test.jar \ - --image=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \ + --oat=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \ + --image=${ANDROID_PRODUCT_OUT}/system/framework/test.art \ --strip-prefix=${ANDROID_PRODUCT_OUT} diff --git a/test/085-old-style-inner-class/build b/test/085-old-style-inner-class/build index 6f41a0de1b..5a1b04c916 100644 --- a/test/085-old-style-inner-class/build +++ b/test/085-old-style-inner-class/build @@ -30,7 +30,9 @@ zip ${ANDROID_PRODUCT_OUT}/system/framework/test.jar classes.dex dex2oatd -Xms16m -Xmx16m \ --boot-dex-file=${ANDROID_PRODUCT_OUT}/system/framework/core.jar \ - --boot=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \ + --boot-oat=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \ + --boot-image=${ANDROID_PRODUCT_OUT}/system/framework/core.art \ --dex-file=${ANDROID_PRODUCT_OUT}/system/framework/test.jar \ - --image=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \ + --oat=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \ + --image=${ANDROID_PRODUCT_OUT}/system/framework/test.art \ --strip-prefix=${ANDROID_PRODUCT_OUT} diff --git a/test/etc/default-build b/test/etc/default-build index dcc372a655..e31e0ead3c 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -30,9 +30,11 @@ zip ${ANDROID_PRODUCT_OUT}/system/framework/test.jar classes.dex dex2oatd -Xms16m -Xmx16m \ --boot-dex-file=${ANDROID_PRODUCT_OUT}/system/framework/core.jar \ - --boot=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \ + --boot-oat=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \ + --boot-image=${ANDROID_PRODUCT_OUT}/system/framework/core.art \ --dex-file=${ANDROID_PRODUCT_OUT}/system/framework/test.jar \ - --image=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \ + --oat=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \ + --image=${ANDROID_PRODUCT_OUT}/system/framework/test.art \ --strip-prefix=${ANDROID_PRODUCT_OUT} if [ -r src-ex ]; then @@ -50,8 +52,10 @@ if [ -r src-ex ]; then dex2oatd -Xms16m -Xmx16m \ --boot-dex-file=${ANDROID_PRODUCT_OUT}/system/framework/core.jar \ - --boot=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \ + --boot-oat=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \ + --boot-image=${ANDROID_PRODUCT_OUT}/system/framework/core.art \ --dex-file=${ANDROID_PRODUCT_OUT}/system/framework/test-ex.jar \ - --image=${ANDROID_PRODUCT_OUT}/system/framework/test-ex.oat \ + --oat=${ANDROID_PRODUCT_OUT}/system/framework/test-ex.oat \ + --image=${ANDROID_PRODUCT_OUT}/system/framework/test-ex.art \ --strip-prefix=${ANDROID_PRODUCT_OUT} fi diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar index 8b490cd1ed..fd96b6b735 100755 --- a/test/etc/push-and-run-test-jar +++ b/test/etc/push-and-run-test-jar @@ -108,13 +108,17 @@ msg "------------------------------" if [ "$QUIET" = "n" ]; then adb push ${ANDROID_PRODUCT_OUT}/system/framework/test.jar /system/framework adb push ${ANDROID_PRODUCT_OUT}/system/framework/test.oat /system/framework + adb push ${ANDROID_PRODUCT_OUT}/system/framework/test.art /system/framework adb push ${ANDROID_PRODUCT_OUT}/system/framework/test-ex.jar /system/framework adb push ${ANDROID_PRODUCT_OUT}/system/framework/test-ex.oat /system/framework + adb push ${ANDROID_PRODUCT_OUT}/system/framework/test-ex.art /system/framework else adb push ${ANDROID_PRODUCT_OUT}/system/framework/test.jar /system/framework >/dev/null 2>&1 adb push ${ANDROID_PRODUCT_OUT}/system/framework/test.oat /system/framework >/dev/null 2>&1 + adb push ${ANDROID_PRODUCT_OUT}/system/framework/test.art /system/framework >/dev/null 2>&1 adb push ${ANDROID_PRODUCT_OUT}/system/framework/test-ex.jar /system/framework >/dev/null 2>&1 adb push ${ANDROID_PRODUCT_OUT}/system/framework/test-ex.oat /system/framework >/dev/null 2>&1 + adb push ${ANDROID_PRODUCT_OUT}/system/framework/test-ex.art /system/framework >/dev/null 2>&1 fi if [ "$DEBUG" = "y" ]; then @@ -130,10 +134,13 @@ fi if [ "$ZYGOTE" = "y" ]; then adb shell cd /data \; dvz -classpath test.jar Main "$@" else - cmdline="cd /data; oatexecd -Xbootclasspath:/system/framework/core.jar \ - -Xbootimage:/system/framework/core.oat \ + cmdline="cd /data; oatexecd \ + -Xbootclasspath:/system/framework/core.jar \ + -Xbootoat:/system/framework/core.oat \ + -Xbootimage:/system/framework/core.art \ -classpath /system/framework/test.jar \ - -Ximage:/system/framework/test.oat Main" + -Xoat:/system/framework/test.oat \ + -Ximage:/system/framework/test.art Main" #cmdline="cd /data; dalvikvm $DEX_VERIFY $DEX_OPTIMIZE $DEX_DEBUG \ # $GC_OPTS -cp test.jar -Xint:${INTERP} -ea Main" if [ "$DEV_MODE" = "y" ]; then |