summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/codegen.go5
-rw-r--r--compiler/Android.bp24
-rw-r--r--compiler/common_compiler_test.cc4
-rw-r--r--compiler/dex/dex_to_dex_decompiler_test.cc2
-rw-r--r--compiler/driver/compiler_options.cc5
-rw-r--r--compiler/driver/compiler_options.h29
-rw-r--r--compiler/jit/jit_compiler.cc11
-rw-r--r--compiler/optimizing/code_generator_vector_arm64.cc4
-rw-r--r--compiler/optimizing/code_generator_vector_arm_vixl.cc2
-rw-r--r--compiler/optimizing/code_generator_vector_mips.cc4
-rw-r--r--compiler/optimizing/code_generator_vector_mips64.cc4
-rw-r--r--compiler/optimizing/code_generator_vector_x86.cc33
-rw-r--r--compiler/optimizing/code_generator_vector_x86_64.cc33
-rw-r--r--compiler/optimizing/inliner.cc28
-rw-r--r--compiler/optimizing/intrinsics_arm_vixl.cc8
-rw-r--r--compiler/optimizing/nodes_vector.h10
-rw-r--r--compiler/optimizing/nodes_vector_test.cc6
-rw-r--r--compiler/optimizing/optimizing_compiler.cc21
-rw-r--r--compiler/optimizing/optimizing_compiler.h7
-rw-r--r--compiler/verifier_deps_test.cc2
-rw-r--r--dex2oat/Android.bp12
-rw-r--r--dex2oat/dex2oat.cc23
-rw-r--r--dex2oat/linker/arm/relative_patcher_thumb2_test.cc46
-rw-r--r--dex2oat/linker/arm64/relative_patcher_arm64_test.cc367
-rw-r--r--dex2oat/linker/image_test.h3
-rw-r--r--dex2oat/linker/image_writer.cc222
-rw-r--r--dex2oat/linker/image_writer.h24
-rw-r--r--dex2oat/linker/oat_writer.cc22
-rw-r--r--dex2oat/linker/relative_patcher_test.h43
-rw-r--r--disassembler/Android.bp6
-rw-r--r--libartbase/Android.bp21
-rw-r--r--libartbase/base/membarrier.cc1
-rw-r--r--libartbase/libartbase.map15
-rw-r--r--libdexfile/dex/dex_instruction.h2
-rw-r--r--libdexfile/dex/dex_instruction_list.h6
-rw-r--r--libdexfile/dex/dex_instruction_test.cc2
-rw-r--r--oatdump/Android.bp6
-rw-r--r--runtime/Android.bp7
-rw-r--r--runtime/class_linker.cc112
-rw-r--r--runtime/class_linker.h9
-rw-r--r--runtime/dexopt_test.cc2
-rw-r--r--runtime/hidden_api.cc238
-rw-r--r--runtime/hidden_api.h206
-rw-r--r--runtime/hidden_api_test.cc69
-rw-r--r--runtime/interpreter/mterp/arm/other.S6
-rw-r--r--runtime/interpreter/mterp/arm64/other.S4
-rw-r--r--runtime/interpreter/mterp/x86/invoke.S6
-rw-r--r--runtime/interpreter/mterp/x86/main.S5
-rw-r--r--runtime/interpreter/mterp/x86/other.S14
-rw-r--r--runtime/interpreter/mterp/x86_64/invoke.S6
-rw-r--r--runtime/interpreter/mterp/x86_64/main.S5
-rw-r--r--runtime/interpreter/mterp/x86_64/other.S14
-rw-r--r--runtime/interpreter/unstarted_runtime.cc21
-rw-r--r--runtime/jit/jit_code_cache.cc20
-rw-r--r--runtime/jit/jit_code_cache.h25
-rw-r--r--runtime/jni/jni_internal.cc28
-rw-r--r--runtime/mirror/class-inl.h10
-rw-r--r--runtime/mirror/class.cc8
-rw-r--r--runtime/mirror/class.h1
-rw-r--r--runtime/native/dalvik_system_ZygoteHooks.cc26
-rw-r--r--runtime/native/java_lang_Class.cc88
-rw-r--r--runtime/runtime.cc8
-rw-r--r--runtime/runtime.h16
-rw-r--r--runtime/well_known_classes.cc4
-rw-r--r--runtime/well_known_classes.h1
-rw-r--r--simulator/Android.bp4
-rw-r--r--test/674-hiddenapi/hiddenapi.cc12
-rw-r--r--test/674-hiddenapi/src-ex/ChildClass.java130
-rw-r--r--test/688-shared-library/check21
-rw-r--r--test/688-shared-library/expected.txt0
-rw-r--r--test/688-shared-library/info.txt2
-rw-r--r--test/688-shared-library/src-art/Main.java194
-rw-r--r--test/688-shared-library/src-ex/Main.java18
-rw-r--r--test/688-shared-library/src-ex/SharedLibraryOne.java18
-rw-r--r--test/Android.bp3
-rw-r--r--test/knownfailures.json1
-rwxr-xr-xtest/testrunner/run_build_test_target.py15
-rw-r--r--test/testrunner/target_config.py7
-rwxr-xr-xtools/build_linux_bionic_tests.sh1
-rwxr-xr-xtools/buildbot-build.sh3
80 files changed, 1398 insertions, 1053 deletions
diff --git a/build/codegen.go b/build/codegen.go
index 8526bf192b..d0db78e571 100644
--- a/build/codegen.go
+++ b/build/codegen.go
@@ -107,8 +107,9 @@ func codegen(ctx android.LoadHookContext, c *codegenProperties, library bool) {
}
type CodegenCommonArchProperties struct {
- Srcs []string
- Cflags []string
+ Srcs []string
+ Cflags []string
+ Cppflags []string
}
type CodegenLibraryArchProperties struct {
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 30a65b280a..0d92b05593 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -183,7 +183,6 @@ art_cc_defaults {
shared_libs: [
"libbase",
"libcutils", // for atrace.
- "liblzma",
],
include_dirs: ["art/disassembler"],
header_libs: [
@@ -199,7 +198,6 @@ cc_defaults {
static_libs: [
"libbase",
"libcutils",
- "liblzma",
],
}
@@ -233,12 +231,12 @@ art_cc_library {
// VIXL assembly support for ARM targets.
static: {
whole_static_libs: [
- "libvixl-arm",
+ "libvixl",
],
},
shared: {
shared_libs: [
- "libvixl-arm",
+ "libvixl",
],
},
},
@@ -246,12 +244,12 @@ art_cc_library {
// VIXL assembly support for ARM64 targets.
static: {
whole_static_libs: [
- "libvixl-arm64",
+ "libvixl",
],
},
shared: {
shared_libs: [
- "libvixl-arm64",
+ "libvixl",
],
},
},
@@ -295,12 +293,12 @@ art_cc_library {
// VIXL assembly support for ARM targets.
static: {
whole_static_libs: [
- "libvixld-arm",
+ "libvixld",
],
},
shared: {
shared_libs: [
- "libvixld-arm",
+ "libvixld",
],
},
},
@@ -308,12 +306,12 @@ art_cc_library {
// VIXL assembly support for ARM64 targets.
static: {
whole_static_libs: [
- "libvixld-arm64",
+ "libvixld",
],
},
shared: {
shared_libs: [
- "libvixld-arm64",
+ "libvixld",
],
},
},
@@ -454,8 +452,7 @@ art_cc_test {
"libprofiled",
"libartd-compiler",
"libartd-simulator-container",
- "libvixld-arm",
- "libvixld-arm64",
+ "libvixld",
"libbacktrace",
"libnativeloader",
@@ -512,7 +509,6 @@ art_cc_test {
},
shared_libs: [
"libartd-compiler",
- "libvixld-arm",
- "libvixld-arm64",
+ "libvixld",
],
}
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 599f4aa189..66421e2562 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -184,7 +184,7 @@ void CommonCompilerTest::OverrideInstructionSetFeatures(InstructionSet instructi
void CommonCompilerTest::CreateCompilerDriver() {
ApplyInstructionSet();
- compiler_options_->boot_image_ = true;
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImage;
compiler_options_->compile_pic_ = false; // Non-PIC boot image is a test configuration.
compiler_options_->SetCompilerFilter(GetCompilerFilter());
compiler_options_->image_classes_.swap(*GetImageClasses());
@@ -345,7 +345,7 @@ void CommonCompilerTest::SetDexFilesForOatFile(const std::vector<const DexFile*>
}
void CommonCompilerTest::ClearBootImageOption() {
- compiler_options_->boot_image_ = false;
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
}
} // namespace art
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index 4f83d605a3..f61e6c4848 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -41,7 +41,7 @@ class DexToDexDecompilerTest : public CommonCompilerTest {
void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
- compiler_options_->boot_image_ = false;
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
compiler_options_->SetCompilerFilter(CompilerFilter::kQuicken);
// Create the main VerifierDeps, here instead of in the compiler since we want to aggregate
// the results for all the dex files, not just the results for the current dex file.
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index be8e10e41e..3610f1831b 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -44,9 +44,8 @@ CompilerOptions::CompilerOptions()
no_inline_from_(),
dex_files_for_oat_file_(),
image_classes_(),
- boot_image_(false),
- core_image_(false),
- app_image_(false),
+ image_type_(ImageType::kNone),
+ compiling_with_core_image_(false),
baseline_(false),
debuggable_(false),
generate_debug_info_(kDefaultGenerateDebugInfo),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 77f84820e5..12fa251606 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -58,6 +58,12 @@ class CompilerOptions final {
static const size_t kDefaultInlineMaxCodeUnits = 32;
static constexpr size_t kUnsetInlineMaxCodeUnits = -1;
+ enum class ImageType : uint8_t {
+ kNone, // JIT or AOT app compilation producing only an oat file but no image.
+ kBootImage, // Creating boot image.
+ kAppImage, // Creating app image.
+ };
+
CompilerOptions();
~CompilerOptions();
@@ -191,27 +197,23 @@ class CompilerOptions final {
// Are we compiling a boot image?
bool IsBootImage() const {
- return boot_image_;
+ return image_type_ == ImageType::kBootImage;
}
bool IsBaseline() const {
return baseline_;
}
- // Are we compiling a core image (small boot image only used for ART testing)?
- bool IsCoreImage() const {
- // Ensure that `core_image_` => `boot_image_`.
- DCHECK(!core_image_ || boot_image_);
- return core_image_;
- }
-
// Are we compiling an app image?
bool IsAppImage() const {
- return app_image_;
+ return image_type_ == ImageType::kAppImage;
}
- void DisableAppImage() {
- app_image_ = false;
+ // Returns whether we are compiling against a "core" image, which
+ // is an indicative we are running tests. The compiler will use that
+ // information for checking invariants.
+ bool CompilingWithCoreImage() const {
+ return compiling_with_core_image_;
}
// Should the code be compiled as position independent?
@@ -356,9 +358,8 @@ class CompilerOptions final {
// Must not be empty for real boot image, only for tests pretending to compile boot image.
HashSet<std::string> image_classes_;
- bool boot_image_;
- bool core_image_;
- bool app_image_;
+ ImageType image_type_;
+ bool compiling_with_core_image_;
bool baseline_;
bool debuggable_;
bool generate_debug_info_;
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index f22f61fa21..bb35065921 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -90,10 +90,11 @@ JitCompiler::JitCompiler() {
// Special case max code units for inlining, whose default is "unset" (implictly
// meaning no limit). Do this before parsing the actual passed options.
compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits);
+ Runtime* runtime = Runtime::Current();
{
std::string error_msg;
- if (!compiler_options_->ParseCompilerOptions(Runtime::Current()->GetCompilerOptions(),
- true /* ignore_unrecognized */,
+ if (!compiler_options_->ParseCompilerOptions(runtime->GetCompilerOptions(),
+ /*ignore_unrecognized=*/ true,
&error_msg)) {
LOG(FATAL) << error_msg;
UNREACHABLE();
@@ -103,7 +104,7 @@ JitCompiler::JitCompiler() {
compiler_options_->SetNonPic();
// Set debuggability based on the runtime value.
- compiler_options_->SetDebuggable(Runtime::Current()->IsJavaDebuggable());
+ compiler_options_->SetDebuggable(runtime->IsJavaDebuggable());
const InstructionSet instruction_set = compiler_options_->GetInstructionSet();
if (kRuntimeISA == InstructionSet::kArm) {
@@ -112,7 +113,7 @@ JitCompiler::JitCompiler() {
DCHECK_EQ(instruction_set, kRuntimeISA);
}
std::unique_ptr<const InstructionSetFeatures> instruction_set_features;
- for (const StringPiece option : Runtime::Current()->GetCompilerOptions()) {
+ for (const StringPiece option : runtime->GetCompilerOptions()) {
VLOG(compiler) << "JIT compiler option " << option;
std::string error_msg;
if (option.starts_with("--instruction-set-variant=")) {
@@ -144,6 +145,8 @@ JitCompiler::JitCompiler() {
instruction_set_features = InstructionSetFeatures::FromCppDefines();
}
compiler_options_->instruction_set_features_ = std::move(instruction_set_features);
+ compiler_options_->compiling_with_core_image_ =
+ CompilerDriver::IsCoreImageFilename(runtime->GetImageLocation());
compiler_driver_.reset(new CompilerDriver(
compiler_options_.get(),
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc
index e79a96bc2a..5a18c1f72b 100644
--- a/compiler/optimizing/code_generator_vector_arm64.cc
+++ b/compiler/optimizing/code_generator_vector_arm64.cc
@@ -216,7 +216,7 @@ void InstructionCodeGeneratorARM64::VisitVecReduce(HVecReduce* instruction) {
switch (instruction->GetPackedType()) {
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ Addv(dst.S(), src.V4S());
break;
@@ -230,7 +230,7 @@ void InstructionCodeGeneratorARM64::VisitVecReduce(HVecReduce* instruction) {
break;
case DataType::Type::kInt64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ Addp(dst.D(), src.V2D());
break;
diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc
index 62b6c4ea01..b092961a56 100644
--- a/compiler/optimizing/code_generator_vector_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc
@@ -138,7 +138,7 @@ void InstructionCodeGeneratorARMVIXL::VisitVecReduce(HVecReduce* instruction) {
switch (instruction->GetPackedType()) {
case DataType::Type::kInt32:
DCHECK_EQ(2u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ Vpadd(DataTypeValue::I32, dst, src, src);
break;
diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc
index 24f4fb2d7b..09e96cc1f4 100644
--- a/compiler/optimizing/code_generator_vector_mips.cc
+++ b/compiler/optimizing/code_generator_vector_mips.cc
@@ -187,7 +187,7 @@ void InstructionCodeGeneratorMIPS::VisitVecReduce(HVecReduce* instruction) {
switch (instruction->GetPackedType()) {
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ Hadd_sD(tmp, src, src);
__ IlvlD(dst, tmp, tmp);
@@ -209,7 +209,7 @@ void InstructionCodeGeneratorMIPS::VisitVecReduce(HVecReduce* instruction) {
break;
case DataType::Type::kInt64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ IlvlD(dst, src, src);
__ AddvD(dst, dst, src);
diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc
index 972c49ebb1..b6873b1a67 100644
--- a/compiler/optimizing/code_generator_vector_mips64.cc
+++ b/compiler/optimizing/code_generator_vector_mips64.cc
@@ -185,7 +185,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecReduce(HVecReduce* instruction) {
switch (instruction->GetPackedType()) {
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ Hadd_sD(tmp, src, src);
__ IlvlD(dst, tmp, tmp);
@@ -207,7 +207,7 @@ void InstructionCodeGeneratorMIPS64::VisitVecReduce(HVecReduce* instruction) {
break;
case DataType::Type::kInt64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ IlvlD(dst, src, src);
__ AddvD(dst, dst, src);
diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc
index c52ecc77c5..0ee00356b9 100644
--- a/compiler/optimizing/code_generator_vector_x86.cc
+++ b/compiler/optimizing/code_generator_vector_x86.cc
@@ -205,8 +205,8 @@ void LocationsBuilderX86::VisitVecReduce(HVecReduce* instruction) {
CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
// Long reduction or min/max require a temporary.
if (instruction->GetPackedType() == DataType::Type::kInt64 ||
- instruction->GetKind() == HVecReduce::kMin ||
- instruction->GetKind() == HVecReduce::kMax) {
+ instruction->GetReductionKind() == HVecReduce::kMin ||
+ instruction->GetReductionKind() == HVecReduce::kMax) {
instruction->GetLocations()->AddTemp(Location::RequiresFpuRegister());
}
}
@@ -218,38 +218,23 @@ void InstructionCodeGeneratorX86::VisitVecReduce(HVecReduce* instruction) {
switch (instruction->GetPackedType()) {
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ movaps(dst, src);
__ phaddd(dst, dst);
__ phaddd(dst, dst);
break;
- case HVecReduce::kMin: {
- XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
- __ movaps(tmp, src);
- __ movaps(dst, src);
- __ psrldq(tmp, Immediate(8));
- __ pminsd(dst, tmp);
- __ psrldq(tmp, Immediate(4));
- __ pminsd(dst, tmp);
- break;
- }
- case HVecReduce::kMax: {
- XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
- __ movaps(tmp, src);
- __ movaps(dst, src);
- __ psrldq(tmp, Immediate(8));
- __ pmaxsd(dst, tmp);
- __ psrldq(tmp, Immediate(4));
- __ pmaxsd(dst, tmp);
- break;
- }
+ case HVecReduce::kMin:
+ case HVecReduce::kMax:
+ // Historical note: We've had a broken implementation here. b/117863065
+ // Do not draw on the old code if we ever want to bring MIN/MAX reduction back.
+ LOG(FATAL) << "Unsupported reduction type.";
}
break;
case DataType::Type::kInt64: {
DCHECK_EQ(2u, instruction->GetVectorLength());
XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ movaps(tmp, src);
__ movaps(dst, src);
diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc
index 87d0106c3e..9c2882766c 100644
--- a/compiler/optimizing/code_generator_vector_x86_64.cc
+++ b/compiler/optimizing/code_generator_vector_x86_64.cc
@@ -188,8 +188,8 @@ void LocationsBuilderX86_64::VisitVecReduce(HVecReduce* instruction) {
CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
// Long reduction or min/max require a temporary.
if (instruction->GetPackedType() == DataType::Type::kInt64 ||
- instruction->GetKind() == HVecReduce::kMin ||
- instruction->GetKind() == HVecReduce::kMax) {
+ instruction->GetReductionKind() == HVecReduce::kMin ||
+ instruction->GetReductionKind() == HVecReduce::kMax) {
instruction->GetLocations()->AddTemp(Location::RequiresFpuRegister());
}
}
@@ -201,38 +201,23 @@ void InstructionCodeGeneratorX86_64::VisitVecReduce(HVecReduce* instruction) {
switch (instruction->GetPackedType()) {
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ movaps(dst, src);
__ phaddd(dst, dst);
__ phaddd(dst, dst);
break;
- case HVecReduce::kMin: {
- XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
- __ movaps(tmp, src);
- __ movaps(dst, src);
- __ psrldq(tmp, Immediate(8));
- __ pminsd(dst, tmp);
- __ psrldq(tmp, Immediate(4));
- __ pminsd(dst, tmp);
- break;
- }
- case HVecReduce::kMax: {
- XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
- __ movaps(tmp, src);
- __ movaps(dst, src);
- __ psrldq(tmp, Immediate(8));
- __ pmaxsd(dst, tmp);
- __ psrldq(tmp, Immediate(4));
- __ pmaxsd(dst, tmp);
- break;
- }
+ case HVecReduce::kMin:
+ case HVecReduce::kMax:
+ // Historical note: We've had a broken implementation here. b/117863065
+ // Do not draw on the old code if we ever want to bring MIN/MAX reduction back.
+ LOG(FATAL) << "Unsupported reduction type.";
}
break;
case DataType::Type::kInt64: {
DCHECK_EQ(2u, instruction->GetVectorLength());
XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ movaps(tmp, src);
__ movaps(dst, src);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index c1daf95727..45d91675fd 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -39,7 +39,6 @@
#include "mirror/object_array-alloc-inl.h"
#include "mirror/object_array-inl.h"
#include "nodes.h"
-#include "optimizing_compiler.h"
#include "reference_type_propagation.h"
#include "register_allocator_linear_scan.h"
#include "scoped_thread_state_change-inl.h"
@@ -151,13 +150,13 @@ bool HInliner::Run() {
// If we're compiling with a core image (which is only used for
// test purposes), honor inlining directives in method names:
- // - if a method's name contains the substring "$inline$", ensure
- // that this method is actually inlined;
// - if a method's name contains the substring "$noinline$", do not
- // inline that method.
+ // inline that method;
+ // - if a method's name contains the substring "$inline$", ensure
+ // that this method is actually inlined.
// We limit the latter to AOT compilation, as the JIT may or may not inline
// depending on the state of classes at runtime.
- const bool honor_noinline_directives = IsCompilingWithCoreImage();
+ const bool honor_noinline_directives = codegen_->GetCompilerOptions().CompilingWithCoreImage();
const bool honor_inline_directives =
honor_noinline_directives && Runtime::Current()->IsAotCompiler();
@@ -1419,10 +1418,6 @@ size_t HInliner::CountRecursiveCallsOf(ArtMethod* method) const {
static inline bool MayInline(const CompilerOptions& compiler_options,
const DexFile& inlined_from,
const DexFile& inlined_into) {
- if (kIsTargetBuild) {
- return true;
- }
-
// We're not allowed to inline across dex files if we're the no-inline-from dex file.
if (!IsSameDexFile(inlined_from, inlined_into) &&
ContainsElement(compiler_options.GetNoInlineFromDexFile(), &inlined_from)) {
@@ -1737,6 +1732,21 @@ static inline Handle<T> NewHandleIfDifferent(T* object,
return (object != hint.Get()) ? handles->NewHandle(object) : hint;
}
+static bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* callee)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!Runtime::Current()->IsAotCompiler()) {
+ // JIT can always encode methods in stack maps.
+ return true;
+ }
+ if (IsSameDexFile(caller_dex_file, *callee->GetDexFile())) {
+ return true;
+ }
+ // TODO(ngeoffray): Support more AOT cases for inlining:
+ // - methods in multidex
+ // - methods in boot image for on-device non-PIC compilation.
+ return false;
+}
+
bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
ReferenceTypeInfo receiver_type,
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 0c463a280e..4d45a9991c 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -446,7 +446,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMathRint(HInvoke* invoke) {
void IntrinsicCodeGeneratorARMVIXL::VisitMathRint(HInvoke* invoke) {
DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
ArmVIXLAssembler* assembler = GetAssembler();
- __ Vrintn(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
+ __ Vrintn(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
}
void IntrinsicLocationsBuilderARMVIXL::VisitMathRoundFloat(HInvoke* invoke) {
@@ -481,7 +481,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitMathRoundFloat(HInvoke* invoke) {
// Handle input < 0 cases.
// If input is negative but not a tie, previous result (round to nearest) is valid.
// If input is a negative tie, change rounding direction to positive infinity, out_reg += 1.
- __ Vrinta(F32, F32, temp1, in_reg);
+ __ Vrinta(F32, temp1, in_reg);
__ Vmov(temp2, 0.5);
__ Vsub(F32, temp1, in_reg, temp1);
__ Vcmp(F32, temp1, temp2);
@@ -2945,7 +2945,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMathCeil(HInvoke* invoke) {
void IntrinsicCodeGeneratorARMVIXL::VisitMathCeil(HInvoke* invoke) {
ArmVIXLAssembler* assembler = GetAssembler();
DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
- __ Vrintp(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
+ __ Vrintp(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
}
void IntrinsicLocationsBuilderARMVIXL::VisitMathFloor(HInvoke* invoke) {
@@ -2957,7 +2957,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMathFloor(HInvoke* invoke) {
void IntrinsicCodeGeneratorARMVIXL::VisitMathFloor(HInvoke* invoke) {
ArmVIXLAssembler* assembler = GetAssembler();
DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
- __ Vrintm(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
+ __ Vrintm(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
}
void IntrinsicLocationsBuilderARMVIXL::VisitIntegerValueOf(HInvoke* invoke) {
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index 597e399dd1..cd4f45e830 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -384,21 +384,21 @@ class HVecReduce final : public HVecUnaryOperation {
HInstruction* input,
DataType::Type packed_type,
size_t vector_length,
- ReductionKind kind,
+ ReductionKind reduction_kind,
uint32_t dex_pc)
: HVecUnaryOperation(kVecReduce, allocator, input, packed_type, vector_length, dex_pc),
- kind_(kind) {
+ reduction_kind_(reduction_kind) {
DCHECK(HasConsistentPackedTypes(input, packed_type));
}
- ReductionKind GetKind() const { return kind_; }
+ ReductionKind GetReductionKind() const { return reduction_kind_; }
bool CanBeMoved() const override { return true; }
bool InstructionDataEquals(const HInstruction* other) const override {
DCHECK(other->IsVecReduce());
const HVecReduce* o = other->AsVecReduce();
- return HVecOperation::InstructionDataEquals(o) && GetKind() == o->GetKind();
+ return HVecOperation::InstructionDataEquals(o) && GetReductionKind() == o->GetReductionKind();
}
DECLARE_INSTRUCTION(VecReduce);
@@ -407,7 +407,7 @@ class HVecReduce final : public HVecUnaryOperation {
DEFAULT_COPY_CONSTRUCTOR(VecReduce);
private:
- const ReductionKind kind_;
+ const ReductionKind reduction_kind_;
};
// Converts every component in the vector,
diff --git a/compiler/optimizing/nodes_vector_test.cc b/compiler/optimizing/nodes_vector_test.cc
index af13449646..b0a665d704 100644
--- a/compiler/optimizing/nodes_vector_test.cc
+++ b/compiler/optimizing/nodes_vector_test.cc
@@ -401,9 +401,9 @@ TEST_F(NodesVectorTest, VectorKindMattersOnReduce) {
EXPECT_TRUE(v2->CanBeMoved());
EXPECT_TRUE(v3->CanBeMoved());
- EXPECT_EQ(HVecReduce::kSum, v1->GetKind());
- EXPECT_EQ(HVecReduce::kMin, v2->GetKind());
- EXPECT_EQ(HVecReduce::kMax, v3->GetKind());
+ EXPECT_EQ(HVecReduce::kSum, v1->GetReductionKind());
+ EXPECT_EQ(HVecReduce::kMin, v2->GetReductionKind());
+ EXPECT_EQ(HVecReduce::kMax, v3->GetReductionKind());
EXPECT_TRUE(v1->Equals(v1));
EXPECT_TRUE(v2->Equals(v2));
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index c9c1194e5a..fe6abd4999 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -1136,7 +1136,7 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
}
if (kIsDebugBuild &&
- IsCompilingWithCoreImage() &&
+ compiler_driver->GetCompilerOptions().CompilingWithCoreImage() &&
IsInstructionSetSupported(compiler_driver->GetCompilerOptions().GetInstructionSet())) {
// For testing purposes, we put a special marker on method names
// that should be compiled with this compiler (when the
@@ -1234,30 +1234,11 @@ Compiler* CreateOptimizingCompiler(CompilerDriver* driver) {
return new OptimizingCompiler(driver);
}
-bool IsCompilingWithCoreImage() {
- const std::string& image = Runtime::Current()->GetImageLocation();
- return CompilerDriver::IsCoreImageFilename(image);
-}
-
bool EncodeArtMethodInInlineInfo(ArtMethod* method ATTRIBUTE_UNUSED) {
// Note: the runtime is null only for unit testing.
return Runtime::Current() == nullptr || !Runtime::Current()->IsAotCompiler();
}
-bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* callee) {
- if (!Runtime::Current()->IsAotCompiler()) {
- // JIT can always encode methods in stack maps.
- return true;
- }
- if (IsSameDexFile(caller_dex_file, *callee->GetDexFile())) {
- return true;
- }
- // TODO(ngeoffray): Support more AOT cases for inlining:
- // - methods in multidex
- // - methods in boot image for on-device non-PIC compilation.
- return false;
-}
-
bool OptimizingCompiler::JitCompile(Thread* self,
jit::JitCodeCache* code_cache,
ArtMethod* method,
diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h
index 6ee9c70fdb..f5279e83eb 100644
--- a/compiler/optimizing/optimizing_compiler.h
+++ b/compiler/optimizing/optimizing_compiler.h
@@ -29,14 +29,7 @@ class DexFile;
Compiler* CreateOptimizingCompiler(CompilerDriver* driver);
-// Returns whether we are compiling against a "core" image, which
-// is an indicative we are running tests. The compiler will use that
-// information for checking invariants.
-bool IsCompilingWithCoreImage();
-
bool EncodeArtMethodInInlineInfo(ArtMethod* method);
-bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* callee)
- REQUIRES_SHARED(Locks::mutator_lock_);
} // namespace art
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index e1b23cc315..eb44dd7f1d 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -79,7 +79,7 @@ class VerifierDepsTest : public CommonCompilerTest {
}
void SetupCompilerDriver() {
- compiler_options_->boot_image_ = false;
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
compiler_driver_->InitializeThreadPools();
}
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index fd5f3cd9e4..8ce96a4c16 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -71,11 +71,8 @@ art_cc_defaults {
generated_sources: ["art_dex2oat_operator_srcs"],
shared_libs: [
"libbase",
- "liblz4",
- "liblzma",
],
include_dirs: [
- "external/lz4/lib",
"external/zlib",
],
export_include_dirs: ["."],
@@ -98,8 +95,6 @@ cc_defaults {
},
static_libs: [
"libbase",
- "liblz4",
- "liblzma",
],
}
@@ -247,7 +242,6 @@ art_cc_binary {
"libdexfile",
"libartbase",
"libbase",
- "liblz4",
"libsigchain",
],
static_libs: [
@@ -285,7 +279,6 @@ art_cc_binary {
"libdexfiled",
"libartbased",
"libbase",
- "liblz4",
"libsigchain",
],
static_libs: [
@@ -315,7 +308,6 @@ cc_defaults {
],
static_libs: [
"libbase",
- "liblz4",
"libsigchain_dummy",
],
}
@@ -411,11 +403,11 @@ art_cc_test {
"external/zlib",
],
shared_libs: [
- "libprofiled",
+ "libartbased",
"libartd-compiler",
"libartd-dexlayout",
"libbase",
- "liblz4",
+ "libprofiled",
"libsigchain",
"libziparchive",
],
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 77928d3299..12a8354007 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -729,16 +729,15 @@ class Dex2Oat final {
void ProcessOptions(ParserOptions* parser_options) {
compiler_options_->compile_pic_ = true; // All AOT compilation is PIC.
- compiler_options_->boot_image_ = !image_filenames_.empty();
- compiler_options_->app_image_ = app_image_fd_ != -1 || !app_image_file_name_.empty();
-
- if (IsBootImage() && image_filenames_.size() == 1) {
- const std::string& boot_image_filename = image_filenames_[0];
- compiler_options_->core_image_ = CompilerDriver::IsCoreImageFilename(boot_image_filename);
+ DCHECK(compiler_options_->image_type_ == CompilerOptions::ImageType::kNone);
+ if (!image_filenames_.empty()) {
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImage;
}
-
- if (IsAppImage() && IsBootImage()) {
- Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
+ if (app_image_fd_ != -1 || !app_image_file_name_.empty()) {
+ if (compiler_options_->IsBootImage()) {
+ Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
+ }
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kAppImage;
}
if (oat_filenames_.empty() && oat_fd_ == -1) {
@@ -951,6 +950,9 @@ class Dex2Oat final {
}
}
compiler_options_->passes_to_run_ = passes_to_run_.get();
+ compiler_options_->compiling_with_core_image_ =
+ !boot_image_filename_.empty() &&
+ CompilerDriver::IsCoreImageFilename(boot_image_filename_);
}
static bool SupportsDeterministicCompilation() {
@@ -1597,7 +1599,7 @@ class Dex2Oat final {
// If we need to downgrade the compiler-filter for size reasons.
if (!IsBootImage() && IsVeryLarge(dex_files)) {
// Disable app image to make sure dex2oat unloading is enabled.
- compiler_options_->DisableAppImage();
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
// If we need to downgrade the compiler-filter for size reasons, do that early before we read
// it below for creating verification callbacks.
@@ -1959,7 +1961,6 @@ class Dex2Oat final {
image_writer_.reset(new linker::ImageWriter(*compiler_options_,
image_base_,
- IsAppImage(),
image_storage_mode_,
oat_filenames_,
dex_file_oat_index_map_,
diff --git a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
index 3d7277aab3..b93e091ae6 100644
--- a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
+++ b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
@@ -827,26 +827,38 @@ void Thumb2RelativePatcherTest::TestBakerFieldNarrow(uint32_t offset, uint32_t r
}
}
-#define TEST_BAKER_FIELD_WIDE(offset, ref_reg) \
- TEST_F(Thumb2RelativePatcherTest, \
- BakerOffsetWide##offset##_##ref_reg) { \
- TestBakerFieldWide(offset, ref_reg); \
+TEST_F(Thumb2RelativePatcherTest, BakerOffsetWide) {
+ struct TestCase {
+ uint32_t offset;
+ uint32_t ref_reg;
+ };
+ static const TestCase test_cases[] = {
+ { 0u, 0u },
+ { 8u, 3u },
+ { 28u, 7u },
+ { 0xffcu, 11u },
+ };
+ for (const TestCase& test_case : test_cases) {
+ Reset();
+ TestBakerFieldWide(test_case.offset, test_case.ref_reg);
}
+}
-TEST_BAKER_FIELD_WIDE(/* offset */ 0, /* ref_reg */ 0)
-TEST_BAKER_FIELD_WIDE(/* offset */ 8, /* ref_reg */ 3)
-TEST_BAKER_FIELD_WIDE(/* offset */ 28, /* ref_reg */ 7)
-TEST_BAKER_FIELD_WIDE(/* offset */ 0xffc, /* ref_reg */ 11)
-
-#define TEST_BAKER_FIELD_NARROW(offset, ref_reg) \
- TEST_F(Thumb2RelativePatcherTest, \
- BakerOffsetNarrow##offset##_##ref_reg) { \
- TestBakerFieldNarrow(offset, ref_reg); \
+TEST_F(Thumb2RelativePatcherTest, BakerOffsetNarrow) {
+ struct TestCase {
+ uint32_t offset;
+ uint32_t ref_reg;
+ };
+ static const TestCase test_cases[] = {
+ { 0, 0u },
+ { 8, 3u },
+ { 28, 7u },
+ };
+ for (const TestCase& test_case : test_cases) {
+ Reset();
+ TestBakerFieldNarrow(test_case.offset, test_case.ref_reg);
}
-
-TEST_BAKER_FIELD_NARROW(/* offset */ 0, /* ref_reg */ 0)
-TEST_BAKER_FIELD_NARROW(/* offset */ 8, /* ref_reg */ 3)
-TEST_BAKER_FIELD_NARROW(/* offset */ 28, /* ref_reg */ 7)
+}
TEST_F(Thumb2RelativePatcherTest, BakerOffsetThunkInTheMiddle) {
// One thunk in the middle with maximum distance branches to it from both sides.
diff --git a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
index 9e3bb978fb..0fc4610909 100644
--- a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
+++ b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
@@ -783,174 +783,242 @@ TEST_F(Arm64RelativePatcherTestDefault, CallOtherJustTooFarBefore) {
EXPECT_TRUE(CheckThunk(thunk_offset));
}
-TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry1) {
- TestNopsAdrpLdr(0u, 0x12345678u, 0x1234u);
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry2) {
- TestNopsAdrpLdr(0u, -0x12345678u, 0x4444u);
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry3) {
- TestNopsAdrpLdr(0u, 0x12345000u, 0x3ffcu);
-}
-
-TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry4) {
- TestNopsAdrpLdr(0u, 0x12345000u, 0x4000u);
+TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry) {
+ struct TestCase {
+ uint32_t bss_begin;
+ uint32_t string_entry_offset;
+ };
+ static const TestCase test_cases[] = {
+ { 0x12345678u, 0x1234u },
+ { -0x12345678u, 0x4444u },
+ { 0x12345000u, 0x3ffcu },
+ { 0x12345000u, 0x4000u }
+ };
+ for (const TestCase& test_case : test_cases) {
+ Reset();
+ TestNopsAdrpLdr(/*num_nops=*/ 0u, test_case.bss_begin, test_case.string_entry_offset);
+ }
}
-TEST_F(Arm64RelativePatcherTestDefault, StringReference1) {
- TestNopsAdrpAdd(0u, 0x12345678u);
+TEST_F(Arm64RelativePatcherTestDefault, StringReference) {
+ for (uint32_t string_offset : { 0x12345678u, -0x12345678u, 0x12345000u, 0x12345ffcu}) {
+ Reset();
+ TestNopsAdrpAdd(/*num_nops=*/ 0u, string_offset);
+ }
}
-TEST_F(Arm64RelativePatcherTestDefault, StringReference2) {
- TestNopsAdrpAdd(0u, -0x12345678u);
+template <typename Test>
+void TestForAdrpOffsets(Test test, std::initializer_list<uint32_t> args) {
+ for (uint32_t adrp_offset : { 0xff4u, 0xff8u, 0xffcu, 0x1000u }) {
+ for (uint32_t arg : args) {
+ test(adrp_offset, arg);
+ }
+ }
}
-TEST_F(Arm64RelativePatcherTestDefault, StringReference3) {
- TestNopsAdrpAdd(0u, 0x12345000u);
+TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryLdur) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t string_entry_offset) {
+ Reset();
+ bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu);
+ TestAdrpLdurLdr(adrp_offset, has_thunk, /*bss_begin=*/ 0x12345678u, string_entry_offset);
+ },
+ { 0x1234u, 0x1238u });
}
-TEST_F(Arm64RelativePatcherTestDefault, StringReference4) {
- TestNopsAdrpAdd(0u, 0x12345ffcu);
+TEST_F(Arm64RelativePatcherTestDenver64, StringBssEntryLdur) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t string_entry_offset) {
+ Reset();
+ TestAdrpLdurLdr(adrp_offset,
+ /*has_thunk=*/ false,
+ /*bss_begin=*/ 0x12345678u,
+ string_entry_offset);
+ },
+ { 0x1234u, 0x1238u });
}
-#define TEST_FOR_OFFSETS(test, disp1, disp2) \
- test(0xff4u, disp1) test(0xff8u, disp1) test(0xffcu, disp1) test(0x1000u, disp1) \
- test(0xff4u, disp2) test(0xff8u, disp2) test(0xffcu, disp2) test(0x1000u, disp2)
-
-#define DEFAULT_LDUR_LDR_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## Ldur ## disp) { \
- bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); \
- TestAdrpLdurLdr(adrp_offset, has_thunk, 0x12345678u, disp); \
- }
-
-TEST_FOR_OFFSETS(DEFAULT_LDUR_LDR_TEST, 0x1234, 0x1238)
-
-#define DENVER64_LDUR_LDR_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDenver64, StringBssEntry ## adrp_offset ## Ldur ## disp) { \
- TestAdrpLdurLdr(adrp_offset, false, 0x12345678u, disp); \
- }
-
-TEST_FOR_OFFSETS(DENVER64_LDUR_LDR_TEST, 0x1234, 0x1238)
-
// LDR <Wt>, <label> is always aligned. We should never have to use a fixup.
-#define LDRW_PCREL_LDR_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## WPcRel ## disp) { \
- TestAdrpLdrPcRelLdr(kLdrWPcRelInsn, disp, adrp_offset, false, 0x12345678u, 0x1234u); \
- }
-
-TEST_FOR_OFFSETS(LDRW_PCREL_LDR_TEST, 0x1234, 0x1238)
+TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryWPcRel) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t pcrel_disp) {
+ Reset();
+ TestAdrpLdrPcRelLdr(kLdrWPcRelInsn,
+ pcrel_disp,
+ adrp_offset,
+ /*has_thunk=*/ false,
+ /*bss_begin=*/ 0x12345678u,
+ /*string_entry_offset=*/ 0x1234u);
+ },
+ { 0x1234u, 0x1238u });
+}
// LDR <Xt>, <label> is aligned when offset + displacement is a multiple of 8.
-#define LDRX_PCREL_LDR_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## XPcRel ## disp) { \
- bool unaligned = !IsAligned<8u>((adrp_offset) + 4u + static_cast<uint32_t>(disp)); \
- bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu) && unaligned; \
- TestAdrpLdrPcRelLdr(kLdrXPcRelInsn, disp, adrp_offset, has_thunk, 0x12345678u, 0x1234u); \
- }
-
-TEST_FOR_OFFSETS(LDRX_PCREL_LDR_TEST, 0x1234, 0x1238)
+TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryXPcRel) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t pcrel_disp) {
+ Reset();
+ bool unaligned = !IsAligned<8u>((adrp_offset) + 4u + static_cast<uint32_t>(pcrel_disp));
+ bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu) && unaligned;
+ TestAdrpLdrPcRelLdr(kLdrXPcRelInsn,
+ pcrel_disp,
+ adrp_offset,
+ has_thunk,
+ /*bss_begin=*/ 0x12345678u,
+ /*string_entry_offset=*/ 0x1234u);
+ },
+ { 0x1234u, 0x1238u });
+}
// LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned. No fixup needed.
-#define LDRW_SPREL_LDR_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## WSpRel ## disp) { \
- TestAdrpLdrSpRelLdr(kLdrWSpRelInsn, (disp) >> 2, adrp_offset, false, 0x12345678u, 0x1234u); \
- }
-
-TEST_FOR_OFFSETS(LDRW_SPREL_LDR_TEST, 0, 4)
-
-#define LDRX_SPREL_LDR_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringBssEntry ## adrp_offset ## XSpRel ## disp) { \
- TestAdrpLdrSpRelLdr(kLdrXSpRelInsn, (disp) >> 3, adrp_offset, false, 0x12345678u, 0x1234u); \
- }
-
-TEST_FOR_OFFSETS(LDRX_SPREL_LDR_TEST, 0, 8)
-
-#define DEFAULT_LDUR_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## Ldur ## disp) { \
- bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); \
- TestAdrpLdurAdd(adrp_offset, has_thunk, disp); \
- }
-
-TEST_FOR_OFFSETS(DEFAULT_LDUR_ADD_TEST, 0x12345678, 0xffffc840)
-
-#define DENVER64_LDUR_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDenver64, StringReference ## adrp_offset ## Ldur ## disp) { \
- TestAdrpLdurAdd(adrp_offset, false, disp); \
- }
-
-TEST_FOR_OFFSETS(DENVER64_LDUR_ADD_TEST, 0x12345678, 0xffffc840)
-
-#define DEFAULT_SUBX3X2_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## SubX3X2 ## disp) { \
- /* SUB unrelated to "ADRP x0, addr". */ \
- uint32_t sub = kSubXInsn | (100 << 10) | (2u << 5) | 3u; /* SUB x3, x2, #100 */ \
- TestAdrpInsn2Add(sub, adrp_offset, false, disp); \
- }
+TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryWSpRel) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t disp) {
+ Reset();
+ TestAdrpLdrSpRelLdr(kLdrWSpRelInsn,
+ /*sprel_disp_in_load_units=*/ disp >> 2,
+ adrp_offset,
+ /*has_thunk=*/ false,
+ /*bss_begin=*/ 0x12345678u,
+ /*string_entry_offset=*/ 0x1234u);
+ },
+ { 0u, 4u });
+}
-TEST_FOR_OFFSETS(DEFAULT_SUBX3X2_ADD_TEST, 0x12345678, 0xffffc840)
+TEST_F(Arm64RelativePatcherTestDefault, StringBssEntryXSpRel) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t disp) {
+ Reset();
+ TestAdrpLdrSpRelLdr(kLdrXSpRelInsn,
+ /*sprel_disp_in_load_units=*/ (disp) >> 3,
+ adrp_offset,
+ /*has_thunk=*/ false,
+ /*bss_begin=*/ 0x12345678u,
+ /*string_entry_offset=*/ 0x1234u);
+ },
+ { 0u, 8u });
+}
-#define DEFAULT_SUBSX3X0_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## SubsX3X0 ## disp) { \
- /* SUBS that uses the result of "ADRP x0, addr". */ \
- uint32_t subs = kSubsXInsn | (100 << 10) | (0u << 5) | 3u; /* SUBS x3, x0, #100 */ \
- TestAdrpInsn2Add(subs, adrp_offset, false, disp); \
- }
+TEST_F(Arm64RelativePatcherTestDefault, StringReferenceLdur) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t string_offset) {
+ Reset();
+ bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu);
+ TestAdrpLdurAdd(adrp_offset, has_thunk, string_offset);
+ },
+ { 0x12345678u, 0xffffc840u });
+}
-TEST_FOR_OFFSETS(DEFAULT_SUBSX3X0_ADD_TEST, 0x12345678, 0xffffc840)
+TEST_F(Arm64RelativePatcherTestDenver64, StringReferenceLdur) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t string_offset) {
+ Reset();
+ TestAdrpLdurAdd(adrp_offset, /*has_thunk=*/ false, string_offset);
+ },
+ { 0x12345678u, 0xffffc840U });
+}
-#define DEFAULT_ADDX0X0_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## AddX0X0 ## disp) { \
- /* ADD that uses the result register of "ADRP x0, addr" as both source and destination. */ \
- uint32_t add = kSubXInsn | (100 << 10) | (0u << 5) | 0u; /* ADD x0, x0, #100 */ \
- TestAdrpInsn2Add(add, adrp_offset, false, disp); \
- }
+TEST_F(Arm64RelativePatcherTestDefault, StringReferenceSubX3X2) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t string_offset) {
+ Reset();
+ /* SUB unrelated to "ADRP x0, addr". */ \
+ uint32_t sub = kSubXInsn | (100 << 10) | (2u << 5) | 3u; /* SUB x3, x2, #100 */
+ TestAdrpInsn2Add(sub, adrp_offset, /*has_thunk=*/ false, string_offset);
+ },
+ { 0x12345678u, 0xffffc840u });
+}
-TEST_FOR_OFFSETS(DEFAULT_ADDX0X0_ADD_TEST, 0x12345678, 0xffffc840)
+TEST_F(Arm64RelativePatcherTestDefault, StringReferenceSubsX3X0) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t string_offset) {
+ Reset();
+ /* SUBS that uses the result of "ADRP x0, addr". */ \
+ uint32_t subs = kSubsXInsn | (100 << 10) | (0u << 5) | 3u; /* SUBS x3, x0, #100 */
+ TestAdrpInsn2Add(subs, adrp_offset, /*has_thunk=*/ false, string_offset);
+ },
+ { 0x12345678u, 0xffffc840u });
+}
-#define DEFAULT_ADDSX0X2_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## AddsX0X2 ## disp) { \
- /* ADDS that does not use the result of "ADRP x0, addr" but overwrites that register. */ \
- uint32_t adds = kAddsXInsn | (100 << 10) | (2u << 5) | 0u; /* ADDS x0, x2, #100 */ \
- bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu); \
- TestAdrpInsn2Add(adds, adrp_offset, has_thunk, disp); \
- }
+TEST_F(Arm64RelativePatcherTestDefault, StringReferenceAddX0X0) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t string_offset) {
+ Reset();
+ /* ADD that uses the result register of "ADRP x0, addr" as both source and destination. */
+ uint32_t add = kSubXInsn | (100 << 10) | (0u << 5) | 0u; /* ADD x0, x0, #100 */
+ TestAdrpInsn2Add(add, adrp_offset, /*has_thunk=*/ false, string_offset);
+ },
+ { 0x12345678u, 0xffffc840 });
+}
-TEST_FOR_OFFSETS(DEFAULT_ADDSX0X2_ADD_TEST, 0x12345678, 0xffffc840)
+TEST_F(Arm64RelativePatcherTestDefault, StringReferenceAddsX0X2) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t string_offset) {
+ Reset();
+ /* ADDS that does not use the result of "ADRP x0, addr" but overwrites that register. */
+ uint32_t adds = kAddsXInsn | (100 << 10) | (2u << 5) | 0u; /* ADDS x0, x2, #100 */
+ bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu);
+ TestAdrpInsn2Add(adds, adrp_offset, has_thunk, string_offset);
+ },
+ { 0x12345678u, 0xffffc840u });
+}
// LDR <Wt>, <label> is always aligned. We should never have to use a fixup.
-#define LDRW_PCREL_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## WPcRel ## disp) { \
- TestAdrpLdrPcRelAdd(kLdrWPcRelInsn, disp, adrp_offset, false, 0x12345678u); \
- }
-
-TEST_FOR_OFFSETS(LDRW_PCREL_ADD_TEST, 0x1234, 0x1238)
+TEST_F(Arm64RelativePatcherTestDefault, StringReferenceWPcRel) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t pcrel_disp) {
+ Reset();
+ TestAdrpLdrPcRelAdd(kLdrWPcRelInsn,
+ pcrel_disp,
+ adrp_offset,
+ /*has_thunk=*/ false,
+ /*string_offset=*/ 0x12345678u);
+ },
+ { 0x1234u, 0x1238u });
+}
// LDR <Xt>, <label> is aligned when offset + displacement is a multiple of 8.
-#define LDRX_PCREL_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## XPcRel ## disp) { \
- bool unaligned = !IsAligned<8u>((adrp_offset) + 4u + static_cast<uint32_t>(disp)); \
- bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu) && unaligned; \
- TestAdrpLdrPcRelAdd(kLdrXPcRelInsn, disp, adrp_offset, has_thunk, 0x12345678u); \
- }
-
-TEST_FOR_OFFSETS(LDRX_PCREL_ADD_TEST, 0x1234, 0x1238)
+TEST_F(Arm64RelativePatcherTestDefault, StringReferenceXPcRel) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t pcrel_disp) {
+ Reset();
+ bool unaligned = !IsAligned<8u>((adrp_offset) + 4u + static_cast<uint32_t>(pcrel_disp));
+ bool has_thunk = ((adrp_offset) == 0xff8u || (adrp_offset) == 0xffcu) && unaligned;
+ TestAdrpLdrPcRelAdd(kLdrXPcRelInsn,
+ pcrel_disp,
+ adrp_offset,
+ has_thunk,
+ /*string_offset=*/ 0x12345678u);
+ },
+ { 0x1234u, 0x1238u });
+}
// LDR <Wt>, [SP, #<pimm>] and LDR <Xt>, [SP, #<pimm>] are always aligned. No fixup needed.
-#define LDRW_SPREL_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## WSpRel ## disp) { \
- TestAdrpLdrSpRelAdd(kLdrWSpRelInsn, (disp) >> 2, adrp_offset, false, 0x12345678u); \
- }
-
-TEST_FOR_OFFSETS(LDRW_SPREL_ADD_TEST, 0, 4)
-
-#define LDRX_SPREL_ADD_TEST(adrp_offset, disp) \
- TEST_F(Arm64RelativePatcherTestDefault, StringReference ## adrp_offset ## XSpRel ## disp) { \
- TestAdrpLdrSpRelAdd(kLdrXSpRelInsn, (disp) >> 3, adrp_offset, false, 0x12345678u); \
- }
+TEST_F(Arm64RelativePatcherTestDefault, StringReferenceWSpRel) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t disp) {
+ Reset();
+ TestAdrpLdrSpRelAdd(kLdrWSpRelInsn,
+ /*sprel_disp_in_load_units=*/ (disp) >> 2,
+ adrp_offset,
+ /*has_thunk=*/ false,
+ /*string_offset=*/ 0x12345678u);
+ },
+ { 0u, 4u });
+}
-TEST_FOR_OFFSETS(LDRX_SPREL_ADD_TEST, 0, 8)
+TEST_F(Arm64RelativePatcherTestDefault, StringReferenceXSpRel) {
+ TestForAdrpOffsets(
+ [&](uint32_t adrp_offset, uint32_t disp) {
+ Reset();
+ TestAdrpLdrSpRelAdd(kLdrXSpRelInsn,
+ /*sprel_disp_in_load_units=*/ (disp) >> 3,
+ adrp_offset,
+ /*has_thunk=*/ false,
+ /*string_offset=*/ 0x12345678u);
+ },
+ { 0u, 8u });
+}
void Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t ref_reg) {
uint32_t valid_regs[] = {
@@ -1039,15 +1107,22 @@ void Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t ref_reg)
}
}
-#define TEST_BAKER_FIELD(offset, ref_reg) \
- TEST_F(Arm64RelativePatcherTestDefault, \
- BakerOffset##offset##_##ref_reg) { \
- TestBakerField(offset, ref_reg); \
+TEST_F(Arm64RelativePatcherTestDefault, BakerOffset) {
+ struct TestCase {
+ uint32_t offset;
+ uint32_t ref_reg;
+ };
+ static const TestCase test_cases[] = {
+ { 0u, 0u },
+ { 8u, 15u},
+ { 0x3ffcu, 29u },
+ };
+ for (const TestCase& test_case : test_cases) {
+ Reset();
+ TestBakerField(test_case.offset, test_case.ref_reg);
}
+}
-TEST_BAKER_FIELD(/* offset */ 0, /* ref_reg */ 0)
-TEST_BAKER_FIELD(/* offset */ 8, /* ref_reg */ 15)
-TEST_BAKER_FIELD(/* offset */ 0x3ffc, /* ref_reg */ 29)
TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddle) {
// One thunk in the middle with maximum distance branches to it from both sides.
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 9d1a4e7b44..9ef2875017 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -216,11 +216,10 @@ inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode,
// TODO: compile_pic should be a test argument.
std::unique_ptr<ImageWriter> writer(new ImageWriter(*compiler_options_,
kRequestedImageBase,
- /*compile_app_image*/false,
storage_mode,
oat_filename_vector,
dex_file_to_oat_index_map,
- /*dirty_image_objects*/nullptr));
+ /*dirty_image_objects=*/ nullptr));
{
{
jobject class_loader = nullptr;
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 2b2b02941a..a3fc1cdf2d 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -145,10 +145,15 @@ static ArrayRef<const uint8_t> MaybeCompressData(ArrayRef<const uint8_t> source,
// Separate objects into multiple bins to optimize dirty memory use.
static constexpr bool kBinObjects = true;
+ObjPtr<mirror::ClassLoader> ImageWriter::GetClassLoader() {
+ CHECK_EQ(class_loaders_.size(), compiler_options_.IsAppImage() ? 1u : 0u);
+ return compiler_options_.IsAppImage() ? *class_loaders_.begin() : nullptr;
+}
+
// Return true if an object is already in an image space.
bool ImageWriter::IsInBootImage(const void* obj) const {
gc::Heap* const heap = Runtime::Current()->GetHeap();
- if (!compile_app_image_) {
+ if (compiler_options_.IsBootImage()) {
DCHECK(heap->GetBootImageSpaces().empty());
return false;
}
@@ -165,7 +170,7 @@ bool ImageWriter::IsInBootImage(const void* obj) const {
bool ImageWriter::IsInBootOatFile(const void* ptr) const {
gc::Heap* const heap = Runtime::Current()->GetHeap();
- if (!compile_app_image_) {
+ if (compiler_options_.IsBootImage()) {
DCHECK(heap->GetBootImageSpaces().empty());
return false;
}
@@ -204,7 +209,7 @@ bool ImageWriter::PrepareImageAddressSpace(TimingLogger* timings) {
PruneNonImageClasses(); // Remove junk
}
- if (compile_app_image_) {
+ if (compiler_options_.IsAppImage()) {
TimingLogger::ScopedTiming t("ClearDexFileCookies", timings);
// Clear dex file cookies for app images to enable app image determinism. This is required
// since the cookie field contains long pointers to DexFiles which are not deterministic.
@@ -226,7 +231,7 @@ bool ImageWriter::PrepareImageAddressSpace(TimingLogger* timings) {
// Used to store information that will later be used to calculate image
// offsets to string references in the AppImage.
std::vector<HeapReferencePointerInfo> string_ref_info;
- if (ClassLinker::kAppImageMayContainStrings && compile_app_image_) {
+ if (ClassLinker::kAppImageMayContainStrings && compiler_options_.IsAppImage()) {
// Count the number of string fields so we can allocate the appropriate
// amount of space in the image section.
TimingLogger::ScopedTiming t("AppImage:CollectStringReferenceInfo", timings);
@@ -248,7 +253,7 @@ bool ImageWriter::PrepareImageAddressSpace(TimingLogger* timings) {
}
// Obtain class count for debugging purposes
- if (VLOG_IS_ON(compiler) && compile_app_image_) {
+ if (VLOG_IS_ON(compiler) && compiler_options_.IsAppImage()) {
ScopedObjectAccess soa(self);
size_t app_image_class_count = 0;
@@ -268,7 +273,7 @@ bool ImageWriter::PrepareImageAddressSpace(TimingLogger* timings) {
VLOG(compiler) << "Dex2Oat:AppImage:classCount = " << app_image_class_count;
}
- if (ClassLinker::kAppImageMayContainStrings && compile_app_image_) {
+ if (ClassLinker::kAppImageMayContainStrings && compiler_options_.IsAppImage()) {
// Use the string reference information obtained earlier to calculate image
// offsets. These will later be written to the image by Write/CopyMetadata.
TimingLogger::ScopedTiming t("AppImage:CalculateImageOffsets", timings);
@@ -580,7 +585,7 @@ void ImageWriter::VerifyNativeGCRootInvariants() const REQUIRES_SHARED(Locks::mu
}
void ImageWriter::CopyMetadata() {
- CHECK(compile_app_image_);
+ DCHECK(compiler_options_.IsAppImage());
CHECK_EQ(image_infos_.size(), 1u);
const ImageInfo& image_info = image_infos_.back();
@@ -613,8 +618,22 @@ bool ImageWriter::Write(int image_fd,
CHECK(!oat_filenames.empty());
CHECK_EQ(image_filenames.size(), oat_filenames.size());
+ Thread* self = Thread::Current();
{
- ScopedObjectAccess soa(Thread::Current());
+ // Preload deterministic contents to the dex cache arrays we're going to write.
+ ScopedObjectAccess soa(self);
+ ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader();
+ std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self);
+ for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
+ if (IsInBootImage(dex_cache.Ptr())) {
+ continue; // Boot image DexCache is not written to the app image.
+ }
+ PreloadDexCache(dex_cache, class_loader);
+ }
+ }
+
+ {
+ ScopedObjectAccess soa(self);
for (size_t i = 0; i < oat_filenames.size(); ++i) {
CreateHeader(i);
CopyAndFixupNativeData(i);
@@ -623,12 +642,12 @@ bool ImageWriter::Write(int image_fd,
{
// TODO: heap validation can't handle these fix up passes.
- ScopedObjectAccess soa(Thread::Current());
+ ScopedObjectAccess soa(self);
Runtime::Current()->GetHeap()->DisableObjectValidation();
CopyAndFixupObjects();
}
- if (compile_app_image_) {
+ if (compiler_options_.IsAppImage()) {
CopyMetadata();
}
@@ -656,7 +675,7 @@ bool ImageWriter::Write(int image_fd,
return false;
}
- if (!compile_app_image_ && fchmod(image_file->Fd(), 0644) != 0) {
+ if (!compiler_options_.IsAppImage() && fchmod(image_file->Fd(), 0644) != 0) {
PLOG(ERROR) << "Failed to make image file world readable: " << image_filename;
image_file->Erase();
return EXIT_FAILURE;
@@ -1209,7 +1228,7 @@ bool ImageWriter::PruneAppImageClassInternal(
std::unordered_set<mirror::Object*>* visited) {
DCHECK(early_exit != nullptr);
DCHECK(visited != nullptr);
- DCHECK(compile_app_image_);
+ DCHECK(compiler_options_.IsAppImage());
if (klass == nullptr || IsInBootImage(klass.Ptr())) {
return false;
}
@@ -1313,7 +1332,8 @@ bool ImageWriter::KeepClass(ObjPtr<mirror::Class> klass) {
if (klass == nullptr) {
return false;
}
- if (compile_app_image_ && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
+ if (!compiler_options_.IsBootImage() &&
+ Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
// Already in boot image, return true.
return true;
}
@@ -1321,7 +1341,7 @@ bool ImageWriter::KeepClass(ObjPtr<mirror::Class> klass) {
if (!compiler_options_.IsImageClass(klass->GetDescriptor(&temp))) {
return false;
}
- if (compile_app_image_) {
+ if (compiler_options_.IsAppImage()) {
// For app images, we need to prune boot loader classes that are not in the boot image since
// these may have already been loaded when the app image is loaded.
// Keep classes in the boot image space since we don't want to re-resolve these.
@@ -1409,8 +1429,79 @@ void ImageWriter::VisitClassLoaders(ClassLoaderVisitor* visitor) {
Runtime::Current()->GetClassLinker()->VisitClassLoaders(visitor);
}
-void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
- ObjPtr<mirror::ClassLoader> class_loader) {
+void ImageWriter::PruneDexCache(ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader) {
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ // Prune methods.
+ dex::TypeIndex last_class_idx; // Initialized to invalid index.
+ ObjPtr<mirror::Class> last_class = nullptr;
+ mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods();
+ for (size_t slot_idx = 0, num = dex_cache->NumResolvedMethods(); slot_idx != num; ++slot_idx) {
+ auto pair =
+ mirror::DexCache::GetNativePairPtrSize(resolved_methods, slot_idx, target_ptr_size_);
+ uint32_t stored_index = pair.index;
+ ArtMethod* method = pair.object;
+ if (method == nullptr) {
+ continue; // Empty entry.
+ }
+ // Check if the referenced class is in the image. Note that we want to check the referenced
+ // class rather than the declaring class to preserve the semantics, i.e. using a MethodId
+ // results in resolving the referenced class and that can for example throw OOME.
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(stored_index);
+ if (method_id.class_idx_ != last_class_idx) {
+ last_class_idx = method_id.class_idx_;
+ last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader);
+ if (last_class != nullptr && !KeepClass(last_class)) {
+ last_class = nullptr;
+ }
+ }
+ if (last_class == nullptr) {
+ dex_cache->ClearResolvedMethod(stored_index, target_ptr_size_);
+ }
+ }
+ // Prune fields.
+ mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields();
+ last_class_idx = dex::TypeIndex(); // Initialized to invalid index.
+ last_class = nullptr;
+ for (size_t slot_idx = 0, num = dex_cache->NumResolvedFields(); slot_idx != num; ++slot_idx) {
+ auto pair = mirror::DexCache::GetNativePairPtrSize(resolved_fields, slot_idx, target_ptr_size_);
+ uint32_t stored_index = pair.index;
+ ArtField* field = pair.object;
+ if (field == nullptr) {
+ continue; // Empty entry.
+ }
+ // Check if the referenced class is in the image. Note that we want to check the referenced
+ // class rather than the declaring class to preserve the semantics, i.e. using a FieldId
+ // results in resolving the referenced class and that can for example throw OOME.
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(stored_index);
+ if (field_id.class_idx_ != last_class_idx) {
+ last_class_idx = field_id.class_idx_;
+ last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader);
+ if (last_class != nullptr && !KeepClass(last_class)) {
+ last_class = nullptr;
+ }
+ }
+ if (last_class == nullptr) {
+ dex_cache->ClearResolvedField(stored_index, target_ptr_size_);
+ }
+ }
+ // Prune types.
+ for (size_t slot_idx = 0, num = dex_cache->NumResolvedTypes(); slot_idx != num; ++slot_idx) {
+ mirror::TypeDexCachePair pair =
+ dex_cache->GetResolvedTypes()[slot_idx].load(std::memory_order_relaxed);
+ uint32_t stored_index = pair.index;
+ ObjPtr<mirror::Class> klass = pair.object.Read();
+ if (klass != nullptr && !KeepClass(klass)) {
+ dex_cache->ClearResolvedType(dex::TypeIndex(stored_index));
+ }
+ }
+ // Strings do not need pruning.
+}
+
+void ImageWriter::PreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader) {
// To ensure deterministic contents of the hash-based arrays, each slot shall contain
// the candidate with the lowest index. As we're processing entries in increasing index
// order, this means trying to look up the entry for the current index if the slot is
@@ -1419,7 +1510,7 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker();
const DexFile& dex_file = *dex_cache->GetDexFile();
- // Prune methods.
+ // Preload the methods array and make the contents deterministic.
mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods();
dex::TypeIndex last_class_idx; // Initialized to invalid index.
ObjPtr<mirror::Class> last_class = nullptr;
@@ -1439,27 +1530,20 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
if (method_id.class_idx_ != last_class_idx) {
last_class_idx = method_id.class_idx_;
last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader);
- if (last_class != nullptr && !KeepClass(last_class)) {
- last_class = nullptr;
- }
}
if (method == nullptr || i < stored_index) {
if (last_class != nullptr) {
// Try to resolve the method with the class linker, which will insert
// it into the dex cache if successful.
method = class_linker->FindResolvedMethod(last_class, dex_cache, class_loader, i);
- // If the referenced class is in the image, the defining class must also be there.
- DCHECK(method == nullptr || KeepClass(method->GetDeclaringClass()));
DCHECK(method == nullptr || dex_cache->GetResolvedMethod(i, target_ptr_size_) == method);
}
} else {
DCHECK_EQ(i, stored_index);
- if (last_class == nullptr) {
- dex_cache->ClearResolvedMethod(stored_index, target_ptr_size_);
- }
+ DCHECK(last_class != nullptr);
}
}
- // Prune fields and make the contents of the field array deterministic.
+ // Preload the fields array and make the contents deterministic.
mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields();
last_class_idx = dex::TypeIndex(); // Initialized to invalid index.
last_class = nullptr;
@@ -1484,19 +1568,17 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
}
if (field == nullptr || i < stored_index) {
if (last_class != nullptr) {
+ // Try to resolve the field with the class linker, which will insert
+ // it into the dex cache if successful.
field = class_linker->FindResolvedFieldJLS(last_class, dex_cache, class_loader, i);
- // If the referenced class is in the image, the defining class must also be there.
- DCHECK(field == nullptr || KeepClass(field->GetDeclaringClass()));
DCHECK(field == nullptr || dex_cache->GetResolvedField(i, target_ptr_size_) == field);
}
} else {
DCHECK_EQ(i, stored_index);
- if (last_class == nullptr) {
- dex_cache->ClearResolvedField(stored_index, target_ptr_size_);
- }
+ DCHECK(last_class != nullptr);
}
}
- // Prune types and make the contents of the type array deterministic.
+ // Preload the types array and make the contents deterministic.
// This is done after fields and methods as their lookup can touch the types array.
for (size_t i = 0, end = dex_cache->GetDexFile()->NumTypeIds(); i < end; ++i) {
dex::TypeIndex type_idx(i);
@@ -1507,15 +1589,10 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
ObjPtr<mirror::Class> klass = pair.object.Read();
if (klass == nullptr || i < stored_index) {
klass = class_linker->LookupResolvedType(type_idx, dex_cache, class_loader);
- if (klass != nullptr) {
- DCHECK_EQ(dex_cache->GetResolvedType(type_idx), klass);
- stored_index = i; // For correct clearing below if not keeping the `klass`.
- }
- } else if (i == stored_index && !KeepClass(klass)) {
- dex_cache->ClearResolvedType(dex::TypeIndex(stored_index));
+ DCHECK(klass == nullptr || dex_cache->GetResolvedType(type_idx) == klass);
}
}
- // Strings do not need pruning, but the contents of the string array must be deterministic.
+ // Preload the strings array and make the contents deterministic.
for (size_t i = 0, end = dex_cache->GetDexFile()->NumStringIds(); i < end; ++i) {
dex::StringIndex string_idx(i);
uint32_t slot_idx = dex_cache->StringSlotIndex(string_idx);
@@ -1560,25 +1637,15 @@ void ImageWriter::PruneNonImageClasses() {
VisitClassLoaders(&class_loader_visitor);
VLOG(compiler) << "Pruned " << class_loader_visitor.GetRemovedClassCount() << " classes";
class_loader = class_loader_visitor.GetClassLoader();
- DCHECK_EQ(class_loader != nullptr, compile_app_image_);
+ DCHECK_EQ(class_loader != nullptr, compiler_options_.IsAppImage());
}
// Clear references to removed classes from the DexCaches.
- std::vector<ObjPtr<mirror::DexCache>> dex_caches;
- {
- ReaderMutexLock mu2(self, *Locks::dex_lock_);
- dex_caches.reserve(class_linker->GetDexCachesData().size());
- for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
- if (self->IsJWeakCleared(data.weak_root)) {
- continue;
- }
- dex_caches.push_back(self->DecodeJObject(data.weak_root)->AsDexCache());
- }
- }
+ std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self);
for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
// Pass the class loader associated with the DexCache. This can either be
// the app's `class_loader` or `nullptr` if boot class loader.
- PruneAndPreloadDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : class_loader);
+ PruneDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : class_loader);
}
// Drop the array class cache in the ClassLinker, as these are roots holding those classes live.
@@ -1588,6 +1655,20 @@ void ImageWriter::PruneNonImageClasses() {
prune_class_memo_.clear();
}
+std::vector<ObjPtr<mirror::DexCache>> ImageWriter::FindDexCaches(Thread* self) {
+ std::vector<ObjPtr<mirror::DexCache>> dex_caches;
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ReaderMutexLock mu2(self, *Locks::dex_lock_);
+ dex_caches.reserve(class_linker->GetDexCachesData().size());
+ for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
+ if (self->IsJWeakCleared(data.weak_root)) {
+ continue;
+ }
+ dex_caches.push_back(self->DecodeJObject(data.weak_root)->AsDexCache());
+ }
+ return dex_caches;
+}
+
void ImageWriter::CheckNonImageClassesRemoved() {
auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
if (obj->IsClass() && !IsInBootImage(obj)) {
@@ -1619,7 +1700,7 @@ mirror::String* ImageWriter::FindInternedString(mirror::String* string) {
return found.Ptr();
}
}
- if (compile_app_image_) {
+ if (!compiler_options_.IsBootImage()) {
Runtime* const runtime = Runtime::Current();
ObjPtr<mirror::String> found = runtime->GetInternTable()->LookupStrong(self, string);
// If we found it in the runtime intern table it could either be in the boot image or interned
@@ -1716,7 +1797,7 @@ ObjPtr<ObjectArray<Object>> ImageWriter::CreateImageRoots(
Handle<ObjectArray<Object>> dex_caches(hs.NewHandle(CollectDexCaches(self, oat_index)));
// build an Object[] of the roots needed to restore the runtime
- int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compile_app_image_);
+ int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compiler_options_.IsAppImage());
Handle<ObjectArray<Object>> image_roots(hs.NewHandle(ObjectArray<Object>::Alloc(
self, GetClassRoot<ObjectArray<Object>>(class_linker), image_roots_size)));
image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get());
@@ -1729,14 +1810,14 @@ ObjPtr<ObjectArray<Object>> ImageWriter::CreateImageRoots(
runtime->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
image_roots->Set<false>(ImageHeader::kNoClassDefFoundError,
runtime->GetPreAllocatedNoClassDefFoundError());
- if (!compile_app_image_) {
+ if (!compiler_options_.IsAppImage()) {
DCHECK(boot_image_live_objects != nullptr);
image_roots->Set<false>(ImageHeader::kBootImageLiveObjects, boot_image_live_objects.Get());
} else {
DCHECK(boot_image_live_objects == nullptr);
}
- for (int32_t i = 0, num = ImageHeader::NumberOfImageRoots(compile_app_image_); i != num; ++i) {
- if (compile_app_image_ && i == ImageHeader::kAppImageClassLoader) {
+ for (int32_t i = 0; i != image_roots_size; ++i) {
+ if (compiler_options_.IsAppImage() && i == ImageHeader::kAppImageClassLoader) {
// image_roots[ImageHeader::kAppImageClassLoader] will be set later for app image.
continue;
}
@@ -1773,7 +1854,7 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack,
mirror::Class* as_klass = obj->AsClass();
mirror::DexCache* dex_cache = as_klass->GetDexCache();
DCHECK(!as_klass->IsErroneous()) << as_klass->GetStatus();
- if (compile_app_image_) {
+ if (compiler_options_.IsAppImage()) {
// Extra sanity, no boot loader classes should be left!
CHECK(!IsBootClassLoaderClass(as_klass)) << as_klass->PrettyClass();
}
@@ -1784,7 +1865,7 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack,
// belongs.
oat_index = GetOatIndexForDexCache(dex_cache);
ImageInfo& image_info = GetImageInfo(oat_index);
- if (!compile_app_image_) {
+ if (!compiler_options_.IsAppImage()) {
// Note: Avoid locking to prevent lock order violations from root visiting;
// image_info.class_table_ is only accessed from the image writer.
image_info.class_table_->InsertWithoutLocks(as_klass);
@@ -1887,7 +1968,7 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack,
// class loader.
mirror::ClassLoader* class_loader = obj->AsClassLoader();
if (class_loader->GetClassTable() != nullptr) {
- DCHECK(compile_app_image_);
+ DCHECK(compiler_options_.IsAppImage());
DCHECK(class_loaders_.empty());
class_loaders_.insert(class_loader);
ImageInfo& image_info = GetImageInfo(oat_index);
@@ -2065,7 +2146,7 @@ void ImageWriter::CalculateNewObjectOffsets() {
Runtime* const runtime = Runtime::Current();
VariableSizedHandleScope handles(self);
MutableHandle<ObjectArray<Object>> boot_image_live_objects = handles.NewHandle(
- compile_app_image_
+ compiler_options_.IsAppImage()
? nullptr
: IntrinsicObjects::AllocateBootImageLiveObjects(self, runtime->GetClassLinker()));
std::vector<Handle<ObjectArray<Object>>> image_roots;
@@ -2100,7 +2181,8 @@ void ImageWriter::CalculateNewObjectOffsets() {
for (auto* m : image_methods_) {
CHECK(m != nullptr);
CHECK(m->IsRuntimeMethod());
- DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image";
+ DCHECK_EQ(!compiler_options_.IsBootImage(), IsInBootImage(m))
+ << "Trampolines should be in boot image";
if (!IsInBootImage(m)) {
AssignMethodOffset(m, NativeObjectRelocationType::kRuntimeMethod, GetDefaultOatIndex());
}
@@ -2152,7 +2234,7 @@ void ImageWriter::CalculateNewObjectOffsets() {
// For app images, there may be objects that are only held live by the boot image. One
// example is finalizer references. Forward these objects so that EnsureBinSlotAssignedCallback
// does not fail any checks.
- if (compile_app_image_) {
+ if (compiler_options_.IsAppImage()) {
for (gc::space::ImageSpace* space : heap->GetBootImageSpaces()) {
DCHECK(space->IsImageSpace());
gc::accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
@@ -2453,7 +2535,7 @@ ArtMethod* ImageWriter::GetImageMethodAddress(ArtMethod* method) {
}
const void* ImageWriter::GetIntrinsicReferenceAddress(uint32_t intrinsic_data) {
- DCHECK(!compile_app_image_);
+ DCHECK(compiler_options_.IsBootImage());
switch (IntrinsicObjects::DecodePatchType(intrinsic_data)) {
case IntrinsicObjects::PatchType::kIntegerValueOfArray: {
const uint8_t* base_address =
@@ -2846,7 +2928,7 @@ void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) {
FixupClassVisitor visitor(this, copy);
ObjPtr<mirror::Object>(orig)->VisitReferences(visitor, visitor);
- if (kBitstringSubtypeCheckEnabled && compile_app_image_) {
+ if (kBitstringSubtypeCheckEnabled && compiler_options_.IsAppImage()) {
// When we call SubtypeCheck::EnsureInitialize, it Assigns new bitstring
// values to the parent of that class.
//
@@ -3027,7 +3109,7 @@ void ImageWriter::FixupDexCache(DexCache* orig_dex_cache, DexCache* copy_dex_cac
const uint8_t* ImageWriter::GetOatAddress(StubType type) const {
DCHECK_LE(type, StubType::kLast);
// If we are compiling an app image, we need to use the stubs of the boot image.
- if (compile_app_image_) {
+ if (!compiler_options_.IsBootImage()) {
// Use the current image pointers.
const std::vector<gc::space::ImageSpace*>& image_spaces =
Runtime::Current()->GetHeap()->GetBootImageSpaces();
@@ -3277,7 +3359,7 @@ void ImageWriter::UpdateOatFileLayout(size_t oat_index,
cur_image_info.oat_data_begin_ = cur_image_info.oat_file_begin_ + oat_data_offset;
cur_image_info.oat_size_ = oat_data_size;
- if (compile_app_image_) {
+ if (compiler_options_.IsAppImage()) {
CHECK_EQ(oat_filenames_.size(), 1u) << "App image should have no next image.";
return;
}
@@ -3316,7 +3398,6 @@ void ImageWriter::UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_hea
ImageWriter::ImageWriter(
const CompilerOptions& compiler_options,
uintptr_t image_begin,
- bool compile_app_image,
ImageHeader::StorageMode image_storage_mode,
const std::vector<const char*>& oat_filenames,
const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
@@ -3324,7 +3405,6 @@ ImageWriter::ImageWriter(
: compiler_options_(compiler_options),
global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
image_objects_offset_begin_(0),
- compile_app_image_(compile_app_image),
target_ptr_size_(InstructionSetPointerSize(compiler_options.GetInstructionSet())),
image_infos_(oat_filenames.size()),
dirty_methods_(0u),
@@ -3334,9 +3414,11 @@ ImageWriter::ImageWriter(
oat_filenames_(oat_filenames),
dex_file_oat_index_map_(dex_file_oat_index_map),
dirty_image_objects_(dirty_image_objects) {
+ DCHECK(compiler_options.IsBootImage() || compiler_options.IsAppImage());
CHECK_NE(image_begin, 0U);
std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
- CHECK_EQ(compile_app_image, !Runtime::Current()->GetHeap()->GetBootImageSpaces().empty())
+ CHECK_EQ(compiler_options.IsBootImage(),
+ Runtime::Current()->GetHeap()->GetBootImageSpaces().empty())
<< "Compiling a boot image should occur iff there are no boot image spaces loaded";
}
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index ccd529aa13..3c377a3ca5 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -78,7 +78,6 @@ class ImageWriter final {
public:
ImageWriter(const CompilerOptions& compiler_options,
uintptr_t image_begin,
- bool compile_app_image,
ImageHeader::StorageMode image_storage_mode,
const std::vector<const char*>& oat_filenames,
const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
@@ -112,10 +111,7 @@ class ImageWriter final {
return true;
}
- ObjPtr<mirror::ClassLoader> GetClassLoader() {
- CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
- return compile_app_image_ ? *class_loaders_.begin() : nullptr;
- }
+ ObjPtr<mirror::ClassLoader> GetClassLoader();
template <typename T>
T* GetImageAddress(T* object) const REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -460,9 +456,18 @@ class ImageWriter final {
// Remove unwanted classes from various roots.
void PruneNonImageClasses() REQUIRES_SHARED(Locks::mutator_lock_);
- // Remove unwanted classes from the DexCache roots and preload deterministic DexCache contents.
- void PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
- ObjPtr<mirror::ClassLoader> class_loader)
+ // Remove unwanted classes from the DexCache roots.
+ void PruneDexCache(ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::classlinker_classes_lock_);
+
+ // Preload deterministic DexCache contents.
+ void PreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::classlinker_classes_lock_);
+
+ // Find dex caches for pruning or preloading.
+ std::vector<ObjPtr<mirror::DexCache>> FindDexCaches(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::classlinker_classes_lock_);
@@ -745,9 +750,6 @@ class ImageWriter final {
// Oat index map for objects.
std::unordered_map<mirror::Object*, uint32_t> oat_index_map_;
- // Boolean flags.
- const bool compile_app_image_;
-
// Size of pointers on the target architecture.
PointerSize target_ptr_size_;
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 28287bd083..7f2877fc08 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -1500,7 +1500,8 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
}
ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(Thread::Current(), *dex_file);
const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
- mirror::Class* klass = dex_cache->GetResolvedType(class_def.class_idx_);
+ ObjPtr<mirror::Class> klass =
+ class_linker_->LookupResolvedType(class_def.class_idx_, dex_cache, class_loader_);
if (klass != nullptr) {
for (ArtMethod& method : klass->GetCopiedMethods(pointer_size_)) {
// Find origin method. Declaring class and dex_method_idx
@@ -1550,24 +1551,11 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(self, *dex_file_);
ArtMethod* resolved_method;
if (writer_->GetCompilerOptions().IsBootImage()) {
- const InvokeType invoke_type = method.GetInvokeType(
- dex_file_->GetClassDef(class_def_index_).access_flags_);
- // Unchecked as we hold mutator_lock_ on entry.
- ScopedObjectAccessUnchecked soa(self);
- StackHandleScope<1> hs(self);
- resolved_method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
- method.GetIndex(),
- hs.NewHandle(dex_cache),
- ScopedNullHandle<mirror::ClassLoader>(),
- /* referrer */ nullptr,
- invoke_type);
+ resolved_method = class_linker_->LookupResolvedMethod(
+ method.GetIndex(), dex_cache, /*class_loader=*/ nullptr);
if (resolved_method == nullptr) {
- LOG(FATAL_WITHOUT_ABORT) << "Unexpected failure to resolve a method: "
+ LOG(FATAL) << "Unexpected failure to look up a method: "
<< dex_file_->PrettyMethod(method.GetIndex(), true);
- self->AssertPendingException();
- mirror::Throwable* exc = self->GetException();
- std::string dump = exc->Dump();
- LOG(FATAL) << dump;
UNREACHABLE();
}
} else {
diff --git a/dex2oat/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h
index 9725570570..56ff0ef3ca 100644
--- a/dex2oat/linker/relative_patcher_test.h
+++ b/dex2oat/linker/relative_patcher_test.h
@@ -50,7 +50,7 @@ class RelativePatcherTest : public CommonCompilerTest {
compiled_methods_(),
patched_code_(),
output_(),
- out_("test output stream", &output_) {
+ out_(nullptr) {
// Override CommonCompilerTest's defaults.
instruction_set_ = instruction_set;
number_of_threads_ = 1u;
@@ -61,10 +61,7 @@ class RelativePatcherTest : public CommonCompilerTest {
OverrideInstructionSetFeatures(instruction_set_, variant_);
CommonCompilerTest::SetUp();
- patcher_ = RelativePatcher::Create(compiler_options_->GetInstructionSet(),
- compiler_options_->GetInstructionSetFeatures(),
- &thunk_provider_,
- &method_offset_map_);
+ Reset();
}
void TearDown() override {
@@ -73,6 +70,24 @@ class RelativePatcherTest : public CommonCompilerTest {
CommonCompilerTest::TearDown();
}
+ // Reset the helper to start another test. Creating and tearing down the Runtime is expensive,
+ // so we merge related tests together.
+ void Reset() {
+ thunk_provider_.Reset();
+ method_offset_map_.map.clear();
+ patcher_ = RelativePatcher::Create(compiler_options_->GetInstructionSet(),
+ compiler_options_->GetInstructionSetFeatures(),
+ &thunk_provider_,
+ &method_offset_map_);
+ bss_begin_ = 0u;
+ string_index_to_offset_map_.clear();
+ compiled_method_refs_.clear();
+ compiled_methods_.clear();
+ patched_code_.clear();
+ output_.clear();
+ out_.reset(new VectorOutputStream("test output stream", &output_));
+ }
+
MethodReference MethodRef(uint32_t method_idx) {
CHECK_NE(method_idx, 0u);
return MethodReference(nullptr, method_idx);
@@ -127,7 +142,7 @@ class RelativePatcherTest : public CommonCompilerTest {
DCHECK(output_.empty());
uint8_t dummy_trampoline[kTrampolineSize];
memset(dummy_trampoline, 0, sizeof(dummy_trampoline));
- out_.WriteFully(dummy_trampoline, kTrampolineSize);
+ out_->WriteFully(dummy_trampoline, kTrampolineSize);
offset = kTrampolineSize;
static const uint8_t kPadding[] = {
0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
@@ -135,14 +150,14 @@ class RelativePatcherTest : public CommonCompilerTest {
uint8_t dummy_header[sizeof(OatQuickMethodHeader)];
memset(dummy_header, 0, sizeof(dummy_header));
for (auto& compiled_method : compiled_methods_) {
- offset = patcher_->WriteThunks(&out_, offset);
+ offset = patcher_->WriteThunks(out_.get(), offset);
uint32_t alignment_size = CodeAlignmentSize(offset);
CHECK_LE(alignment_size, sizeof(kPadding));
- out_.WriteFully(kPadding, alignment_size);
+ out_->WriteFully(kPadding, alignment_size);
offset += alignment_size;
- out_.WriteFully(dummy_header, sizeof(OatQuickMethodHeader));
+ out_->WriteFully(dummy_header, sizeof(OatQuickMethodHeader));
offset += sizeof(OatQuickMethodHeader);
ArrayRef<const uint8_t> code = compiled_method->GetQuickCode();
if (!compiled_method->GetPatches().empty()) {
@@ -179,10 +194,10 @@ class RelativePatcherTest : public CommonCompilerTest {
}
}
}
- out_.WriteFully(&code[0], code.size());
+ out_->WriteFully(&code[0], code.size());
offset += code.size();
}
- offset = patcher_->WriteThunks(&out_, offset);
+ offset = patcher_->WriteThunks(out_.get(), offset);
CHECK_EQ(offset, output_size);
CHECK_EQ(output_.size(), output_size);
}
@@ -270,6 +285,10 @@ class RelativePatcherTest : public CommonCompilerTest {
*debug_name = value.GetDebugName();
}
+ void Reset() {
+ thunk_map_.clear();
+ }
+
private:
class ThunkKey {
public:
@@ -342,7 +361,7 @@ class RelativePatcherTest : public CommonCompilerTest {
std::vector<std::unique_ptr<CompiledMethod>> compiled_methods_;
std::vector<uint8_t> patched_code_;
std::vector<uint8_t> output_;
- VectorOutputStream out_;
+ std::unique_ptr<VectorOutputStream> out_;
};
} // namespace linker
diff --git a/disassembler/Android.bp b/disassembler/Android.bp
index a7c1802515..5aa159e98b 100644
--- a/disassembler/Android.bp
+++ b/disassembler/Android.bp
@@ -69,8 +69,7 @@ art_cc_library {
defaults: ["libart-disassembler-defaults"],
shared_libs: [
// For disassembler_arm*.
- "libvixl-arm",
- "libvixl-arm64",
+ "libvixl",
],
}
@@ -82,7 +81,6 @@ art_cc_library {
],
shared_libs: [
// For disassembler_arm*.
- "libvixld-arm",
- "libvixld-arm64",
+ "libvixld",
],
}
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 5010f683d7..6a667bc7a7 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -54,6 +54,10 @@ cc_defaults {
"libziparchive",
"libz",
],
+ // Exclude the version script from Darwin host since it's not
+ // supported by the linker there. That means ASan checks on Darwin
+ // might trigger ODR violations.
+ version_script: "libartbase.map",
},
host: {
shared_libs: [
@@ -61,16 +65,31 @@ cc_defaults {
"libz",
],
},
+ linux_glibc: {
+ version_script: "libartbase.map",
+ },
+ windows: {
+ version_script: "libartbase.map",
+ },
},
generated_sources: ["art_libartbase_operator_srcs"],
cflags: ["-DBUILDING_LIBART=1"],
shared_libs: [
"liblog",
- // For ashmem.
+ // For ashmem.
"libcutils",
// For common macros.
"libbase",
],
+
+ // Utilities used by various ART libs and tools are linked in statically
+ // here to avoid shared lib dependencies outside the ART APEX. No target
+ // there should depend on these separately.
+ whole_static_libs: [
+ "liblz4",
+ "liblzma",
+ ],
+
export_include_dirs: ["."],
// ART's macros.h depends on libbase's macros.h.
// Note: runtime_options.h depends on cmdline. But we don't really want to export this
diff --git a/libartbase/base/membarrier.cc b/libartbase/base/membarrier.cc
index def949e3a7..4c86b6b0f6 100644
--- a/libartbase/base/membarrier.cc
+++ b/libartbase/base/membarrier.cc
@@ -25,7 +25,6 @@
#if defined(__BIONIC__)
#include <atomic>
-#include <android/get_device_api_level.h>
#include <linux/membarrier.h>
#define CHECK_MEMBARRIER_CMD(art_value, membarrier_value) \
diff --git a/libartbase/libartbase.map b/libartbase/libartbase.map
new file mode 100644
index 0000000000..6249930dc1
--- /dev/null
+++ b/libartbase/libartbase.map
@@ -0,0 +1,15 @@
+# This is used only to hide data symbols that get imported through
+# whole_static_libs, or else they might trigger the ASan odr-violation check.
+# Before adding symbols here, please make sure that it doesn't give rise to a
+# real ODR problem. All these symbols are either in .rodata or .data.rel.ro
+# sections.
+LIBARTBASE {
+ local:
+ PPMD7_kExpEscape;
+ XZ_SIG;
+ g_AlignedAlloc;
+ g_Alloc;
+ g_BigAlloc;
+ g_MidAlloc;
+ k7zSignature;
+};
diff --git a/libdexfile/dex/dex_instruction.h b/libdexfile/dex/dex_instruction.h
index ad8a1842fc..4b389043d7 100644
--- a/libdexfile/dex/dex_instruction.h
+++ b/libdexfile/dex/dex_instruction.h
@@ -173,7 +173,7 @@ class Instruction {
};
enum VerifyFlag : uint32_t {
- kVerifyNone = 0x0000000,
+ kVerifyNothing = 0x0000000,
kVerifyRegA = 0x0000001,
kVerifyRegAWide = 0x0000002,
kVerifyRegB = 0x0000004,
diff --git a/libdexfile/dex/dex_instruction_list.h b/libdexfile/dex/dex_instruction_list.h
index 9f0aba421a..b9540a6e6f 100644
--- a/libdexfile/dex/dex_instruction_list.h
+++ b/libdexfile/dex/dex_instruction_list.h
@@ -19,7 +19,7 @@
// V(opcode, instruction_code, name, format, index, flags, extended_flags, verifier_flags);
#define DEX_INSTRUCTION_LIST(V) \
- V(0x00, NOP, "nop", k10x, kIndexNone, kContinue, 0, kVerifyNone) \
+ V(0x00, NOP, "nop", k10x, kIndexNone, kContinue, 0, kVerifyNothing) \
V(0x01, MOVE, "move", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
V(0x02, MOVE_FROM16, "move/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
V(0x03, MOVE_16, "move/16", k32x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
@@ -33,7 +33,7 @@
V(0x0B, MOVE_RESULT_WIDE, "move-result-wide", k11x, kIndexNone, kContinue, 0, kVerifyRegAWide) \
V(0x0C, MOVE_RESULT_OBJECT, "move-result-object", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
V(0x0D, MOVE_EXCEPTION, "move-exception", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
- V(0x0E, RETURN_VOID, "return-void", k10x, kIndexNone, kReturn, 0, kVerifyNone) \
+ V(0x0E, RETURN_VOID, "return-void", k10x, kIndexNone, kReturn, 0, kVerifyNothing) \
V(0x0F, RETURN, "return", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \
V(0x10, RETURN_WIDE, "return-wide", k11x, kIndexNone, kReturn, 0, kVerifyRegAWide) \
V(0x11, RETURN_OBJECT, "return-object", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \
@@ -134,7 +134,7 @@
V(0x70, INVOKE_DIRECT, "invoke-direct", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
V(0x71, INVOKE_STATIC, "invoke-static", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArg) \
V(0x72, INVOKE_INTERFACE, "invoke-interface", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
- V(0x73, RETURN_VOID_NO_BARRIER, "return-void-no-barrier", k10x, kIndexNone, kReturn, 0, kVerifyNone) \
+ V(0x73, RETURN_VOID_NO_BARRIER, "return-void-no-barrier", k10x, kIndexNone, kReturn, 0, kVerifyNothing) \
V(0x74, INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
V(0x75, INVOKE_SUPER_RANGE, "invoke-super/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
V(0x76, INVOKE_DIRECT_RANGE, "invoke-direct/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
diff --git a/libdexfile/dex/dex_instruction_test.cc b/libdexfile/dex/dex_instruction_test.cc
index 02400f4a14..3f79abf1d8 100644
--- a/libdexfile/dex/dex_instruction_test.cc
+++ b/libdexfile/dex/dex_instruction_test.cc
@@ -26,7 +26,7 @@ TEST(StaticGetters, PropertiesOfNopTest) {
EXPECT_EQ(Instruction::k10x, Instruction::FormatOf(nop));
EXPECT_EQ(Instruction::kIndexNone, Instruction::IndexTypeOf(nop));
EXPECT_EQ(Instruction::kContinue, Instruction::FlagsOf(nop));
- EXPECT_EQ(Instruction::kVerifyNone, Instruction::VerifyFlagsOf(nop));
+ EXPECT_EQ(Instruction::kVerifyNothing, Instruction::VerifyFlagsOf(nop));
}
static void Build45cc(uint8_t num_args, uint16_t method_idx, uint16_t proto_idx,
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index 596a94664d..45f853b06e 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -100,8 +100,7 @@ art_cc_binary {
],
static_libs: [
"libart-disassembler",
- "libvixl-arm",
- "libvixl-arm64",
+ "libvixl",
],
}
@@ -124,8 +123,7 @@ art_cc_binary {
},
static_libs: [
"libartd-disassembler",
- "libvixld-arm",
- "libvixld-arm64",
+ "libvixld",
],
group_static_libs: true,
}
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 86ee952022..410901ea47 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -377,7 +377,6 @@ libart_cc_defaults {
include_dirs: [
"art/sigchainlib",
"external/icu/icu4c/source/common",
- "external/lz4/lib",
"external/zlib",
],
header_libs: [
@@ -390,7 +389,6 @@ libart_cc_defaults {
"libnativebridge",
"libnativeloader",
"libbacktrace",
- "liblz4",
"liblog",
// For atrace, properties, ashmem, set_sched_policy.
"libcutils",
@@ -422,8 +420,6 @@ libart_static_cc_defaults {
"libbase",
"libcutils",
"liblog",
- "liblz4",
- "liblzma",
"libnativebridge",
"libnativeloader",
"libunwindstack",
@@ -661,8 +657,7 @@ art_cc_test {
],
shared_libs: [
"libartd-compiler",
- "libvixld-arm",
- "libvixld-arm64",
+ "libvixld",
],
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index cc4f56cc06..35379cc251 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1543,26 +1543,6 @@ void AppImageLoadingHelper::HandleAppImageStrings(gc::space::ImageSpace* space)
}
}
-// Update the class loader. Should only be used on classes in the image space.
-class UpdateClassLoaderVisitor {
- public:
- UpdateClassLoaderVisitor(gc::space::ImageSpace* space, ObjPtr<mirror::ClassLoader> class_loader)
- : space_(space),
- class_loader_(class_loader) {}
-
- bool operator()(ObjPtr<mirror::Class> klass) const REQUIRES_SHARED(Locks::mutator_lock_) {
- // Do not update class loader for boot image classes where the app image
- // class loader is only the initiating loader but not the defining loader.
- if (klass->GetClassLoader() != nullptr) {
- klass->SetClassLoader(class_loader_);
- }
- return true;
- }
-
- gc::space::ImageSpace* const space_;
- ObjPtr<mirror::ClassLoader> const class_loader_;
-};
-
static std::unique_ptr<const DexFile> OpenOatDexFile(const OatFile* oat_file,
const char* location,
std::string* error_msg)
@@ -2036,9 +2016,17 @@ bool ClassLinker::AddImageSpace(
ScopedTrace trace("AppImage:UpdateClassLoaders");
// Update class loader and resolved strings. If added_class_table is false, the resolved
// strings were forwarded UpdateAppImageClassLoadersAndDexCaches.
- UpdateClassLoaderVisitor visitor(space, class_loader.Get());
+ ObjPtr<mirror::ClassLoader> loader(class_loader.Get());
for (const ClassTable::TableSlot& root : temp_set) {
- visitor(root.Read());
+ // Note: We probably don't need the read barrier unless we copy the app image objects into
+ // the region space.
+ ObjPtr<mirror::Class> klass(root.Read());
+ // Do not update class loader for boot image classes where the app image
+ // class loader is only the initiating loader but not the defining loader.
+ // Avoid read barrier since we are comparing against null.
+ if (klass->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
+ klass->SetClassLoader</*kCheckTransaction=*/ false>(loader);
+ }
}
}
@@ -2527,6 +2515,35 @@ ClassPathEntry FindInClassPath(const char* descriptor,
return ClassPathEntry(nullptr, nullptr);
}
+bool ClassLinker::FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self,
+ const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader,
+ /*out*/ ObjPtr<mirror::Class>* result) {
+ ArtField* field =
+ jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader.Get());
+ if (raw_shared_libraries == nullptr) {
+ return true;
+ }
+
+ StackHandleScope<2> hs(self);
+ Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries(
+ hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
+ MutableHandle<mirror::ClassLoader> temp_loader = hs.NewHandle<mirror::ClassLoader>(nullptr);
+ for (int32_t i = 0; i < shared_libraries->GetLength(); ++i) {
+ temp_loader.Assign(shared_libraries->Get(i));
+ if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, temp_loader, result)) {
+ return false; // One of the shared libraries is not supported.
+ }
+ if (*result != nullptr) {
+ return true; // Found the class up the chain.
+ }
+ }
+ return true;
+}
+
bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Thread* self,
const char* descriptor,
@@ -2542,6 +2559,7 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl
if (IsPathOrDexClassLoader(soa, class_loader)) {
// For regular path or dex class loader the search order is:
// - parent
+ // - shared libraries
// - class loader dex files
// Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
@@ -2554,6 +2572,13 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl
return true; // Found the class up the chain.
}
+ if (!FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result)) {
+ return false; // One of the shared library loader is not supported.
+ }
+ if (*result != nullptr) {
+ return true; // Found the class in a shared library.
+ }
+
// Search the current class loader classpath.
*result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
return true;
@@ -2562,6 +2587,7 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl
if (IsDelegateLastClassLoader(soa, class_loader)) {
// For delegate last, the search order is:
// - boot class path
+ // - shared libraries
// - class loader dex files
// - parent
*result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
@@ -2569,6 +2595,13 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl
return true; // The class is part of the boot class path.
}
+ if (!FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result)) {
+ return false; // One of the shared library loader is not supported.
+ }
+ if (*result != nullptr) {
+ return true; // Found the class in a shared library.
+ }
+
*result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
if (*result != nullptr) {
return true; // Found the class in the current class loader
@@ -5070,8 +5103,10 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
ArtField* resolved_field = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
if (resolved_field == nullptr) {
// Populating cache of a dex file which defines `klass` should always be allowed.
- DCHECK_EQ(hiddenapi::GetMemberAction(
- field, class_loader.Get(), dex_cache.Get(), hiddenapi::kNone), hiddenapi::kAllow);
+ DCHECK(!hiddenapi::ShouldDenyAccessToMember(
+ field,
+ hiddenapi::AccessContext(class_loader.Get(), dex_cache.Get()),
+ hiddenapi::AccessMethod::kNone));
dex_cache->SetResolvedField(field_idx, field, image_pointer_size_);
} else {
DCHECK_EQ(field, resolved_field);
@@ -8102,8 +8137,9 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass,
}
DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);
if (resolved != nullptr &&
- hiddenapi::GetMemberAction(
- resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+ hiddenapi::ShouldDenyAccessToMember(resolved,
+ hiddenapi::AccessContext(class_loader, dex_cache),
+ hiddenapi::AccessMethod::kLinking)) {
resolved = nullptr;
}
if (resolved != nullptr) {
@@ -8133,11 +8169,9 @@ static bool CheckNoSuchMethod(ArtMethod* method,
ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
return method == nullptr ||
- hiddenapi::GetMemberAction(method,
- class_loader,
- dex_cache,
- hiddenapi::kNone) // do not print warnings
- == hiddenapi::kDeny;
+ hiddenapi::ShouldDenyAccessToMember(method,
+ hiddenapi::AccessContext(class_loader, dex_cache),
+ hiddenapi::AccessMethod::kNone); // no warnings
}
ArtMethod* ClassLinker::FindIncompatibleMethod(ObjPtr<mirror::Class> klass,
@@ -8273,8 +8307,10 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx,
resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_);
}
if (resolved != nullptr &&
- hiddenapi::GetMemberAction(
- resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) {
+ hiddenapi::ShouldDenyAccessToMember(
+ resolved,
+ hiddenapi::AccessContext(class_loader.Get(), dex_cache.Get()),
+ hiddenapi::AccessMethod::kLinking)) {
resolved = nullptr;
}
return resolved;
@@ -8373,8 +8409,9 @@ ArtField* ClassLinker::FindResolvedField(ObjPtr<mirror::Class> klass,
}
if (resolved != nullptr &&
- hiddenapi::GetMemberAction(
- resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+ hiddenapi::ShouldDenyAccessToMember(resolved,
+ hiddenapi::AccessContext(class_loader, dex_cache),
+ hiddenapi::AccessMethod::kLinking)) {
resolved = nullptr;
}
@@ -8399,8 +8436,9 @@ ArtField* ClassLinker::FindResolvedFieldJLS(ObjPtr<mirror::Class> klass,
resolved = mirror::Class::FindField(self, klass, name, type);
if (resolved != nullptr &&
- hiddenapi::GetMemberAction(
- resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+ hiddenapi::ShouldDenyAccessToMember(resolved,
+ hiddenapi::AccessContext(class_loader, dex_cache),
+ hiddenapi::AccessMethod::kLinking)) {
resolved = nullptr;
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 60e68d5f78..a65299a514 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -868,6 +868,15 @@ class ClassLinker {
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
+ bool FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self,
+ const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader,
+ /*out*/ ObjPtr<mirror::Class>* result)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
// Finds the class in the classpath of the given class loader. It only searches the class loader
// dex files and does not recurse into its parent.
// The method checks that the provided class loader is either a PathClassLoader or a
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index ed3a18db28..b11e368871 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -66,7 +66,7 @@ bool DexoptTest::Dex2Oat(const std::vector<std::string>& args, std::string* erro
}
runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
- if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) {
+ if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kDisabled) {
argv.push_back("--runtime-arg");
argv.push_back("-Xhidden-api-checks");
}
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index f3552765c5..3b7b938d50 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -44,38 +44,53 @@ static constexpr bool kLogAllAccesses = false;
static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
switch (value) {
- case kNone:
+ case AccessMethod::kNone:
LOG(FATAL) << "Internal access to hidden API should not be logged";
UNREACHABLE();
- case kReflection:
+ case AccessMethod::kReflection:
os << "reflection";
break;
- case kJNI:
+ case AccessMethod::kJNI:
os << "JNI";
break;
- case kLinking:
+ case AccessMethod::kLinking:
os << "linking";
break;
}
return os;
}
-static constexpr bool EnumsEqual(EnforcementPolicy policy, hiddenapi::ApiList apiList) {
- return static_cast<int>(policy) == static_cast<int>(apiList);
-}
-
-// GetMemberAction-related static_asserts.
-static_assert(
- EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, hiddenapi::ApiList::kDarkGreylist) &&
- EnumsEqual(EnforcementPolicy::kBlacklistOnly, hiddenapi::ApiList::kBlacklist),
- "Mismatch between EnforcementPolicy and ApiList enums");
-static_assert(
- EnforcementPolicy::kJustWarn < EnforcementPolicy::kDarkGreyAndBlackList &&
- EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
- "EnforcementPolicy values ordering not correct");
-
namespace detail {
+// Do not change the values of items in this enum, as they are written to the
+// event log for offline analysis. Any changes will interfere with that analysis.
+enum AccessContextFlags {
+ // Accessed member is a field if this bit is set, else a method
+ kMemberIsField = 1 << 0,
+ // Indicates if access was denied to the member, instead of just printing a warning.
+ kAccessDenied = 1 << 1,
+};
+
+static int32_t GetMaxAllowedSdkVersionForApiList(ApiList api_list) {
+ SdkCodes sdk = SdkCodes::kVersionNone;
+ switch (api_list) {
+ case ApiList::kWhitelist:
+ case ApiList::kLightGreylist:
+ sdk = SdkCodes::kVersionUnlimited;
+ break;
+ case ApiList::kDarkGreylist:
+ sdk = SdkCodes::kVersionO_MR1;
+ break;
+ case ApiList::kBlacklist:
+ sdk = SdkCodes::kVersionNone;
+ break;
+ case ApiList::kNoList:
+ LOG(FATAL) << "Unexpected value";
+ UNREACHABLE();
+ }
+ return static_cast<int32_t>(sdk);
+}
+
MemberSignature::MemberSignature(ArtField* field) {
class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_);
member_name_ = field->GetName();
@@ -137,6 +152,7 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method, hiddenapi::Api
LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ")
<< Dumpable<MemberSignature>(*this) << " (" << list << ", " << access_method << ")";
}
+
#ifdef ART_TARGET_ANDROID
// Convert an AccessMethod enum to a value for logging from the proto enum.
// This method may look odd (the enum values are current the same), but it
@@ -145,13 +161,13 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method, hiddenapi::Api
// future.
inline static int32_t GetEnumValueForLog(AccessMethod access_method) {
switch (access_method) {
- case kNone:
+ case AccessMethod::kNone:
return android::metricslogger::ACCESS_METHOD_NONE;
- case kReflection:
+ case AccessMethod::kReflection:
return android::metricslogger::ACCESS_METHOD_REFLECTION;
- case kJNI:
+ case AccessMethod::kJNI:
return android::metricslogger::ACCESS_METHOD_JNI;
- case kLinking:
+ case AccessMethod::kLinking:
return android::metricslogger::ACCESS_METHOD_LINKING;
default:
DCHECK(false);
@@ -159,9 +175,9 @@ inline static int32_t GetEnumValueForLog(AccessMethod access_method) {
}
#endif
-void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) {
+void MemberSignature::LogAccessToEventLog(AccessMethod access_method, bool access_denied) {
#ifdef ART_TARGET_ANDROID
- if (access_method == kLinking || access_method == kNone) {
+ if (access_method == AccessMethod::kLinking || access_method == AccessMethod::kNone) {
// Linking warnings come from static analysis/compilation of the bytecode
// and can contain false positives (i.e. code that is never run). We choose
// not to log these in the event log.
@@ -170,7 +186,7 @@ void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action act
}
ComplexEventLogger log_maker(ACTION_HIDDEN_API_ACCESSED);
log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_METHOD, GetEnumValueForLog(access_method));
- if (action_taken == kDeny) {
+ if (access_denied) {
log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_DENIED, 1);
}
const std::string& package_name = Runtime::Current()->GetProcessPackageName();
@@ -183,10 +199,42 @@ void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action act
log_maker.Record();
#else
UNUSED(access_method);
- UNUSED(action_taken);
+ UNUSED(access_denied);
#endif
}
+void MemberSignature::NotifyHiddenApiListener(AccessMethod access_method) {
+ if (access_method != AccessMethod::kReflection && access_method != AccessMethod::kJNI) {
+ // We can only up-call into Java during reflection and JNI down-calls.
+ return;
+ }
+
+ Runtime* runtime = Runtime::Current();
+ if (!runtime->IsAotCompiler()) {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+
+ ScopedLocalRef<jobject> consumer_object(soa.Env(),
+ soa.Env()->GetStaticObjectField(
+ WellKnownClasses::dalvik_system_VMRuntime,
+ WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer));
+ // If the consumer is non-null, we call back to it to let it know that we
+ // have encountered an API that's in one of our lists.
+ if (consumer_object != nullptr) {
+ std::ostringstream member_signature_str;
+ Dump(member_signature_str);
+
+ ScopedLocalRef<jobject> signature_str(
+ soa.Env(),
+ soa.Env()->NewStringUTF(member_signature_str.str().c_str()));
+
+ // Call through to Consumer.accept(String memberSignature);
+ soa.Env()->CallVoidMethod(consumer_object.get(),
+ WellKnownClasses::java_util_function_Consumer_accept,
+ signature_str.get());
+ }
+ }
+}
+
static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) {
return true;
}
@@ -205,116 +253,68 @@ static ALWAYS_INLINE void MaybeWhitelistMember(Runtime* runtime, T* member)
}
template<typename T>
-Action GetMemberActionImpl(T* member,
- hiddenapi::ApiList api_list,
- Action action,
- AccessMethod access_method) {
- DCHECK_NE(action, kAllow);
-
- // Get the signature, we need it later.
- MemberSignature member_signature(member);
+bool ShouldDenyAccessToMemberImpl(T* member,
+ hiddenapi::ApiList api_list,
+ AccessMethod access_method) {
+ DCHECK(member != nullptr);
Runtime* runtime = Runtime::Current();
+ EnforcementPolicy policy = runtime->GetHiddenApiEnforcementPolicy();
+
+ const bool deny_access =
+ (policy == EnforcementPolicy::kEnabled) &&
+ (runtime->GetTargetSdkVersion() > GetMaxAllowedSdkVersionForApiList(api_list));
+
+ MemberSignature member_signature(member);
// Check for an exemption first. Exempted APIs are treated as white list.
- // We only do this if we're about to deny, or if the app is debuggable. This is because:
- // - we only print a warning for light greylist violations for debuggable apps
- // - for non-debuggable apps, there is no distinction between light grey & whitelisted APIs.
- // - we want to avoid the overhead of checking for exemptions for light greylisted APIs whenever
- // possible.
- const bool shouldWarn = kLogAllAccesses || runtime->IsJavaDebuggable();
- if (shouldWarn || action == kDeny) {
- if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
- action = kAllow;
- // Avoid re-examining the exemption list next time.
- // Note this results in no warning for the member, which seems like what one would expect.
- // Exemptions effectively adds new members to the whitelist.
- MaybeWhitelistMember(runtime, member);
- return kAllow;
- }
+ if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
+ // Avoid re-examining the exemption list next time.
+ // Note this results in no warning for the member, which seems like what one would expect.
+ // Exemptions effectively adds new members to the whitelist.
+ MaybeWhitelistMember(runtime, member);
+ return false;
+ }
- if (access_method != kNone) {
- // Print a log message with information about this class member access.
- // We do this if we're about to block access, or the app is debuggable.
+ if (access_method != AccessMethod::kNone) {
+ // Print a log message with information about this class member access.
+ // We do this if we're about to deny access, or the app is debuggable.
+ if (kLogAllAccesses || deny_access || runtime->IsJavaDebuggable()) {
member_signature.WarnAboutAccess(access_method, api_list);
}
- }
- if (kIsTargetBuild && !kIsTargetLinux) {
- uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
- // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
- static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
- if (eventLogSampleRate != 0 &&
- (static_cast<uint32_t>(std::rand()) & 0xffff) < eventLogSampleRate) {
- member_signature.LogAccessToEventLog(access_method, action);
+ // If there is a StrictMode listener, notify it about this violation.
+ member_signature.NotifyHiddenApiListener(access_method);
+
+ // If event log sampling is enabled, report this violation.
+ if (kIsTargetBuild && !kIsTargetLinux) {
+ uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
+ // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
+ static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
+ if (eventLogSampleRate != 0 &&
+ (static_cast<uint32_t>(std::rand()) & 0xffff) < eventLogSampleRate) {
+ member_signature.LogAccessToEventLog(access_method, deny_access);
+ }
}
- }
-
- if (action == kDeny) {
- // Block access
- return action;
- }
-
- // Allow access to this member but print a warning.
- DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
- if (access_method != kNone) {
- // Depending on a runtime flag, we might move the member into whitelist and
- // skip the warning the next time the member is accessed.
- MaybeWhitelistMember(runtime, member);
-
- // If this action requires a UI warning, set the appropriate flag.
- if (shouldWarn &&
- (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag())) {
- runtime->SetPendingHiddenApiWarning(true);
+ // If this access was not denied, move the member into whitelist and skip
+ // the warning the next time the member is accessed.
+ if (!deny_access) {
+ MaybeWhitelistMember(runtime, member);
}
}
- return action;
+ return deny_access;
}
// Need to instantiate this.
-template Action GetMemberActionImpl<ArtField>(ArtField* member,
- hiddenapi::ApiList api_list,
- Action action,
- AccessMethod access_method);
-template Action GetMemberActionImpl<ArtMethod>(ArtMethod* member,
- hiddenapi::ApiList api_list,
- Action action,
- AccessMethod access_method);
+template bool ShouldDenyAccessToMemberImpl<ArtField>(ArtField* member,
+ hiddenapi::ApiList api_list,
+ AccessMethod access_method);
+template bool ShouldDenyAccessToMemberImpl<ArtMethod>(ArtMethod* member,
+ hiddenapi::ApiList api_list,
+ AccessMethod access_method);
} // namespace detail
-template<typename T>
-void NotifyHiddenApiListener(T* member) {
- Runtime* runtime = Runtime::Current();
- if (!runtime->IsAotCompiler()) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
-
- ScopedLocalRef<jobject> consumer_object(soa.Env(),
- soa.Env()->GetStaticObjectField(
- WellKnownClasses::dalvik_system_VMRuntime,
- WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer));
- // If the consumer is non-null, we call back to it to let it know that we
- // have encountered an API that's in one of our lists.
- if (consumer_object != nullptr) {
- detail::MemberSignature member_signature(member);
- std::ostringstream member_signature_str;
- member_signature.Dump(member_signature_str);
-
- ScopedLocalRef<jobject> signature_str(
- soa.Env(),
- soa.Env()->NewStringUTF(member_signature_str.str().c_str()));
-
- // Call through to Consumer.accept(String memberSignature);
- soa.Env()->CallVoidMethod(consumer_object.get(),
- WellKnownClasses::java_util_function_Consumer_accept,
- signature_str.get());
- }
- }
-}
-
-template void NotifyHiddenApiListener<ArtMethod>(ArtMethod* member);
-template void NotifyHiddenApiListener<ArtField>(ArtField* member);
-
} // namespace hiddenapi
} // namespace art
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 57f1a599cf..ed00e2a892 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -32,11 +32,10 @@ namespace hiddenapi {
// This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in
// frameworks/base/core/java/android/content/pm/ApplicationInfo.java
enum class EnforcementPolicy {
- kNoChecks = 0,
+ kDisabled = 0,
kJustWarn = 1, // keep checks enabled, but allow everything (enables logging)
- kDarkGreyAndBlackList = 2, // ban dark grey & blacklist
- kBlacklistOnly = 3, // ban blacklist violations only
- kMax = kBlacklistOnly,
+ kEnabled = 2, // ban dark grey & blacklist
+ kMax = kEnabled,
};
inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) {
@@ -45,55 +44,58 @@ inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) {
return static_cast<EnforcementPolicy>(api_policy_int);
}
-enum Action {
- kAllow,
- kAllowButWarn,
- kAllowButWarnAndToast,
- kDeny
-};
-
-enum AccessMethod {
+enum class AccessMethod {
kNone, // internal test that does not correspond to an actual access by app
kReflection,
kJNI,
kLinking,
};
-// Do not change the values of items in this enum, as they are written to the
-// event log for offline analysis. Any changes will interfere with that analysis.
-enum AccessContextFlags {
- // Accessed member is a field if this bit is set, else a method
- kMemberIsField = 1 << 0,
- // Indicates if access was denied to the member, instead of just printing a warning.
- kAccessDenied = 1 << 1,
-};
+struct AccessContext {
+ public:
+ explicit AccessContext(bool is_trusted) : is_trusted_(is_trusted) {}
-inline Action GetActionFromAccessFlags(ApiList api_list) {
- if (api_list == ApiList::kWhitelist) {
- return kAllow;
- }
+ explicit AccessContext(ObjPtr<mirror::Class> klass) : is_trusted_(GetIsTrusted(klass)) {}
- EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
- if (policy == EnforcementPolicy::kNoChecks) {
- // Exit early. Nothing to enforce.
- return kAllow;
- }
+ AccessContext(ObjPtr<mirror::ClassLoader> class_loader, ObjPtr<mirror::DexCache> dex_cache)
+ : is_trusted_(GetIsTrusted(class_loader, dex_cache)) {}
+
+ bool IsTrusted() const { return is_trusted_; }
+
+ private:
+ static bool GetIsTrusted(ObjPtr<mirror::ClassLoader> class_loader,
+ ObjPtr<mirror::DexCache> dex_cache)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Trust if the caller is in is boot class loader.
+ if (class_loader.IsNull()) {
+ return true;
+ }
+
+ // Trust if caller is in a platform dex file.
+ if (!dex_cache.IsNull()) {
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ if (dex_file != nullptr && dex_file->IsPlatformDexFile()) {
+ return true;
+ }
+ }
- // if policy is "just warn", always warn. We returned above for whitelist APIs.
- if (policy == EnforcementPolicy::kJustWarn) {
- return kAllowButWarn;
+ return false;
}
- DCHECK(policy >= EnforcementPolicy::kDarkGreyAndBlackList);
- // The logic below relies on equality of values in the enums EnforcementPolicy and
- // ApiList, and their ordering. Assertions are in hidden_api.cc.
- if (static_cast<int>(policy) > static_cast<int>(api_list)) {
- return api_list == ApiList::kDarkGreylist
- ? kAllowButWarnAndToast
- : kAllowButWarn;
- } else {
- return kDeny;
+
+ static bool GetIsTrusted(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(!klass.IsNull());
+
+ if (klass->ShouldSkipHiddenApiChecks() && Runtime::Current()->IsJavaDebuggable()) {
+ // Class is known, it is marked trusted and we are in debuggable mode.
+ return true;
+ }
+
+ // Check other aspects of the context.
+ return GetIsTrusted(klass->GetClassLoader(), klass->GetDexCache());
}
-}
+
+ bool is_trusted_;
+};
class ScopedHiddenApiEnforcementPolicySetting {
public:
@@ -114,6 +116,13 @@ class ScopedHiddenApiEnforcementPolicySetting {
// Implementation details. DO NOT ACCESS DIRECTLY.
namespace detail {
+enum class SdkCodes {
+ kVersionNone = std::numeric_limits<int32_t>::min(),
+ kVersionUnlimited = std::numeric_limits<int32_t>::max(),
+ kVersionO_MR1 = 27,
+ kVersionP = 28,
+};
+
// Class to encapsulate the signature of a member (ArtField or ArtMethod). This
// is used as a helper when matching prefixes, and when logging the signature.
class MemberSignature {
@@ -146,59 +155,31 @@ class MemberSignature {
void WarnAboutAccess(AccessMethod access_method, ApiList list);
- void LogAccessToEventLog(AccessMethod access_method, Action action_taken);
+ void LogAccessToEventLog(AccessMethod access_method, bool access_denied);
+
+ // Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that
+ // |member| was accessed. This is usually called when an API is on the black,
+ // dark grey or light grey lists. Given that the callback can execute arbitrary
+ // code, a call to this method can result in thread suspension.
+ void NotifyHiddenApiListener(AccessMethod access_method);
};
template<typename T>
-Action GetMemberActionImpl(T* member,
- ApiList api_list,
- Action action,
- AccessMethod access_method)
+bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_);
-// Returns true if the caller is either loaded by the boot strap class loader or comes from
-// a dex file located in ${ANDROID_ROOT}/framework/.
-ALWAYS_INLINE
-inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller,
- ObjPtr<mirror::ClassLoader> caller_class_loader,
- ObjPtr<mirror::DexCache> caller_dex_cache)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (caller_class_loader.IsNull()) {
- // Boot class loader.
- return true;
- }
-
- if (!caller_dex_cache.IsNull()) {
- const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
- if (caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile()) {
- // Caller is in a platform dex file.
- return true;
- }
- }
-
- if (!caller.IsNull() &&
- caller->ShouldSkipHiddenApiChecks() &&
- Runtime::Current()->IsJavaDebuggable()) {
- // We are in debuggable mode and this caller has been marked trusted.
- return true;
- }
-
- return false;
-}
-
} // namespace detail
-// Returns true if access to `member` should be denied to the caller of the
-// reflective query. The decision is based on whether the caller is trusted or
-// not. Because different users of this function determine this in a different
-// way, `fn_caller_is_trusted(self)` is called and should return true if the
-// caller is allowed to access the platform.
+// Returns true if access to `member` should be denied in the given context.
+// The decision is based on whether the caller is in a trusted context or not.
+// Because determining the access context can be expensive, a lambda function
+// "fn_get_access_context" is lazily invoked after other criteria have been
+// considered.
// This function might print warnings into the log if the member is hidden.
template<typename T>
-inline Action GetMemberAction(T* member,
- Thread* self,
- std::function<bool(Thread*)> fn_caller_is_trusted,
- AccessMethod access_method)
+inline bool ShouldDenyAccessToMember(T* member,
+ std::function<AccessContext()> fn_get_access_context,
+ AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
@@ -210,53 +191,34 @@ inline Action GetMemberAction(T* member,
// results, e.g. print whitelist warnings (b/78327881).
ApiList api_list = member->GetHiddenApiAccessFlags();
- Action action = GetActionFromAccessFlags(member->GetHiddenApiAccessFlags());
- if (action == kAllow) {
- // Nothing to do.
- return action;
+ // Exit early if member is on the whitelist.
+ if (api_list == ApiList::kWhitelist) {
+ return false;
}
- // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access.
+ // Check if caller is exempted from access checks.
// This can be *very* expensive. Save it for last.
- if (fn_caller_is_trusted(self)) {
- // Caller is trusted. Exit.
- return kAllow;
+ if (fn_get_access_context().IsTrusted()) {
+ return false;
}
- // Member is hidden and caller is not in the platform.
- return detail::GetMemberActionImpl(member, api_list, action, access_method);
-}
-
-inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller) REQUIRES_SHARED(Locks::mutator_lock_) {
- return !caller.IsNull() &&
- detail::IsCallerTrusted(caller, caller->GetClassLoader(), caller->GetDexCache());
+ // Member is hidden and caller is not exempted. Enter slow path.
+ return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
}
-// Returns true if access to `member` should be denied to a caller loaded with
-// `caller_class_loader`.
-// This function might print warnings into the log if the member is hidden.
+// Helper method for callers where access context can be determined beforehand.
+// Wraps AccessContext in a lambda and passes it to the real ShouldDenyAccessToMember.
template<typename T>
-inline Action GetMemberAction(T* member,
- ObjPtr<mirror::ClassLoader> caller_class_loader,
- ObjPtr<mirror::DexCache> caller_dex_cache,
- AccessMethod access_method)
+inline bool ShouldDenyAccessToMember(T* member,
+ AccessContext access_context,
+ AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
- bool is_caller_trusted =
- detail::IsCallerTrusted(/* caller= */ nullptr, caller_class_loader, caller_dex_cache);
- return GetMemberAction(member,
- /* thread= */ nullptr,
- [is_caller_trusted] (Thread*) { return is_caller_trusted; },
- access_method);
+ return ShouldDenyAccessToMember(
+ member,
+ [&] () REQUIRES_SHARED(Locks::mutator_lock_) { return access_context; },
+ access_method);
}
-// Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that
-// |member| was accessed. This is usually called when an API is on the black,
-// dark grey or light grey lists. Given that the callback can execute arbitrary
-// code, a call to this method can result in thread suspension.
-template<typename T> void NotifyHiddenApiListener(T* member)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
-
} // namespace hiddenapi
} // namespace art
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index 1727af016c..627d9a7e1c 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -23,7 +23,7 @@
namespace art {
using hiddenapi::detail::MemberSignature;
-using hiddenapi::GetActionFromAccessFlags;
+using hiddenapi::detail::ShouldDenyAccessToMemberImpl;
class HiddenApiTest : public CommonRuntimeTest {
protected:
@@ -68,6 +68,15 @@ class HiddenApiTest : public CommonRuntimeTest {
return art_field;
}
+ bool ShouldDenyAccess(hiddenapi::ApiList list) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Choose parameters such that there are no side effects (AccessMethod::kNone)
+ // and that the member is not on the exemptions list (here we choose one which
+ // is not even in boot class path).
+ return ShouldDenyAccessToMemberImpl(/* member= */ class1_field1_,
+ list,
+ /* access_method= */ hiddenapi::AccessMethod::kNone);
+ }
+
protected:
Thread* self_;
jobject jclass_loader_;
@@ -88,41 +97,33 @@ class HiddenApiTest : public CommonRuntimeTest {
};
TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) {
- runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist), hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist), hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist), hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist), hiddenapi::kAllow);
+ ScopedObjectAccess soa(self_);
+
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDisabled);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), false);
runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist),
- hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist),
- hiddenapi::kAllowButWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist),
- hiddenapi::kAllowButWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist),
- hiddenapi::kAllowButWarn);
-
- runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist),
- hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist),
- hiddenapi::kAllowButWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist),
- hiddenapi::kDeny);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist),
- hiddenapi::kDeny);
-
- runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist),
- hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist),
- hiddenapi::kAllowButWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist),
- hiddenapi::kAllowButWarnAndToast);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist),
- hiddenapi::kDeny);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), false);
+
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ runtime_->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionO_MR1));
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), true);
+
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ runtime_->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionP));
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), true);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), true);
}
TEST_F(HiddenApiTest, CheckMembersRead) {
diff --git a/runtime/interpreter/mterp/arm/other.S b/runtime/interpreter/mterp/arm/other.S
index 340038c83e..fcdde1e72a 100644
--- a/runtime/interpreter/mterp/arm/other.S
+++ b/runtime/interpreter/mterp/arm/other.S
@@ -159,6 +159,9 @@
cmp r0, #0
bne MterpException
FETCH_ADVANCE_INST 1
+ ldr r0, [rSELF, #THREAD_USE_MTERP_OFFSET]
+ cmp r0, #0
+ beq MterpFallback
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
@@ -179,6 +182,9 @@
cmp r0, #0 @ failed?
bne MterpException
FETCH_ADVANCE_INST 1 @ before throw: advance rPC, load rINST
+ ldr r0, [rSELF, #THREAD_USE_MTERP_OFFSET]
+ cmp r0, #0
+ beq MterpFallback
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
diff --git a/runtime/interpreter/mterp/arm64/other.S b/runtime/interpreter/mterp/arm64/other.S
index 024a5c8a07..f1d0ef34dc 100644
--- a/runtime/interpreter/mterp/arm64/other.S
+++ b/runtime/interpreter/mterp/arm64/other.S
@@ -146,6 +146,8 @@
bl artLockObjectFromCode
cbnz w0, MterpException
FETCH_ADVANCE_INST 1
+ ldr w0, [xSELF, #THREAD_USE_MTERP_OFFSET]
+ cbz w0, MterpFallback
GET_INST_OPCODE ip // extract opcode from rINST
GOTO_OPCODE ip // jump to next instruction
@@ -165,6 +167,8 @@
bl artUnlockObjectFromCode // w0<- success for unlock(self, obj)
cbnz w0, MterpException
FETCH_ADVANCE_INST 1 // before throw: advance rPC, load rINST
+ ldr w0, [xSELF, #THREAD_USE_MTERP_OFFSET]
+ cbz w0, MterpFallback
GET_INST_OPCODE ip // extract opcode from rINST
GOTO_OPCODE ip // jump to next instruction
diff --git a/runtime/interpreter/mterp/x86/invoke.S b/runtime/interpreter/mterp/x86/invoke.S
index cfb9c7c719..06cd904756 100644
--- a/runtime/interpreter/mterp/x86/invoke.S
+++ b/runtime/interpreter/mterp/x86/invoke.S
@@ -18,8 +18,7 @@
jz MterpException
ADVANCE_PC 3
movl rSELF, %eax
- movb THREAD_USE_MTERP_OFFSET(%eax), %al
- testb %al, %al
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
jz MterpFallback
RESTORE_IBASE
FETCH_INST
@@ -45,8 +44,7 @@
jz MterpException
ADVANCE_PC 4
movl rSELF, %eax
- movb THREAD_USE_MTERP_OFFSET(%eax), %al
- testb %al, %al
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
jz MterpFallback
RESTORE_IBASE
FETCH_INST
diff --git a/runtime/interpreter/mterp/x86/main.S b/runtime/interpreter/mterp/x86/main.S
index b233f2c522..0621fb468a 100644
--- a/runtime/interpreter/mterp/x86/main.S
+++ b/runtime/interpreter/mterp/x86/main.S
@@ -91,6 +91,8 @@ unspecified registers or condition codes.
#include "asm_support.h"
#include "interpreter/cfi_asm_support.h"
+#define LITERAL(value) $$(value)
+
/*
* Handle mac compiler specific
*/
@@ -561,8 +563,7 @@ MterpException:
movl rPC, OFF_FP_DEX_PC_PTR(rFP)
/* Do we need to switch interpreters? */
movl rSELF, %eax
- movb THREAD_USE_MTERP_OFFSET(%eax), %al
- testb %al, %al
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
jz MterpFallback
/* resume execution at catch block */
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86/other.S b/runtime/interpreter/mterp/x86/other.S
index 5de33813b8..270ccb688c 100644
--- a/runtime/interpreter/mterp/x86/other.S
+++ b/runtime/interpreter/mterp/x86/other.S
@@ -132,7 +132,12 @@
RESTORE_IBASE
testb %al, %al
jnz MterpException
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
+ ADVANCE_PC 1
+ movl rSELF, %eax
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
+ jz MterpFallback
+ FETCH_INST
+ GOTO_NEXT
%def op_monitor_exit():
/*
@@ -152,7 +157,12 @@
RESTORE_IBASE
testb %al, %al
jnz MterpException
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
+ ADVANCE_PC 1
+ movl rSELF, %eax
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
+ jz MterpFallback
+ FETCH_INST
+ GOTO_NEXT
%def op_move(is_object="0"):
/* for move, move-object, long-to-int */
diff --git a/runtime/interpreter/mterp/x86_64/invoke.S b/runtime/interpreter/mterp/x86_64/invoke.S
index f727915265..15b48c91dc 100644
--- a/runtime/interpreter/mterp/x86_64/invoke.S
+++ b/runtime/interpreter/mterp/x86_64/invoke.S
@@ -16,8 +16,7 @@
jz MterpException
ADVANCE_PC 3
movq rSELF, %rax
- movb THREAD_USE_MTERP_OFFSET(%rax), %al
- testb %al, %al
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
jz MterpFallback
FETCH_INST
GOTO_NEXT
@@ -40,8 +39,7 @@
jz MterpException
ADVANCE_PC 4
movq rSELF, %rax
- movb THREAD_USE_MTERP_OFFSET(%rax), %al
- testb %al, %al
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
jz MterpFallback
FETCH_INST
GOTO_NEXT
diff --git a/runtime/interpreter/mterp/x86_64/main.S b/runtime/interpreter/mterp/x86_64/main.S
index 75eb00c461..4609067a5a 100644
--- a/runtime/interpreter/mterp/x86_64/main.S
+++ b/runtime/interpreter/mterp/x86_64/main.S
@@ -87,6 +87,8 @@ unspecified registers or condition codes.
#include "asm_support.h"
#include "interpreter/cfi_asm_support.h"
+#define LITERAL(value) $$(value)
+
/*
* Handle mac compiler specific
*/
@@ -527,8 +529,7 @@ MterpException:
movq rPC, OFF_FP_DEX_PC_PTR(rFP)
/* Do we need to switch interpreters? */
movq rSELF, %rax
- movb THREAD_USE_MTERP_OFFSET(%rax), %al
- testb %al, %al
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
jz MterpFallback
/* resume execution at catch block */
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86_64/other.S b/runtime/interpreter/mterp/x86_64/other.S
index 849155ca8f..412389f3ed 100644
--- a/runtime/interpreter/mterp/x86_64/other.S
+++ b/runtime/interpreter/mterp/x86_64/other.S
@@ -108,7 +108,12 @@
call SYMBOL(artLockObjectFromCode) # (object, self)
testq %rax, %rax
jnz MterpException
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
+ ADVANCE_PC 1
+ movq rSELF, %rax
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
+ jz MterpFallback
+ FETCH_INST
+ GOTO_NEXT
%def op_monitor_exit():
/*
@@ -125,7 +130,12 @@
call SYMBOL(artUnlockObjectFromCode) # (object, self)
testq %rax, %rax
jnz MterpException
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
+ ADVANCE_PC 1
+ movq rSELF, %rax
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
+ jz MterpFallback
+ FETCH_INST
+ GOTO_NEXT
%def op_move(is_object="0"):
/* for move, move-object, long-to-int */
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 9bc2179b63..e292a7612c 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -182,15 +182,16 @@ static mirror::String* GetClassName(Thread* self, ShadowFrame* shadow_frame, siz
}
template<typename T>
-static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* frame)
+static ALWAYS_INLINE bool ShouldDenyAccessToMember(T* member, ShadowFrame* frame)
REQUIRES_SHARED(Locks::mutator_lock_) {
// All uses in this file are from reflection
- constexpr hiddenapi::AccessMethod access_method = hiddenapi::kReflection;
- return hiddenapi::GetMemberAction(
+ constexpr hiddenapi::AccessMethod access_method = hiddenapi::AccessMethod::kReflection;
+ return hiddenapi::ShouldDenyAccessToMember(
member,
- frame->GetMethod()->GetDeclaringClass()->GetClassLoader(),
- frame->GetMethod()->GetDeclaringClass()->GetDexCache(),
- access_method) == hiddenapi::kDeny;
+ [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
+ },
+ access_method);
}
void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self,
@@ -297,7 +298,7 @@ void UnstartedRuntime::UnstartedClassNewInstance(
auto* cl = Runtime::Current()->GetClassLinker();
if (cl->EnsureInitialized(self, h_klass, true, true)) {
ArtMethod* cons = h_klass->FindConstructor("()V", cl->GetImagePointerSize());
- if (cons != nullptr && ShouldBlockAccessToMember(cons, shadow_frame)) {
+ if (cons != nullptr && ShouldDenyAccessToMember(cons, shadow_frame)) {
cons = nullptr;
}
if (cons != nullptr) {
@@ -342,7 +343,7 @@ void UnstartedRuntime::UnstartedClassGetDeclaredField(
}
}
}
- if (found != nullptr && ShouldBlockAccessToMember(found, shadow_frame)) {
+ if (found != nullptr && ShouldDenyAccessToMember(found, shadow_frame)) {
found = nullptr;
}
if (found == nullptr) {
@@ -407,7 +408,7 @@ void UnstartedRuntime::UnstartedClassGetDeclaredMethod(
self, klass, name, args);
}
}
- if (method != nullptr && ShouldBlockAccessToMember(method->GetArtMethod(), shadow_frame)) {
+ if (method != nullptr && ShouldDenyAccessToMember(method->GetArtMethod(), shadow_frame)) {
method = nullptr;
}
result->SetL(method);
@@ -445,7 +446,7 @@ void UnstartedRuntime::UnstartedClassGetDeclaredConstructor(
}
}
if (constructor != nullptr &&
- ShouldBlockAccessToMember(constructor->GetArtMethod(), shadow_frame)) {
+ ShouldDenyAccessToMember(constructor->GetArtMethod(), shadow_frame)) {
constructor = nullptr;
}
result->SetL(constructor);
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 359f97e705..1701ca8a78 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -1274,10 +1274,10 @@ size_t JitCodeCache::ReserveData(Thread* self,
class MarkCodeVisitor final : public StackVisitor {
public:
- MarkCodeVisitor(Thread* thread_in, JitCodeCache* code_cache_in)
+ MarkCodeVisitor(Thread* thread_in, JitCodeCache* code_cache_in, CodeCacheBitmap* bitmap)
: StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kSkipInlinedFrames),
code_cache_(code_cache_in),
- bitmap_(code_cache_->GetLiveBitmap()) {}
+ bitmap_(bitmap) {}
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
@@ -1299,13 +1299,13 @@ class MarkCodeVisitor final : public StackVisitor {
class MarkCodeClosure final : public Closure {
public:
- MarkCodeClosure(JitCodeCache* code_cache, Barrier* barrier)
- : code_cache_(code_cache), barrier_(barrier) {}
+ MarkCodeClosure(JitCodeCache* code_cache, CodeCacheBitmap* bitmap, Barrier* barrier)
+ : code_cache_(code_cache), bitmap_(bitmap), barrier_(barrier) {}
void Run(Thread* thread) override REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedTrace trace(__PRETTY_FUNCTION__);
DCHECK(thread == Thread::Current() || thread->IsSuspended());
- MarkCodeVisitor visitor(thread, code_cache_);
+ MarkCodeVisitor visitor(thread, code_cache_, bitmap_);
visitor.WalkStack();
if (kIsDebugBuild) {
// The stack walking code queries the side instrumentation stack if it
@@ -1320,7 +1320,7 @@ class MarkCodeClosure final : public Closure {
code_cache_->LookupMethodHeader(frame.return_pc_, /* method= */ nullptr);
if (method_header != nullptr) {
const void* code = method_header->GetCode();
- CHECK(code_cache_->GetLiveBitmap()->Test(FromCodeToAllocation(code)));
+ CHECK(bitmap_->Test(FromCodeToAllocation(code)));
}
}
}
@@ -1329,6 +1329,7 @@ class MarkCodeClosure final : public Closure {
private:
JitCodeCache* const code_cache_;
+ CodeCacheBitmap* const bitmap_;
Barrier* const barrier_;
};
@@ -1374,7 +1375,7 @@ bool JitCodeCache::IncreaseCodeCacheCapacity() {
void JitCodeCache::MarkCompiledCodeOnThreadStacks(Thread* self) {
Barrier barrier(0);
size_t threads_running_checkpoint = 0;
- MarkCodeClosure closure(this, &barrier);
+ MarkCodeClosure closure(this, GetLiveBitmap(), &barrier);
threads_running_checkpoint = Runtime::Current()->GetThreadList()->RunCheckpoint(&closure);
// Now that we have run our checkpoint, move to a suspended state and wait
// for other threads to run the checkpoint.
@@ -1987,11 +1988,6 @@ void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self, bool osr) {
}
}
-size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) {
- MutexLock mu(Thread::Current(), lock_);
- return mspace_usable_size(reinterpret_cast<const void*>(FromCodeToAllocation(ptr)));
-}
-
void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method,
const OatQuickMethodHeader* header) {
DCHECK(!method->IsNative());
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 126fd441db..a5075638f2 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -71,7 +71,6 @@ template<class T> class ObjectArray;
namespace jit {
-class JitInstrumentationCache;
class ScopedCodeCacheWrite;
// Alignment in bits that will suit all architectures.
@@ -97,12 +96,6 @@ class JitCodeCache {
std::string* error_msg);
~JitCodeCache();
- // Number of bytes allocated in the code cache.
- size_t CodeCacheSize() REQUIRES(!lock_);
-
- // Number of bytes allocated in the data cache.
- size_t DataCacheSize() REQUIRES(!lock_);
-
bool NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!lock_);
@@ -177,10 +170,6 @@ class JitCodeCache {
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!lock_);
- CodeCacheBitmap* GetLiveBitmap() const {
- return live_bitmap_.get();
- }
-
// Perform a collection on the code cache.
void GarbageCollectCache(Thread* self)
REQUIRES(!lock_)
@@ -234,10 +223,6 @@ class JitCodeCache {
REQUIRES(!lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- uint64_t GetLastUpdateTimeNs() const;
-
- size_t GetMemorySizeOfCodePointer(const void* ptr) REQUIRES(!lock_);
-
void InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* code)
REQUIRES(!lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -339,6 +324,12 @@ class JitCodeCache {
void FreeCodeAndData(const void* code_ptr) REQUIRES(lock_);
// Number of bytes allocated in the code cache.
+ size_t CodeCacheSize() REQUIRES(!lock_);
+
+ // Number of bytes allocated in the data cache.
+ size_t DataCacheSize() REQUIRES(!lock_);
+
+ // Number of bytes allocated in the code cache.
size_t CodeCacheSizeLocked() REQUIRES(lock_);
// Number of bytes allocated in the data cache.
@@ -375,6 +366,10 @@ class JitCodeCache {
REQUIRES(lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ CodeCacheBitmap* GetLiveBitmap() const {
+ return live_bitmap_.get();
+ }
+
uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_);
void FreeCode(uint8_t* code) REQUIRES(lock_);
uint8_t* AllocateData(size_t data_size) REQUIRES(lock_);
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index 5e01b7941f..af86cc0303 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -84,20 +84,20 @@ namespace art {
// things not rendering correctly. E.g. b/16858794
static constexpr bool kWarnJniAbort = false;
-static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::IsCallerTrusted(GetCallingClass(self, /* num_frames= */ 1));
-}
-
template<typename T>
-ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
+ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- hiddenapi::Action action = hiddenapi::GetMemberAction(
- member, self, IsCallerTrusted, hiddenapi::kJNI);
- if (action != hiddenapi::kAllow) {
- hiddenapi::NotifyHiddenApiListener(member);
- }
-
- return action == hiddenapi::kDeny;
+ return hiddenapi::ShouldDenyAccessToMember(
+ member,
+ [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Construct AccessContext from the first calling class on stack.
+ // If the calling class cannot be determined, e.g. unattached threads,
+ // we conservatively assume the caller is trusted.
+ ObjPtr<mirror::Class> caller = GetCallingClass(self, /* num_frames */ 1);
+ return caller.IsNull() ? hiddenapi::AccessContext(/* is_trusted= */ true)
+ : hiddenapi::AccessContext(caller);
+ },
+ hiddenapi::AccessMethod::kJNI);
}
// Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set
@@ -259,7 +259,7 @@ static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class,
} else {
method = c->FindClassMethod(name, sig, pointer_size);
}
- if (method != nullptr && ShouldBlockAccessToMember(method, soa.Self())) {
+ if (method != nullptr && ShouldDenyAccessToMember(method, soa.Self())) {
method = nullptr;
}
if (method == nullptr || method->IsStatic() != is_static) {
@@ -338,7 +338,7 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con
} else {
field = c->FindInstanceField(name, field_type->GetDescriptor(&temp));
}
- if (field != nullptr && ShouldBlockAccessToMember(field, soa.Self())) {
+ if (field != nullptr && ShouldDenyAccessToMember(field, soa.Self())) {
field = nullptr;
}
if (field == nullptr) {
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 6b9ba8c8a2..185ae3b2ac 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -1102,6 +1102,16 @@ inline bool Class::CannotBeAssignedFromOtherTypes() {
return component->IsPrimitive() || component->CannotBeAssignedFromOtherTypes();
}
+template <bool kCheckTransaction>
+inline void Class::SetClassLoader(ObjPtr<ClassLoader> new_class_loader) {
+ if (kCheckTransaction && Runtime::Current()->IsActiveTransaction()) {
+ SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
+ } else {
+ DCHECK(!Runtime::Current()->IsActiveTransaction());
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
+ }
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index e33e407149..83d76a98cd 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -427,14 +427,6 @@ bool Class::IsThrowableClass() {
return GetClassRoot<mirror::Throwable>()->IsAssignableFrom(this);
}
-void Class::SetClassLoader(ObjPtr<ClassLoader> new_class_loader) {
- if (Runtime::Current()->IsActiveTransaction()) {
- SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
- } else {
- SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
- }
-}
-
template <typename SignatureType>
static inline ArtMethod* FindInterfaceMethodWithSignature(ObjPtr<Class> klass,
const StringPiece& name,
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index bb54b3d33f..c38cc86c59 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -628,6 +628,7 @@ class MANAGED Class final : public Object {
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ClassLoader* GetClassLoader() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
+ template <bool kCheckTransaction = true>
void SetClassLoader(ObjPtr<ClassLoader> new_cl) REQUIRES_SHARED(Locks::mutator_lock_);
static constexpr MemberOffset DexCacheOffset() {
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 4d3ad620cc..56e9094983 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -76,12 +76,16 @@ static void EnableDebugger() {
}
}
#endif
- // We don't want core dumps, though, so set the core dump size to 0.
+ // We don't want core dumps, though, so set the soft limit on core dump size
+ // to 0 without changing the hard limit.
rlimit rl;
- rl.rlim_cur = 0;
- rl.rlim_max = RLIM_INFINITY;
- if (setrlimit(RLIMIT_CORE, &rl) == -1) {
- PLOG(ERROR) << "setrlimit(RLIMIT_CORE) failed for pid " << getpid();
+ if (getrlimit(RLIMIT_CORE, &rl) == -1) {
+ PLOG(ERROR) << "getrlimit(RLIMIT_CORE) failed for pid " << getpid();
+ } else {
+ rl.rlim_cur = 0;
+ if (setrlimit(RLIMIT_CORE, &rl) == -1) {
+ PLOG(ERROR) << "setrlimit(RLIMIT_CORE) failed for pid " << getpid();
+ }
}
}
@@ -304,8 +308,7 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
// Our system thread ID, etc, has changed so reset Thread state.
thread->InitAfterFork();
runtime_flags = EnableDebugFeatures(runtime_flags);
- hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kNoChecks;
- bool dedupe_hidden_api_warnings = true;
+ hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kDisabled;
if ((runtime_flags & DISABLE_VERIFIER) != 0) {
Runtime::Current()->DisableVerifier();
@@ -372,14 +375,14 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
}
}
- bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks;
+ bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kDisabled;
DCHECK(!(is_system_server && do_hidden_api_checks))
<< "SystemServer should be forked with EnforcementPolicy::kDisable";
DCHECK(!(is_zygote && do_hidden_api_checks))
<< "Child zygote processes should be forked with EnforcementPolicy::kDisable";
Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy);
- Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings);
- if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks &&
+ Runtime::Current()->SetDedupeHiddenApiWarnings(true);
+ if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kDisabled &&
Runtime::Current()->GetHiddenApiEventLogSampleRate() != 0) {
// Hidden API checks are enabled, and we are sampling access for the event log. Initialize the
// random seed, to ensure the sampling is actually random. We do this post-fork, as doing it
@@ -387,9 +390,6 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
std::srand(static_cast<uint32_t>(NanoTime()));
}
- // Clear the hidden API warning flag, in case it was set.
- Runtime::Current()->SetPendingHiddenApiWarning(false);
-
if (is_zygote) {
// If creating a child-zygote, do not call into the runtime's post-fork logic.
// Doing so would spin up threads for Binder and JDWP. Instead, the Java side
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index c7b8ad4392..612a4b33b1 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -54,11 +54,12 @@
namespace art {
-// Returns true if the first caller outside of the Class class or java.lang.invoke package
-// is in a platform DEX file.
-static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
- // Walk the stack and find the first frame not from java.lang.Class and not from java.lang.invoke.
- // This is very expensive. Save this till the last.
+// Walks the stack, finds the caller of this reflective call and returns
+// a hiddenapi AccessContext formed from its declaring class.
+static hiddenapi::AccessContext GetReflectionCaller(Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Walk the stack and find the first frame not from java.lang.Class and not
+ // from java.lang.invoke. This is very expensive. Save this till the last.
struct FirstExternalCallerVisitor : public StackVisitor {
explicit FirstExternalCallerVisitor(Thread* thread)
: StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
@@ -102,50 +103,42 @@ static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_)
FirstExternalCallerVisitor visitor(self);
visitor.WalkStack();
- return visitor.caller != nullptr &&
- hiddenapi::IsCallerTrusted(visitor.caller->GetDeclaringClass());
-}
-// Returns true if the first non-ClassClass caller up the stack is not allowed to
-// access hidden APIs. This can be *very* expensive. Never call this in a loop.
-ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
- return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerTrusted(self);
+ // Construct AccessContext from the calling class found on the stack.
+ // If the calling class cannot be determined, e.g. unattached threads,
+ // we conservatively assume the caller is trusted.
+ ObjPtr<mirror::Class> caller = (visitor.caller == nullptr)
+ ? nullptr : visitor.caller->GetDeclaringClass();
+ return caller.IsNull() ? hiddenapi::AccessContext(/* is_trusted= */ true)
+ : hiddenapi::AccessContext(caller);
}
// Returns true if the first non-ClassClass caller up the stack should not be
// allowed access to `member`.
template<typename T>
-ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
+ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- hiddenapi::Action action = hiddenapi::GetMemberAction(
- member, self, IsCallerTrusted, hiddenapi::kReflection);
- if (action != hiddenapi::kAllow) {
- hiddenapi::NotifyHiddenApiListener(member);
- }
-
- return action == hiddenapi::kDeny;
+ return hiddenapi::ShouldDenyAccessToMember(
+ member,
+ [&]() REQUIRES_SHARED(Locks::mutator_lock_) { return GetReflectionCaller(self); },
+ hiddenapi::AccessMethod::kReflection);
}
// Returns true if a class member should be discoverable with reflection given
// the criteria. Some reflection calls only return public members
// (public_only == true), some members should be hidden from non-boot class path
-// callers (enforce_hidden_api == true).
+// callers (hiddenapi_context).
template<typename T>
ALWAYS_INLINE static bool IsDiscoverable(bool public_only,
- bool enforce_hidden_api,
+ hiddenapi::AccessContext access_context,
T* member)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (public_only && ((member->GetAccessFlags() & kAccPublic) == 0)) {
return false;
}
- return hiddenapi::GetMemberAction(member,
- nullptr,
- [enforce_hidden_api] (Thread*) { return !enforce_hidden_api; },
- hiddenapi::kNone)
- != hiddenapi::kDeny;
+ return !hiddenapi::ShouldDenyAccessToMember(
+ member, access_context, hiddenapi::AccessMethod::kNone);
}
ALWAYS_INLINE static inline ObjPtr<mirror::Class> DecodeClass(
@@ -266,15 +259,15 @@ static ObjPtr<mirror::ObjectArray<mirror::Field>> GetDeclaredFields(
IterationRange<StrideIterator<ArtField>> ifields = klass->GetIFields();
IterationRange<StrideIterator<ArtField>> sfields = klass->GetSFields();
size_t array_size = klass->NumInstanceFields() + klass->NumStaticFields();
- bool enforce_hidden_api = ShouldEnforceHiddenApi(self);
+ hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(self);
// Lets go subtract all the non discoverable fields.
for (ArtField& field : ifields) {
- if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+ if (!IsDiscoverable(public_only, hiddenapi_context, &field)) {
--array_size;
}
}
for (ArtField& field : sfields) {
- if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+ if (!IsDiscoverable(public_only, hiddenapi_context, &field)) {
--array_size;
}
}
@@ -285,7 +278,7 @@ static ObjPtr<mirror::ObjectArray<mirror::Field>> GetDeclaredFields(
return nullptr;
}
for (ArtField& field : ifields) {
- if (IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+ if (IsDiscoverable(public_only, hiddenapi_context, &field)) {
auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
&field,
force_resolve);
@@ -300,7 +293,7 @@ static ObjPtr<mirror::ObjectArray<mirror::Field>> GetDeclaredFields(
}
}
for (ArtField& field : sfields) {
- if (IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+ if (IsDiscoverable(public_only, hiddenapi_context, &field)) {
auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
&field,
force_resolve);
@@ -459,8 +452,7 @@ static jobject Class_getPublicFieldRecursive(JNIEnv* env, jobject javaThis, jstr
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Field> field = hs.NewHandle(GetPublicFieldRecursive(
soa.Self(), DecodeClass(soa, javaThis), name_string));
- if (field.Get() == nullptr ||
- ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) {
+ if (field.Get() == nullptr || ShouldDenyAccessToMember(field->GetArtField(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference<jobject>(field.Get());
@@ -477,7 +469,7 @@ static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring nam
Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis));
Handle<mirror::Field> result =
hs.NewHandle(GetDeclaredField(soa.Self(), h_klass.Get(), h_string.Get()));
- if (result == nullptr || ShouldBlockAccessToMember(result->GetArtField(), soa.Self())) {
+ if (result == nullptr || ShouldDenyAccessToMember(result->GetArtField(), soa.Self())) {
std::string name_str = h_string->ToModifiedUtf8();
if (name_str == "value" && h_klass->IsStringClass()) {
// We log the error for this specific case, as the user might just swallow the exception.
@@ -509,19 +501,19 @@ static jobject Class_getDeclaredConstructorInternal(
soa.Self(),
DecodeClass(soa, javaThis),
soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
- if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
+ if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference<jobject>(result.Get());
}
static ALWAYS_INLINE inline bool MethodMatchesConstructor(
- ArtMethod* m, bool public_only, bool enforce_hidden_api)
+ ArtMethod* m, bool public_only, hiddenapi::AccessContext hiddenapi_context)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(m != nullptr);
return m->IsConstructor() &&
!m->IsStatic() &&
- IsDiscoverable(public_only, enforce_hidden_api, m);
+ IsDiscoverable(public_only, hiddenapi_context, m);
}
static jobjectArray Class_getDeclaredConstructorsInternal(
@@ -529,12 +521,12 @@ static jobjectArray Class_getDeclaredConstructorsInternal(
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<2> hs(soa.Self());
bool public_only = (publicOnly != JNI_FALSE);
- bool enforce_hidden_api = ShouldEnforceHiddenApi(soa.Self());
+ hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(soa.Self());
Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis));
size_t constructor_count = 0;
// Two pass approach for speed.
for (auto& m : h_klass->GetDirectMethods(kRuntimePointerSize)) {
- constructor_count += MethodMatchesConstructor(&m, public_only, enforce_hidden_api) ? 1u : 0u;
+ constructor_count += MethodMatchesConstructor(&m, public_only, hiddenapi_context) ? 1u : 0u;
}
auto h_constructors = hs.NewHandle(mirror::ObjectArray<mirror::Constructor>::Alloc(
soa.Self(), GetClassRoot<mirror::ObjectArray<mirror::Constructor>>(), constructor_count));
@@ -544,7 +536,7 @@ static jobjectArray Class_getDeclaredConstructorsInternal(
}
constructor_count = 0;
for (auto& m : h_klass->GetDirectMethods(kRuntimePointerSize)) {
- if (MethodMatchesConstructor(&m, public_only, enforce_hidden_api)) {
+ if (MethodMatchesConstructor(&m, public_only, hiddenapi_context)) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
ObjPtr<mirror::Constructor> constructor =
@@ -571,7 +563,7 @@ static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis,
DecodeClass(soa, javaThis),
soa.Decode<mirror::String>(name),
soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
- if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
+ if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference<jobject>(result.Get());
@@ -582,7 +574,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<2> hs(soa.Self());
- bool enforce_hidden_api = ShouldEnforceHiddenApi(soa.Self());
+ hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(soa.Self());
bool public_only = (publicOnly != JNI_FALSE);
Handle<mirror::Class> klass = hs.NewHandle(DecodeClass(soa, javaThis));
@@ -591,7 +583,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT
uint32_t modifiers = m.GetAccessFlags();
// Add non-constructor declared methods.
if ((modifiers & kAccConstructor) == 0 &&
- IsDiscoverable(public_only, enforce_hidden_api, &m)) {
+ IsDiscoverable(public_only, hiddenapi_context, &m)) {
++num_methods;
}
}
@@ -605,7 +597,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT
for (ArtMethod& m : klass->GetDeclaredMethods(kRuntimePointerSize)) {
uint32_t modifiers = m.GetAccessFlags();
if ((modifiers & kAccConstructor) == 0 &&
- IsDiscoverable(public_only, enforce_hidden_api, &m)) {
+ IsDiscoverable(public_only, hiddenapi_context, &m)) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
ObjPtr<mirror::Method> method =
@@ -819,7 +811,7 @@ static jobject Class_newInstance(JNIEnv* env, jobject javaThis) {
soa.Self(),
ScopedNullHandle<mirror::ObjectArray<mirror::Class>>(),
kRuntimePointerSize);
- if (UNLIKELY(constructor == nullptr) || ShouldBlockAccessToMember(constructor, soa.Self())) {
+ if (UNLIKELY(constructor == nullptr) || ShouldDenyAccessToMember(constructor, soa.Self())) {
soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
"%s has no zero argument constructor",
klass->PrettyClass().c_str());
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 7367706deb..4720599a91 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -269,10 +269,8 @@ Runtime::Runtime()
oat_file_manager_(nullptr),
is_low_memory_mode_(false),
safe_mode_(false),
- hidden_api_policy_(hiddenapi::EnforcementPolicy::kNoChecks),
- pending_hidden_api_warning_(false),
+ hidden_api_policy_(hiddenapi::EnforcementPolicy::kDisabled),
dedupe_hidden_api_warnings_(true),
- always_set_hidden_api_warning_flag_(false),
hidden_api_access_event_log_rate_(0),
dump_native_stack_on_sig_quit_(true),
pruned_dalvik_cache_(false),
@@ -1245,8 +1243,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
// As is, we're encoding some logic here about which specific policy to use, which would be better
// controlled by the framework.
hidden_api_policy_ = do_hidden_api_checks
- ? hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList
- : hiddenapi::EnforcementPolicy::kNoChecks;
+ ? hiddenapi::EnforcementPolicy::kEnabled
+ : hiddenapi::EnforcementPolicy::kDisabled;
no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain);
force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index e2176c2117..f5aa300649 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -535,10 +535,6 @@ class Runtime {
return hidden_api_policy_;
}
- void SetPendingHiddenApiWarning(bool value) {
- pending_hidden_api_warning_ = value;
- }
-
void SetHiddenApiExemptions(const std::vector<std::string>& exemptions) {
hidden_api_exemptions_ = exemptions;
}
@@ -547,10 +543,6 @@ class Runtime {
return hidden_api_exemptions_;
}
- bool HasPendingHiddenApiWarning() const {
- return pending_hidden_api_warning_;
- }
-
void SetDedupeHiddenApiWarnings(bool value) {
dedupe_hidden_api_warnings_ = value;
}
@@ -559,14 +551,6 @@ class Runtime {
return dedupe_hidden_api_warnings_;
}
- void AlwaysSetHiddenApiWarningFlag() {
- always_set_hidden_api_warning_flag_ = true;
- }
-
- bool ShouldAlwaysSetHiddenApiWarningFlag() const {
- return always_set_hidden_api_warning_flag_;
- }
-
void SetHiddenApiEventLogSampleRate(uint32_t rate) {
hidden_api_access_event_log_rate_ = rate;
}
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 65039bc6d4..5300e2d389 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -119,6 +119,7 @@ jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
jfieldID WellKnownClasses::dalvik_system_DexFile_cookie;
jfieldID WellKnownClasses::dalvik_system_DexFile_fileName;
jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
+jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
@@ -293,7 +294,7 @@ uint32_t WellKnownClasses::StringInitToEntryPoint(ArtMethod* string_init) {
void WellKnownClasses::Init(JNIEnv* env) {
hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
- hiddenapi::EnforcementPolicy::kNoChecks);
+ hiddenapi::EnforcementPolicy::kDisabled);
dalvik_annotation_optimization_CriticalNative =
CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
@@ -367,6 +368,7 @@ void WellKnownClasses::Init(JNIEnv* env) {
org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
dalvik_system_BaseDexClassLoader_pathList = CacheField(env, dalvik_system_BaseDexClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
+ dalvik_system_BaseDexClassLoader_sharedLibraryLoaders = CacheField(env, dalvik_system_BaseDexClassLoader, false, "sharedLibraryLoaders", "[Ljava/lang/ClassLoader;");
dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "Ljava/lang/Object;");
dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;");
dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 130747c026..0b7ed0994c 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -126,6 +126,7 @@ struct WellKnownClasses {
static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
static jfieldID dalvik_system_BaseDexClassLoader_pathList;
+ static jfieldID dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
static jfieldID dalvik_system_DexFile_cookie;
static jfieldID dalvik_system_DexFile_fileName;
static jfieldID dalvik_system_DexPathList_dexElements;
diff --git a/simulator/Android.bp b/simulator/Android.bp
index 8690426466..223c891da0 100644
--- a/simulator/Android.bp
+++ b/simulator/Android.bp
@@ -45,7 +45,7 @@ art_cc_library {
shared_libs: [
"libart",
"libartbase",
- "libvixl-arm64",
+ "libvixl",
],
}
@@ -58,7 +58,7 @@ art_cc_library {
shared_libs: [
"libartd",
"libartbased",
- "libvixld-arm64",
+ "libvixld",
],
}
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index 96754c3076..8e3e4eb5ce 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -28,9 +28,9 @@ namespace Test674HiddenApi {
extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) {
Runtime* runtime = Runtime::Current();
- runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly);
+ runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ runtime->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionO_MR1));
runtime->SetDedupeHiddenApiWarnings(false);
- runtime->AlwaysSetHiddenApiWarningFlag();
}
extern "C" JNIEXPORT void JNICALL Java_Main_appendToBootClassLoader(
@@ -287,13 +287,5 @@ extern "C" JNIEXPORT jint JNICALL Java_Reflection_getHiddenApiAccessFlags(JNIEnv
return static_cast<jint>(kAccHiddenApiBits);
}
-extern "C" JNIEXPORT jboolean JNICALL Java_ChildClass_hasPendingWarning(JNIEnv*, jclass) {
- return Runtime::Current()->HasPendingHiddenApiWarning();
-}
-
-extern "C" JNIEXPORT void JNICALL Java_ChildClass_clearWarning(JNIEnv*, jclass) {
- Runtime::Current()->SetPendingHiddenApiWarning(false);
-}
-
} // namespace Test674HiddenApi
} // namespace art
diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java
index db3ba6d8bf..3427b8eba6 100644
--- a/test/674-hiddenapi/src-ex/ChildClass.java
+++ b/test/674-hiddenapi/src-ex/ChildClass.java
@@ -98,10 +98,8 @@ public class ChildClass {
expected = Behaviour.Granted;
} else if (hiddenness == Hiddenness.Blacklist) {
expected = Behaviour.Denied;
- } else if (isDebuggable) {
- expected = Behaviour.Warning;
} else {
- expected = Behaviour.Granted;
+ expected = Behaviour.Warning;
}
for (boolean isStatic : booleanValues) {
@@ -145,7 +143,7 @@ public class ChildClass {
}
private static void checkMemberCallback(Class<?> klass, String name,
- boolean isPublic, boolean isField) {
+ boolean isPublic, boolean isField, boolean expectedCallback) {
try {
RecordingConsumer consumer = new RecordingConsumer();
VMRuntime.setNonSdkApiUsageConsumer(consumer);
@@ -168,8 +166,14 @@ public class ChildClass {
// only interested in whether the callback is invoked.
}
- if (consumer.recordedValue == null || !consumer.recordedValue.contains(name)) {
- throw new RuntimeException("No callback for member: " + name);
+ boolean actualCallback = consumer.recordedValue != null &&
+ consumer.recordedValue.contains(name);
+ if (expectedCallback != actualCallback) {
+ if (expectedCallback) {
+ throw new RuntimeException("Expected callback for member: " + name);
+ } else {
+ throw new RuntimeException("Did not expect callback for member: " + name);
+ }
}
} finally {
VMRuntime.setNonSdkApiUsageConsumer(null);
@@ -181,7 +185,7 @@ public class ChildClass {
boolean isPublic = (visibility == Visibility.Public);
boolean canDiscover = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
+ boolean invokesMemberCallback = (behaviour != Behaviour.Granted);
if (klass.isInterface() && (!isStatic || !isPublic)) {
// Interfaces only have public static fields.
@@ -243,8 +247,6 @@ public class ChildClass {
canDiscover);
}
- // Finish here if we could not discover the field.
-
if (canDiscover) {
// Test that modifiers are unaffected.
@@ -254,44 +256,22 @@ public class ChildClass {
// Test getters and setters when meaningful.
- clearWarning();
if (!Reflection.canGetField(klass, name)) {
throwAccessException(klass, name, true, "Field.getInt()");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "Field.getInt()", setsWarning);
- }
-
- clearWarning();
if (!Reflection.canSetField(klass, name)) {
throwAccessException(klass, name, true, "Field.setInt()");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "Field.setInt()", setsWarning);
- }
-
- clearWarning();
if (!JNI.canGetField(klass, name, isStatic)) {
throwAccessException(klass, name, true, "getIntField");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "getIntField", setsWarning);
- }
-
- clearWarning();
if (!JNI.canSetField(klass, name, isStatic)) {
throwAccessException(klass, name, true, "setIntField");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "setIntField", setsWarning);
- }
}
// Test that callbacks are invoked correctly.
- clearWarning();
- if (setsWarning || !canDiscover) {
- checkMemberCallback(klass, name, isPublic, true /* isField */);
- }
+ checkMemberCallback(klass, name, isPublic, true /* isField */, invokesMemberCallback);
}
private static void checkMethod(Class<?> klass, String name, boolean isStatic,
@@ -304,7 +284,7 @@ public class ChildClass {
}
boolean canDiscover = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
+ boolean invokesMemberCallback = (behaviour != Behaviour.Granted);
// Test discovery with reflection.
@@ -354,39 +334,21 @@ public class ChildClass {
}
// Test whether we can invoke the method. This skips non-static interface methods.
-
if (!klass.isInterface() || isStatic) {
- clearWarning();
if (!Reflection.canInvokeMethod(klass, name)) {
throwAccessException(klass, name, false, "invoke()");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, false, "invoke()", setsWarning);
- }
-
- clearWarning();
if (!JNI.canInvokeMethodA(klass, name, isStatic)) {
throwAccessException(klass, name, false, "CallMethodA");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, false, "CallMethodA()", setsWarning);
- }
-
- clearWarning();
if (!JNI.canInvokeMethodV(klass, name, isStatic)) {
throwAccessException(klass, name, false, "CallMethodV");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, false, "CallMethodV()", setsWarning);
- }
}
}
// Test that callbacks are invoked correctly.
- clearWarning();
- if (setsWarning || !canDiscover) {
- checkMemberCallback(klass, name, isPublic, false /* isField */);
- }
+ checkMemberCallback(klass, name, isPublic, false /* isField */, invokesMemberCallback);
}
private static void checkConstructor(Class<?> klass, Visibility visibility, Hiddenness hiddenness,
@@ -403,7 +365,6 @@ public class ChildClass {
MethodType methodType = MethodType.methodType(void.class, args);
boolean canDiscover = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
// Test discovery with reflection.
@@ -446,70 +407,41 @@ public class ChildClass {
canDiscover);
}
- // Finish here if we could not discover the constructor.
-
- if (!canDiscover) {
- return;
- }
-
- // Test whether we can invoke the constructor.
-
- clearWarning();
- if (!Reflection.canInvokeConstructor(klass, args, initargs)) {
- throwAccessException(klass, fullName, false, "invoke()");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, fullName, false, "invoke()", setsWarning);
- }
-
- clearWarning();
- if (!JNI.canInvokeConstructorA(klass, signature)) {
- throwAccessException(klass, fullName, false, "NewObjectA");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, fullName, false, "NewObjectA", setsWarning);
- }
+ if (canDiscover) {
+ // Test whether we can invoke the constructor.
- clearWarning();
- if (!JNI.canInvokeConstructorV(klass, signature)) {
- throwAccessException(klass, fullName, false, "NewObjectV");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, fullName, false, "NewObjectV", setsWarning);
+ if (!Reflection.canInvokeConstructor(klass, args, initargs)) {
+ throwAccessException(klass, fullName, false, "invoke()");
+ }
+ if (!JNI.canInvokeConstructorA(klass, signature)) {
+ throwAccessException(klass, fullName, false, "NewObjectA");
+ }
+ if (!JNI.canInvokeConstructorV(klass, signature)) {
+ throwAccessException(klass, fullName, false, "NewObjectV");
+ }
}
}
private static void checkNullaryConstructor(Class<?> klass, Behaviour behaviour)
throws Exception {
boolean canAccess = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
- clearWarning();
if (Reflection.canUseNewInstance(klass) != canAccess) {
throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
"be able to construct " + klass.getName() + ". " +
"isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
}
- if (canAccess && hasPendingWarning() != setsWarning) {
- throwWarningException(klass, "nullary constructor", false, "newInstance", setsWarning);
- }
}
private static void checkLinking(String className, boolean takesParameter, Behaviour behaviour)
throws Exception {
boolean canAccess = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
- clearWarning();
if (Linking.canAccess(className, takesParameter) != canAccess) {
throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
"be able to verify " + className + "." +
"isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
}
- if (canAccess && hasPendingWarning() != setsWarning) {
- throwWarningException(
- Class.forName(className), "access", false, "static linking", setsWarning);
- }
}
private static void throwDiscoveryException(Class<?> klass, String name, boolean isField,
@@ -528,15 +460,6 @@ public class ChildClass {
"everythingWhitelisted = " + everythingWhitelisted);
}
- private static void throwWarningException(Class<?> klass, String name, boolean isField,
- String fn, boolean setsWarning) {
- throw new RuntimeException("Expected access to " + (isField ? "field " : "method ") +
- klass.getName() + "." + name + " using " + fn + " to " + (setsWarning ? "" : "not ") +
- "set the warning flag. " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
- "everythingWhitelisted = " + everythingWhitelisted);
- }
-
private static void throwModifiersException(Class<?> klass, String name, boolean isField) {
throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
"." + name + " to not expose hidden modifiers");
@@ -545,7 +468,4 @@ public class ChildClass {
private static boolean isParentInBoot;
private static boolean isChildInBoot;
private static boolean everythingWhitelisted;
-
- private static native boolean hasPendingWarning();
- private static native void clearWarning();
}
diff --git a/test/688-shared-library/check b/test/688-shared-library/check
new file mode 100644
index 0000000000..0b6c9e4d2f
--- /dev/null
+++ b/test/688-shared-library/check
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Finalizers of DexFile will complain not being able to close
+# the main dex file, as it's still open. That's OK to ignore.
+sed -e '/^E\/System/d' "$2" > "$2.tmp"
+
+diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
diff --git a/test/688-shared-library/expected.txt b/test/688-shared-library/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/688-shared-library/expected.txt
diff --git a/test/688-shared-library/info.txt b/test/688-shared-library/info.txt
new file mode 100644
index 0000000000..2eda65d454
--- /dev/null
+++ b/test/688-shared-library/info.txt
@@ -0,0 +1,2 @@
+Tests on BaseDexClassLoader shared libraries and their class
+loading behavior.
diff --git a/test/688-shared-library/src-art/Main.java b/test/688-shared-library/src-art/Main.java
new file mode 100644
index 0000000000..d59e7dc10f
--- /dev/null
+++ b/test/688-shared-library/src-art/Main.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import dalvik.system.DelegateLastClassLoader;
+import dalvik.system.PathClassLoader;
+
+public class Main {
+ static final String TEST_NAME = "688-shared-library";
+ static final String MAIN_JAR_FILE = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar";
+ static final String EX_JAR_FILE = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + "-ex.jar";
+ static ClassLoader bootLoader = Object.class.getClassLoader();
+
+ public static void main(String[] args) throws Exception {
+ testNoLibrary();
+ testOneLibrary();
+ testTwoLibraries1();
+ testTwoLibraries2();
+ testTransitive1();
+ testTransitive2();
+ testTransitive3();
+ testTransitive4();
+ }
+
+ public static void assertIdentical(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void testNoLibrary() throws Exception {
+ ClassLoader loader = new PathClassLoader(MAIN_JAR_FILE, null, bootLoader);
+ Class<?> cls = loader.loadClass("Main");
+ assertIdentical(loader, cls.getClassLoader());
+ }
+
+ public static void testOneLibrary() throws Exception {
+ ClassLoader[] sharedLibraries = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader delegateFirst =
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ }
+
+ public static void testTwoLibraries1() throws Exception {
+ ClassLoader[] sharedLibraries = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraries);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[1], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[1], cls.getClassLoader());
+ }
+
+ public static void testTwoLibraries2() throws Exception {
+ ClassLoader[] sharedLibraries = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraries);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+
+ ClassLoader delegateLast = new DelegateLastClassLoader("", null, bootLoader, sharedLibraries);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ }
+
+ public static void testTransitive1() throws Exception {
+ ClassLoader[] sharedLibraryLevel2 = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader[] sharedLibraryLevel1 = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+ };
+
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ }
+
+ public static void testTransitive2() throws Exception {
+ ClassLoader[] sharedLibraryLevel2 = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader[] sharedLibraryLevel1 = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+ };
+
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+ }
+
+ public static void testTransitive3() throws Exception {
+ ClassLoader[] sharedLibraryLevel2 = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader[] sharedLibraryLevel1 = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+ sharedLibraryLevel2[0],
+ };
+
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+ }
+
+ public static void testTransitive4() throws Exception {
+ ClassLoader[] sharedLibraryLevel2 = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader[] sharedLibraryLevel1 = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+ sharedLibraryLevel2[0],
+ };
+
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ }
+}
diff --git a/test/688-shared-library/src-ex/Main.java b/test/688-shared-library/src-ex/Main.java
new file mode 100644
index 0000000000..f6555b95bc
--- /dev/null
+++ b/test/688-shared-library/src-ex/Main.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+}
diff --git a/test/688-shared-library/src-ex/SharedLibraryOne.java b/test/688-shared-library/src-ex/SharedLibraryOne.java
new file mode 100644
index 0000000000..d86755f6b2
--- /dev/null
+++ b/test/688-shared-library/src-ex/SharedLibraryOne.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class SharedLibraryOne {
+}
diff --git a/test/Android.bp b/test/Android.bp
index 561f95eb47..d85e2a6fca 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -59,8 +59,7 @@ art_cc_defaults {
shared_libs: [
"libartd",
"libartd-disassembler",
- "libvixld-arm",
- "libvixld-arm64",
+ "libvixld",
"libart-gtest",
"libdexfiled",
"libprofiled",
diff --git a/test/knownfailures.json b/test/knownfailures.json
index f0eacfae8b..ae20557e2f 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1026,6 +1026,7 @@
"677-fsi2",
"678-quickening",
"679-locks",
+ "688-shared-library",
"999-redefine-hiddenapi",
"1000-non-moving-space-stress",
"1951-monitor-enter-no-suspend",
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 14287b0f16..b4a4ada506 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -62,6 +62,18 @@ custom_env['SOONG_ALLOW_MISSING_DEPENDENCIES'] = 'true'
print(custom_env)
os.environ.update(custom_env)
+# build is just a binary/script that is directly executed to build any artifacts needed for the
+# test.
+if 'build' in target:
+ build_command = target.get('build').format(
+ ANDROID_BUILD_TOP = env.ANDROID_BUILD_TOP,
+ MAKE_OPTIONS='DX= -j{threads}'.format(threads = n_threads))
+ sys.stdout.write(str(build_command) + '\n')
+ sys.stdout.flush()
+ if subprocess.call(build_command.split()):
+ sys.exit(1)
+
+# make runs soong/kati to build the target listed in the entry.
if 'make' in target:
build_command = 'build/soong/soong_ui.bash --make-mode'
build_command += ' DX='
@@ -107,7 +119,8 @@ if 'run-test' in target:
run_test_command += ['--host']
run_test_command += ['--dex2oat-jobs']
run_test_command += ['4']
- run_test_command += ['-b']
+ if '--no-build-dependencies' not in test_flags:
+ run_test_command += ['-b']
run_test_command += ['--verbose']
sys.stdout.write(str(run_test_command) + '\n')
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 9efe435dc2..077129fa6c 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -329,4 +329,11 @@ target_config = {
'art-golem-linux-x64': {
'golem' : 'linux-x64'
},
+ 'art-linux-bionic-x64': {
+ 'build': '{ANDROID_BUILD_TOP}/art/tools/build_linux_bionic_tests.sh {MAKE_OPTIONS}',
+ 'run-test': ['--run-test-option=--bionic',
+ '--host',
+ '--64',
+ '--no-build-dependencies'],
+ },
}
diff --git a/tools/build_linux_bionic_tests.sh b/tools/build_linux_bionic_tests.sh
index dba2d50e38..3ee7d506aa 100755
--- a/tools/build_linux_bionic_tests.sh
+++ b/tools/build_linux_bionic_tests.sh
@@ -79,6 +79,7 @@ bionic_targets=(
$soong_out/bin/profmand
$soong_out/bin/hiddenapi
$soong_out/bin/hprof-conv
+ $soong_out/bin/timeout_dumper
$(find $ANDROID_HOST_OUT/lib64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g")
$(find $ANDROID_HOST_OUT/nativetest64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g"))
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index c1cc2e2318..da463d5511 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -61,7 +61,7 @@ while true; do
done
# Allow to build successfully in master-art.
-extra_args=SOONG_ALLOW_MISSING_DEPENDENCIES=true
+extra_args="SOONG_ALLOW_MISSING_DEPENDENCIES=true TEMPORARY_DISABLE_PATH_RESTRICTIONS=true"
if [[ $mode == "host" ]]; then
make_command="make $j_arg $extra_args $showcommands build-art-host-tests $common_targets"
@@ -94,4 +94,5 @@ done
echo "Executing $make_command"
+# Disable path restrictions to enable luci builds using vpython.
bash -c "$make_command"