summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/debug/elf_symtab_writer.h3
-rw-r--r--compiler/optimizing/bounds_check_elimination.cc45
-rw-r--r--compiler/optimizing/inliner.cc30
-rw-r--r--patchoat/Android.bp2
-rw-r--r--patchoat/patchoat.cc289
-rw-r--r--patchoat/patchoat.h15
-rw-r--r--patchoat/patchoat_test.cc278
-rw-r--r--runtime/art_method-inl.h2
-rw-r--r--runtime/art_method.h4
-rw-r--r--runtime/class_linker.cc17
-rw-r--r--runtime/common_runtime_test.h6
-rw-r--r--runtime/dex/dex_file.h8
-rw-r--r--runtime/dex/dex_hidden_access_flags.h110
-rw-r--r--runtime/fault_handler.cc14
-rw-r--r--runtime/gc/collector/concurrent_copying.cc1
-rw-r--r--runtime/gc/collector/semi_space.cc1
-rw-r--r--runtime/gc/gc_cause.cc1
-rw-r--r--runtime/gc/gc_cause.h3
-rw-r--r--runtime/gc/heap.cc69
-rw-r--r--runtime/gc/heap.h29
-rw-r--r--runtime/hidden_api_access_flags.h152
-rw-r--r--runtime/modifiers.h9
-rw-r--r--test/004-NativeAllocations/src-art/Main.java147
-rw-r--r--test/137-cfi/cfi.cc29
-rw-r--r--test/305-other-fault-handler/expected.txt2
-rw-r--r--test/305-other-fault-handler/fault_handler.cc102
-rw-r--r--test/305-other-fault-handler/info.txt3
-rw-r--r--test/305-other-fault-handler/src/Main.java25
-rw-r--r--test/449-checker-bce/src/Main.java88
-rw-r--r--test/673-checker-throw-vmethod/expected.txt1
-rw-r--r--test/673-checker-throw-vmethod/info.txt1
-rw-r--r--test/673-checker-throw-vmethod/src/Main.java219
-rw-r--r--test/Android.bp1
-rw-r--r--test/knownfailures.json6
-rw-r--r--tools/hiddenapi/hiddenapi.cc14
-rw-r--r--tools/hiddenapi/hiddenapi_test.cc100
36 files changed, 1363 insertions, 463 deletions
diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h
index 95a0b4ff47..9007360d01 100644
--- a/compiler/debug/elf_symtab_writer.h
+++ b/compiler/debug/elf_symtab_writer.h
@@ -107,7 +107,8 @@ static void WriteDebugSymbols(linker::ElfBuilder<ElfTypes>* builder,
for (auto it : debug_info.dex_files) {
uint64_t dex_address = dex->GetAddress() + it.first /* offset within the section */;
const DexFile* dex_file = it.second;
- symtab->Add(strtab->Write(kDexFileSymbolName), dex, dex_address, 0, STB_LOCAL, STT_NOTYPE);
+ typename ElfTypes::Word dex_name = strtab->Write(kDexFileSymbolName);
+ symtab->Add(dex_name, dex, dex_address, dex_file->Size(), STB_LOCAL, STT_NOTYPE);
if (mini_debug_info) {
continue; // Don't add interpreter method names to mini-debug-info for now.
}
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 9c2068ec5e..147df1e3e8 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -302,7 +302,7 @@ class ValueRange : public ArenaObject<kArenaAllocBoundsCheckElimination> {
ValueBound GetLower() const { return lower_; }
ValueBound GetUpper() const { return upper_; }
- bool IsConstantValueRange() { return lower_.IsConstant() && upper_.IsConstant(); }
+ bool IsConstantValueRange() const { return lower_.IsConstant() && upper_.IsConstant(); }
// If it's certain that this value range fits in other_range.
virtual bool FitsIn(ValueRange* other_range) const {
@@ -789,24 +789,33 @@ class BCEVisitor : public HGraphVisitor {
ApplyRangeFromComparison(left, block, false_successor, new_range);
}
} else if (cond == kCondNE || cond == kCondEQ) {
- if (left->IsArrayLength() && lower.IsConstant() && upper.IsConstant()) {
- // Special case:
- // length == [c,d] yields [c, d] along true
- // length != [c,d] yields [c, d] along false
- if (!lower.Equals(ValueBound::Min()) || !upper.Equals(ValueBound::Max())) {
- ValueRange* new_range = new (&allocator_) ValueRange(&allocator_, lower, upper);
- ApplyRangeFromComparison(
- left, block, cond == kCondEQ ? true_successor : false_successor, new_range);
- }
- // In addition:
- // length == 0 yields [1, max] along false
- // length != 0 yields [1, max] along true
- if (lower.GetConstant() == 0 && upper.GetConstant() == 0) {
- ValueRange* new_range = new (&allocator_) ValueRange(
- &allocator_, ValueBound(nullptr, 1), ValueBound::Max());
- ApplyRangeFromComparison(
- left, block, cond == kCondEQ ? false_successor : true_successor, new_range);
+ if (left->IsArrayLength()) {
+ if (lower.IsConstant() && upper.IsConstant()) {
+ // Special case:
+ // length == [c,d] yields [c, d] along true
+ // length != [c,d] yields [c, d] along false
+ if (!lower.Equals(ValueBound::Min()) || !upper.Equals(ValueBound::Max())) {
+ ValueRange* new_range = new (&allocator_) ValueRange(&allocator_, lower, upper);
+ ApplyRangeFromComparison(
+ left, block, cond == kCondEQ ? true_successor : false_successor, new_range);
+ }
+ // In addition:
+ // length == 0 yields [1, max] along false
+ // length != 0 yields [1, max] along true
+ if (lower.GetConstant() == 0 && upper.GetConstant() == 0) {
+ ValueRange* new_range = new (&allocator_) ValueRange(
+ &allocator_, ValueBound(nullptr, 1), ValueBound::Max());
+ ApplyRangeFromComparison(
+ left, block, cond == kCondEQ ? false_successor : true_successor, new_range);
+ }
}
+ } else if (lower.IsRelatedToArrayLength() && lower.Equals(upper)) {
+ // Special aliasing case, with x not array length itself:
+ // x == [length,length] yields x == length along true
+ // x != [length,length] yields x == length along false
+ ValueRange* new_range = new (&allocator_) ValueRange(&allocator_, lower, upper);
+ ApplyRangeFromComparison(
+ left, block, cond == kCondEQ ? true_successor : false_successor, new_range);
}
}
}
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 41e4bbe4ff..452be6feae 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -459,25 +459,29 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) {
}
if (actual_method != nullptr) {
+ // Single target.
bool result = TryInlineAndReplace(invoke_instruction,
actual_method,
ReferenceTypeInfo::CreateInvalid(),
/* do_rtp */ true,
cha_devirtualize);
- if (result && !invoke_instruction->IsInvokeStaticOrDirect()) {
- if (cha_devirtualize) {
- // Add dependency due to devirtulization. We've assumed resolved_method
- // has single implementation.
- outermost_graph_->AddCHASingleImplementationDependency(resolved_method);
- MaybeRecordStat(stats_, MethodCompilationStat::kCHAInline);
- } else {
- MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvokeVirtualOrInterface);
- }
- } else if (!result && invoke_instruction->IsInvokeStaticOrDirect()) {
- // Analyze always throws property for static/direct method call with single target.
- if (AlwaysThrows(actual_method)) {
- invoke_instruction->SetAlwaysThrows(true);
+ if (result) {
+ // Successfully inlined.
+ if (!invoke_instruction->IsInvokeStaticOrDirect()) {
+ if (cha_devirtualize) {
+ // Add dependency due to devirtualization. We've assumed resolved_method
+ // has single implementation.
+ outermost_graph_->AddCHASingleImplementationDependency(resolved_method);
+ MaybeRecordStat(stats_, MethodCompilationStat::kCHAInline);
+ } else {
+ MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvokeVirtualOrInterface);
+ }
}
+ } else if (!cha_devirtualize && AlwaysThrows(actual_method)) {
+ // Set always throws property for non-inlined method call with single target
+ // (unless it was obtained through CHA, because that would imply we have
+ // to add the CHA dependency, which seems not worth it).
+ invoke_instruction->SetAlwaysThrows(true);
}
return result;
}
diff --git a/patchoat/Android.bp b/patchoat/Android.bp
index 0902823644..0e8e517cd4 100644
--- a/patchoat/Android.bp
+++ b/patchoat/Android.bp
@@ -26,6 +26,7 @@ cc_defaults {
},
shared_libs: [
"libbase",
+ "libcrypto", // For computing the digest of image file
],
}
@@ -58,5 +59,6 @@ art_cc_test {
],
shared_libs: [
"libartd",
+ "libcrypto", // For computing the digest of image file
],
}
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index eb648cba18..6c9cf864b3 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -15,6 +15,7 @@
*/
#include "patchoat.h"
+#include <openssl/sha.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>
@@ -42,6 +43,7 @@
#include "gc/space/image_space.h"
#include "image-inl.h"
#include "intern_table.h"
+#include "leb128.h"
#include "mirror/dex_cache.h"
#include "mirror/executable.h"
#include "mirror/method.h"
@@ -58,6 +60,8 @@
namespace art {
+using android::base::StringPrintf;
+
static const OatHeader* GetOatHeader(const ElfFile* elf_file) {
uint64_t off = 0;
if (!elf_file->GetSectionOffsetAndSize(".rodata", &off, nullptr)) {
@@ -120,11 +124,134 @@ static bool SymlinkFile(const std::string& input_filename, const std::string& ou
return true;
}
+bool PatchOat::GeneratePatch(
+ const MemMap& original,
+ const MemMap& relocated,
+ std::vector<uint8_t>* output,
+ std::string* error_msg) {
+ // FORMAT of the patch (aka image relocation) file:
+ // * SHA-256 digest (32 bytes) of original/unrelocated file (e.g., the one from /system)
+ // * List of monotonically increasing offsets (max value defined by uint32_t) at which relocations
+ // occur.
+ // Each element is represented as the delta from the previous offset in the list (first element
+ // is a delta from 0). Each delta is encoded using unsigned LEB128: little-endian
+ // variable-length 7 bits per byte encoding, where all bytes have the highest bit (0x80) set
+ // except for the final byte which does not have that bit set. For example, 0x3f is offset 0x3f,
+ // whereas 0xbf 0x05 is offset (0x3f & 0x7f) | (0x5 << 7) which is 0x2bf. Most deltas end up
+ // being encoding using just one byte, achieving ~4x decrease in relocation file size compared
+ // to the encoding where offsets are stored verbatim, as uint32_t.
+
+ size_t original_size = original.Size();
+ size_t relocated_size = relocated.Size();
+ if (original_size != relocated_size) {
+ *error_msg =
+ StringPrintf(
+ "Original and relocated image sizes differ: %zu vs %zu", original_size, relocated_size);
+ return false;
+ }
+ if ((original_size % 4) != 0) {
+ *error_msg = StringPrintf("Image size not multiple of 4: %zu", original_size);
+ return false;
+ }
+ if (original_size > UINT32_MAX) {
+ *error_msg = StringPrintf("Image too large: %zu" , original_size);
+ return false;
+ }
+
+ const ImageHeader& relocated_header =
+ *reinterpret_cast<const ImageHeader*>(relocated.Begin());
+ // Offsets are supposed to differ between original and relocated by this value
+ off_t expected_diff = relocated_header.GetPatchDelta();
+ if (expected_diff == 0) {
+ // Can't identify offsets which are supposed to differ due to relocation
+ *error_msg = "Relocation delta is 0";
+ return false;
+ }
+
+ // Output the SHA-256 digest of the original
+ output->resize(SHA256_DIGEST_LENGTH);
+ const uint8_t* original_bytes = original.Begin();
+ SHA256(original_bytes, original_size, output->data());
+
+ // Output the list of offsets at which the original and patched images differ
+ size_t last_diff_offset = 0;
+ size_t diff_offset_count = 0;
+ const uint8_t* relocated_bytes = relocated.Begin();
+ for (size_t offset = 0; offset < original_size; offset += 4) {
+ uint32_t original_value = *reinterpret_cast<const uint32_t*>(original_bytes + offset);
+ uint32_t relocated_value = *reinterpret_cast<const uint32_t*>(relocated_bytes + offset);
+ off_t diff = relocated_value - original_value;
+ if (diff == 0) {
+ continue;
+ } else if (diff != expected_diff) {
+ *error_msg =
+ StringPrintf(
+ "Unexpected diff at offset %zu. Expected: %jd, but was: %jd",
+ offset,
+ (intmax_t) expected_diff,
+ (intmax_t) diff);
+ return false;
+ }
+
+ uint32_t offset_diff = offset - last_diff_offset;
+ last_diff_offset = offset;
+ diff_offset_count++;
+
+ EncodeUnsignedLeb128(output, offset_diff);
+ }
+
+ if (diff_offset_count == 0) {
+ *error_msg = "Original and patched images are identical";
+ return false;
+ }
+
+ return true;
+}
+
+static bool WriteRelFile(
+ const MemMap& original,
+ const MemMap& relocated,
+ const std::string& rel_filename,
+ std::string* error_msg) {
+ std::vector<uint8_t> output;
+ if (!PatchOat::GeneratePatch(original, relocated, &output, error_msg)) {
+ return false;
+ }
+
+ std::unique_ptr<File> rel_file(OS::CreateEmptyFileWriteOnly(rel_filename.c_str()));
+ if (rel_file.get() == nullptr) {
+ *error_msg = StringPrintf("Failed to create/open output file %s", rel_filename.c_str());
+ return false;
+ }
+ if (!rel_file->WriteFully(output.data(), output.size())) {
+ *error_msg = StringPrintf("Failed to write to %s", rel_filename.c_str());
+ return false;
+ }
+ if (rel_file->FlushCloseOrErase() != 0) {
+ *error_msg = StringPrintf("Failed to flush and close %s", rel_filename.c_str());
+ return false;
+ }
+
+ return true;
+}
+
bool PatchOat::Patch(const std::string& image_location,
off_t delta,
- const std::string& output_directory,
+ const std::string& output_image_directory,
+ const std::string& output_image_relocation_directory,
InstructionSet isa,
TimingLogger* timings) {
+ bool output_image = !output_image_directory.empty();
+ bool output_image_relocation = !output_image_relocation_directory.empty();
+ if ((!output_image) && (!output_image_relocation)) {
+ // Nothing to do
+ return true;
+ }
+ if ((output_image_relocation) && (delta == 0)) {
+ LOG(ERROR) << "Cannot output image relocation information when requested relocation delta is 0";
+ return false;
+ }
+
CHECK(Runtime::Current() == nullptr);
CHECK(!image_location.empty()) << "image file must have a filename.";
@@ -221,32 +348,35 @@ bool PatchOat::Patch(const std::string& image_location,
return false;
}
- MaybePic is_oat_pic = IsOatPic(elf.get());
- if (is_oat_pic >= ERROR_FIRST) {
- // Error logged by IsOatPic
- return false;
- } else if (is_oat_pic == NOT_PIC) {
- LOG(ERROR) << "patchoat cannot be used on non-PIC oat file: " << input_oat_file->GetPath();
- return false;
- } else {
- CHECK(is_oat_pic == PIC);
-
- // Create a symlink.
- std::string converted_image_filename = space->GetImageLocation();
- std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@');
- std::string output_image_filename = output_directory +
- (android::base::StartsWith(converted_image_filename, "/") ? "" : "/") +
- converted_image_filename;
- std::string output_vdex_filename =
- ImageHeader::GetVdexLocationFromImageLocation(output_image_filename);
- std::string output_oat_filename =
- ImageHeader::GetOatLocationFromImageLocation(output_image_filename);
-
- if (!ReplaceOatFileWithSymlink(input_oat_file->GetPath(),
- output_oat_filename) ||
- !SymlinkFile(input_vdex_filename, output_vdex_filename)) {
- // Errors already logged by above call.
+ if (output_image) {
+ MaybePic is_oat_pic = IsOatPic(elf.get());
+ if (is_oat_pic >= ERROR_FIRST) {
+ // Error logged by IsOatPic
return false;
+ } else if (is_oat_pic == NOT_PIC) {
+ LOG(ERROR) << "patchoat cannot be used on non-PIC oat file: " << input_oat_file->GetPath();
+ return false;
+ } else {
+ CHECK(is_oat_pic == PIC);
+
+ // Create a symlink.
+ std::string converted_image_filename = space->GetImageLocation();
+ std::replace(
+ converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@');
+ std::string output_image_filename = output_image_directory +
+ (android::base::StartsWith(converted_image_filename, "/") ? "" : "/") +
+ converted_image_filename;
+ std::string output_vdex_filename =
+ ImageHeader::GetVdexLocationFromImageLocation(output_image_filename);
+ std::string output_oat_filename =
+ ImageHeader::GetOatLocationFromImageLocation(output_image_filename);
+
+ if (!ReplaceOatFileWithSymlink(input_oat_file->GetPath(),
+ output_oat_filename) ||
+ !SymlinkFile(input_vdex_filename, output_vdex_filename)) {
+ // Errors already logged by above call.
+ return false;
+ }
}
}
@@ -267,28 +397,72 @@ bool PatchOat::Patch(const std::string& image_location,
}
}
- // Write the patched image spaces.
- for (size_t i = 0; i < spaces.size(); ++i) {
- gc::space::ImageSpace* space = spaces[i];
+ if (output_image) {
+ // Write the patched image spaces.
+ for (size_t i = 0; i < spaces.size(); ++i) {
+ gc::space::ImageSpace* space = spaces[i];
- t.NewTiming("Writing image");
- std::string converted_image_filename = space->GetImageLocation();
- std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@');
- std::string output_image_filename = output_directory +
- (android::base::StartsWith(converted_image_filename, "/") ? "" : "/") +
- converted_image_filename;
- std::unique_ptr<File> output_image_file(CreateOrOpen(output_image_filename.c_str()));
- if (output_image_file.get() == nullptr) {
- LOG(ERROR) << "Failed to open output image file at " << output_image_filename;
- return false;
+ t.NewTiming("Writing image");
+ std::string converted_image_filename = space->GetImageLocation();
+ std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@');
+ std::string output_image_filename = output_image_directory +
+ (android::base::StartsWith(converted_image_filename, "/") ? "" : "/") +
+ converted_image_filename;
+ std::unique_ptr<File> output_image_file(CreateOrOpen(output_image_filename.c_str()));
+ if (output_image_file.get() == nullptr) {
+ LOG(ERROR) << "Failed to open output image file at " << output_image_filename;
+ return false;
+ }
+
+ PatchOat& p = space_to_patchoat_map.find(space)->second;
+
+ bool success = p.WriteImage(output_image_file.get());
+ success = FinishFile(output_image_file.get(), success);
+ if (!success) {
+ return false;
+ }
}
+ }
- PatchOat& p = space_to_patchoat_map.find(space)->second;
+ if (output_image_relocation) {
+ // Write the image relocation information for each space.
+ for (size_t i = 0; i < spaces.size(); ++i) {
+ gc::space::ImageSpace* space = spaces[i];
+
+ t.NewTiming("Writing image relocation");
+ std::string original_image_filename(space->GetImageLocation() + ".rel");
+ std::string image_relocation_filename =
+ output_image_relocation_directory
+ + (android::base::StartsWith(original_image_filename, "/") ? "" : "/")
+ + original_image_filename.substr(original_image_filename.find_last_of("/"));
+ File& input_image = *space_to_file_map.find(space)->second;
+ int64_t input_image_size = input_image.GetLength();
+ if (input_image_size < 0) {
+ LOG(ERROR) << "Error while getting input image size";
+ return false;
+ }
+ std::string error_msg;
+ std::unique_ptr<MemMap> original(MemMap::MapFile(input_image_size,
+ PROT_READ,
+ MAP_PRIVATE,
+ input_image.Fd(),
+ 0,
+ /*low_4gb*/false,
+ input_image.GetPath().c_str(),
+ &error_msg));
+ if (original.get() == nullptr) {
+ LOG(ERROR) << "Unable to map image file " << input_image.GetPath() << " : " << error_msg;
+ return false;
+ }
- bool success = p.WriteImage(output_image_file.get());
- success = FinishFile(output_image_file.get(), success);
- if (!success) {
- return false;
+ PatchOat& p = space_to_patchoat_map.find(space)->second;
+ const MemMap* relocated = p.image_;
+
+ if (!WriteRelFile(*original, *relocated, image_relocation_filename, &error_msg)) {
+ LOG(ERROR) << "Failed to create image relocation file " << image_relocation_filename
+ << ": " << error_msg;
+ return false;
+ }
}
}
@@ -739,6 +913,9 @@ NO_RETURN static void Usage(const char *fmt, ...) {
UsageError(" --output-image-file=<file.art>: Specifies the exact file to write the patched");
UsageError(" image file to.");
UsageError("");
+ UsageError(" --output-image-relocation-file=<file.art.rel>: Specifies the exact file to write");
+ UsageError(" the image relocation information to.");
+ UsageError("");
UsageError(" --base-offset-delta=<delta>: Specify the amount to change the old base-offset by.");
UsageError(" This value may be negative.");
UsageError("");
@@ -754,12 +931,13 @@ static int patchoat_image(TimingLogger& timings,
InstructionSet isa,
const std::string& input_image_location,
const std::string& output_image_filename,
+ const std::string& output_image_relocation_filename,
off_t base_delta,
bool base_delta_set,
bool debug) {
CHECK(!input_image_location.empty());
- if (output_image_filename.empty()) {
- Usage("Image patching requires --output-image-file");
+ if ((output_image_filename.empty()) && (output_image_relocation_filename.empty())) {
+ Usage("Image patching requires --output-image-file or --output-image-relocation-file");
}
if (!base_delta_set) {
@@ -778,9 +956,19 @@ static int patchoat_image(TimingLogger& timings,
TimingLogger::ScopedTiming pt("patch image and oat", &timings);
- std::string output_directory =
+ std::string output_image_directory =
output_image_filename.substr(0, output_image_filename.find_last_of('/'));
- bool ret = PatchOat::Patch(input_image_location, base_delta, output_directory, isa, &timings);
+ std::string output_image_relocation_directory =
+ output_image_relocation_filename.substr(
+ 0, output_image_relocation_filename.find_last_of('/'));
+ bool ret =
+ PatchOat::Patch(
+ input_image_location,
+ base_delta,
+ output_image_directory,
+ output_image_relocation_directory,
+ isa,
+ &timings);
if (kIsDebugBuild) {
LOG(INFO) << "Exiting with return ... " << ret;
@@ -811,6 +999,7 @@ static int patchoat(int argc, char **argv) {
InstructionSet isa = InstructionSet::kNone;
std::string input_image_location;
std::string output_image_filename;
+ std::string output_image_relocation_filename;
off_t base_delta = 0;
bool base_delta_set = false;
bool dump_timings = kIsDebugBuild;
@@ -832,6 +1021,9 @@ static int patchoat(int argc, char **argv) {
input_image_location = option.substr(strlen("--input-image-location=")).data();
} else if (option.starts_with("--output-image-file=")) {
output_image_filename = option.substr(strlen("--output-image-file=")).data();
+ } else if (option.starts_with("--output-image-relocation-file=")) {
+ output_image_relocation_filename =
+ option.substr(strlen("--output-image-relocation-file=")).data();
} else if (option.starts_with("--base-offset-delta=")) {
const char* base_delta_str = option.substr(strlen("--base-offset-delta=")).data();
base_delta_set = true;
@@ -856,6 +1048,7 @@ static int patchoat(int argc, char **argv) {
isa,
input_image_location,
output_image_filename,
+ output_image_relocation_filename,
base_delta,
base_delta_set,
debug);
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 83516845d8..1033a2e5e1 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -44,12 +44,25 @@ class Class;
class PatchOat {
public:
+ // Relocates the provided image by the specified offset. If output_image_directory is non-empty,
+ // outputs the relocated image into that directory. If output_image_relocation_directory is
+ // non-empty, outputs image relocation files (see GeneratePatch) into that directory.
static bool Patch(const std::string& image_location,
off_t delta,
- const std::string& output_directory,
+ const std::string& output_image_directory,
+ const std::string& output_image_relocation_directory,
InstructionSet isa,
TimingLogger* timings);
+ // Generates a patch which can be used to efficiently relocate the original file or to check that
+ // a relocated file matches the original. The patch is generated from the difference of the
+ // |original| and the already |relocated| image, and written to |output| in the form of unsigned
+ // LEB128 for each relocation position.
+ static bool GeneratePatch(const MemMap& original,
+ const MemMap& relocated,
+ std::vector<uint8_t>* output,
+ std::string* error_msg);
+
~PatchOat() {}
PatchOat(PatchOat&&) = default;
diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc
index 86e851c72b..90cb4f8310 100644
--- a/patchoat/patchoat_test.cc
+++ b/patchoat/patchoat_test.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <openssl/sha.h>
#include <dirent.h>
#include <sys/types.h>
@@ -24,6 +25,7 @@
#include "android-base/strings.h"
#include "dexopt_test.h"
+#include "leb128.h"
#include "runtime.h"
#include <gtest/gtest.h>
@@ -137,6 +139,21 @@ class PatchoatTest : public DexoptTest {
return RunDex2OatOrPatchoat(argv, error_msg);
}
+ bool GenerateBootImageRelFile(const std::string& input_image_location,
+ const std::string& output_rel_filename,
+ off_t base_offset_delta,
+ std::string* error_msg) {
+ Runtime* const runtime = Runtime::Current();
+ std::vector<std::string> argv;
+ argv.push_back(runtime->GetPatchoatExecutable());
+ argv.push_back("--input-image-location=" + input_image_location);
+ argv.push_back("--output-image-relocation-file=" + output_rel_filename);
+ argv.push_back(StringPrintf("--base-offset-delta=0x%jx", (intmax_t) base_offset_delta));
+ argv.push_back(StringPrintf("--instruction-set=%s", GetInstructionSetString(kRuntimeISA)));
+
+ return RunDex2OatOrPatchoat(argv, error_msg);
+ }
+
bool RunDex2OatOrPatchoat(const std::vector<std::string>& args, std::string* error_msg) {
int link[2];
@@ -263,6 +280,34 @@ class PatchoatTest : public DexoptTest {
}
bool BinaryDiff(
+ const std::string& filename1,
+ const std::vector<uint8_t>& data1,
+ const std::string& filename2,
+ const std::vector<uint8_t>& data2,
+ std::string* error_msg) {
+ if (data1.size() != data1.size()) {
+ *error_msg =
+ StringPrintf(
+ "%s and %s are of different size: %zu vs %zu",
+ filename1.c_str(),
+ filename2.c_str(),
+ data1.size(),
+ data2.size());
+ return true;
+ }
+ size_t size = data1.size();
+ for (size_t i = 0; i < size; i++) {
+ if (data1[i] != data2[i]) {
+ *error_msg =
+ StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool BinaryDiff(
const std::string& filename1, const std::string& filename2, std::string* error_msg) {
std::string read_error_msg;
std::vector<uint8_t> image1;
@@ -275,26 +320,97 @@ class PatchoatTest : public DexoptTest {
*error_msg = StringPrintf("Failed to read %s: %s", filename2.c_str(), read_error_msg.c_str());
return true;
}
- if (image1.size() != image2.size()) {
+ return BinaryDiff(filename1, image1, filename2, image2, error_msg);
+ }
+
+ bool IsImageIdenticalToOriginalExceptForRelocation(
+ const std::string& relocated_filename,
+ const std::string& original_filename,
+ const std::string& rel_filename,
+ std::string* error_msg) {
+ *error_msg = "";
+ std::string read_error_msg;
+ std::vector<uint8_t> rel;
+ if (!ReadFully(rel_filename, &rel, &read_error_msg)) {
+ *error_msg =
+ StringPrintf("Failed to read %s: %s", rel_filename.c_str(), read_error_msg.c_str());
+ return false;
+ }
+ std::vector<uint8_t> relocated;
+ if (!ReadFully(relocated_filename, &relocated, &read_error_msg)) {
+ *error_msg =
+ StringPrintf("Failed to read %s: %s", relocated_filename.c_str(), read_error_msg.c_str());
+ return false;
+ }
+
+ size_t image_size = relocated.size();
+ if ((image_size % 4) != 0) {
*error_msg =
StringPrintf(
- "%s and %s are of different size: %zu vs %zu",
- filename1.c_str(),
- filename2.c_str(),
- image1.size(),
- image2.size());
- return true;
+ "Relocated image file %s size not multiple of 4: %zu",
+ relocated_filename.c_str(), image_size);
+ return false;
}
- size_t size = image1.size();
- for (size_t i = 0; i < size; i++) {
- if (image1[i] != image2[i]) {
+ if (image_size > UINT32_MAX) {
+ *error_msg =
+ StringPrintf(
+ "Relocated image file %s too large: %zu" , relocated_filename.c_str(), image_size);
+ return false;
+ }
+
+ const ImageHeader& relocated_header = *reinterpret_cast<const ImageHeader*>(relocated.data());
+ off_t expected_diff = relocated_header.GetPatchDelta();
+
+ if (expected_diff != 0) {
+ // Relocated image is expected to differ from the original due to relocation.
+ // Unrelocate the image in memory to compensate.
+ uint8_t* image_start = relocated.data();
+ const uint8_t* rel_end = &rel[rel.size()];
+ if (rel.size() < SHA256_DIGEST_LENGTH) {
*error_msg =
- StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i);
- return true;
+ StringPrintf("Malformed image relocation file %s: too short", rel_filename.c_str());
+ return false;
+ }
+ const uint8_t* rel_ptr = &rel[SHA256_DIGEST_LENGTH];
+ // The remaining .rel file consists of offsets at which relocation should've occurred.
+ // For each offset, we "unrelocate" the image by subtracting the expected relocation
+ // diff value (as specified in the image header).
+ //
+ // Each offset is encoded as a delta/diff relative to the previous offset. With the
+ // very first offset being encoded relative to offset 0.
+ // Deltas are encoded using little-endian 7 bits per byte encoding, with all bytes except
+ // the last one having the highest bit set.
+ uint32_t offset = 0;
+ while (rel_ptr != rel_end) {
+ uint32_t offset_delta = 0;
+ if (DecodeUnsignedLeb128Checked(&rel_ptr, rel_end, &offset_delta)) {
+ offset += offset_delta;
+ uint32_t *image_value = reinterpret_cast<uint32_t*>(image_start + offset);
+ *image_value -= expected_diff;
+ } else {
+ *error_msg =
+ StringPrintf(
+ "Malformed image relocation file %s: "
+ "last byte has it's most significant bit set",
+ rel_filename.c_str());
+ return false;
+ }
}
}
- return false;
+ // Image in memory is now supposed to be identical to the original. Compare it to the original.
+ std::vector<uint8_t> original;
+ if (!ReadFully(original_filename, &original, &read_error_msg)) {
+ *error_msg =
+ StringPrintf("Failed to read %s: %s", original_filename.c_str(), read_error_msg.c_str());
+ return false;
+ }
+ if (BinaryDiff(relocated_filename, relocated, original_filename, original, error_msg)) {
+ return false;
+ }
+
+ // Relocated image is identical to the original, once relocations are taken into account
+ return true;
}
};
@@ -408,4 +524,140 @@ TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) {
#endif
}
+TEST_F(PatchoatTest, RelFileSufficientToUnpatch) {
+ // This test checks that a boot image relocated using patchoat can be unrelocated using the .rel
+ // file created by patchoat.
+
+ // This test doesn't work when heap poisoning is enabled because some of the
+ // references are negated. b/72117833 is tracking the effort to have patchoat
+ // and its tests support heap poisoning.
+ TEST_DISABLED_FOR_HEAP_POISONING();
+
+ // Compile boot image into a random directory using dex2oat
+ ScratchFile dex2oat_orig_scratch;
+ dex2oat_orig_scratch.Unlink();
+ std::string dex2oat_orig_dir = dex2oat_orig_scratch.GetFilename();
+ ASSERT_EQ(0, mkdir(dex2oat_orig_dir.c_str(), 0700));
+ const uint32_t orig_base_addr = 0x60000000;
+ std::vector<std::string> dex2oat_extra_args;
+ std::string error_msg;
+ if (!CompileBootImageToDir(dex2oat_orig_dir, dex2oat_extra_args, orig_base_addr, &error_msg)) {
+ FAIL() << "CompileBootImage1 failed: " << error_msg;
+ }
+
+ // Generate image relocation file for the original boot image
+ ScratchFile rel_scratch;
+ rel_scratch.Unlink();
+ std::string rel_dir = rel_scratch.GetFilename();
+ ASSERT_EQ(0, mkdir(rel_dir.c_str(), 0700));
+ std::string dex2oat_orig_with_arch_dir =
+ dex2oat_orig_dir + "/" + GetInstructionSetString(kRuntimeISA);
+ // The arch-including symlink is needed by patchoat
+ ASSERT_EQ(0, symlink(dex2oat_orig_dir.c_str(), dex2oat_orig_with_arch_dir.c_str()));
+ off_t base_addr_delta = 0x100000;
+ if (!GenerateBootImageRelFile(
+ dex2oat_orig_dir + "/boot.art",
+ rel_dir + "/boot.art.rel",
+ base_addr_delta,
+ &error_msg)) {
+ FAIL() << "RelocateBootImage failed: " << error_msg;
+ }
+
+ // Relocate the original boot image using patchoat
+ ScratchFile relocated_scratch;
+ relocated_scratch.Unlink();
+ std::string relocated_dir = relocated_scratch.GetFilename();
+ ASSERT_EQ(0, mkdir(relocated_dir.c_str(), 0700));
+ // Use a different relocation delta from the one used when generating .rel files above. This is
+ // to make sure .rel files are not specific to a particular relocation delta.
+ base_addr_delta -= 0x10000;
+ if (!RelocateBootImage(
+ dex2oat_orig_dir + "/boot.art",
+ relocated_dir + "/boot.art",
+ base_addr_delta,
+ &error_msg)) {
+ FAIL() << "RelocateBootImage failed: " << error_msg;
+ }
+
+ // Assert that patchoat created the same set of .art and .art.rel files
+ std::vector<std::string> rel_basenames;
+ std::vector<std::string> relocated_image_basenames;
+ if (!ListDirFilesEndingWith(rel_dir, "", &rel_basenames, &error_msg)) {
+ FAIL() << "Failed to list *.art.rel files in " << rel_dir << ": " << error_msg;
+ }
+ if (!ListDirFilesEndingWith(relocated_dir, ".art", &relocated_image_basenames, &error_msg)) {
+ FAIL() << "Failed to list *.art files in " << relocated_dir << ": " << error_msg;
+ }
+ std::sort(rel_basenames.begin(), rel_basenames.end());
+ std::sort(relocated_image_basenames.begin(), relocated_image_basenames.end());
+
+ // .art and .art.rel file names output by patchoat look like
+ // tmp@art-data-<random>-<random>@boot*.art, encoding the name of the directory in their name.
+ // To compare these with each other, we retain only the part of the file name after the last @,
+ // and we also drop the extension.
+ std::vector<std::string> rel_shortened_basenames(rel_basenames.size());
+ std::vector<std::string> relocated_image_shortened_basenames(relocated_image_basenames.size());
+ for (size_t i = 0; i < rel_basenames.size(); i++) {
+ rel_shortened_basenames[i] = rel_basenames[i].substr(rel_basenames[i].find_last_of("@") + 1);
+ rel_shortened_basenames[i] =
+ rel_shortened_basenames[i].substr(0, rel_shortened_basenames[i].find("."));
+ }
+ for (size_t i = 0; i < relocated_image_basenames.size(); i++) {
+ relocated_image_shortened_basenames[i] =
+ relocated_image_basenames[i].substr(relocated_image_basenames[i].find_last_of("@") + 1);
+ relocated_image_shortened_basenames[i] =
+ relocated_image_shortened_basenames[i].substr(
+ 0, relocated_image_shortened_basenames[i].find("."));
+ }
+ ASSERT_EQ(rel_shortened_basenames, relocated_image_shortened_basenames);
+
+ // For each image file, assert that unrelocating the image produces its original version
+ for (size_t i = 0; i < relocated_image_basenames.size(); i++) {
+ const std::string& original_image_filename =
+ dex2oat_orig_dir + "/" + relocated_image_shortened_basenames[i] + ".art";
+ const std::string& relocated_image_filename =
+ relocated_dir + "/" + relocated_image_basenames[i];
+ const std::string& rel_filename = rel_dir + "/" + rel_basenames[i];
+
+ // Assert that relocated image differs from the original
+ if (!BinaryDiff(original_image_filename, relocated_image_filename, &error_msg)) {
+ FAIL() << "Relocated image " << relocated_image_filename
+ << " identical to the original image " << original_image_filename;
+ }
+
+ // Assert that relocated image is identical to the original except for relocations described in
+ // the .rel file
+ if (!IsImageIdenticalToOriginalExceptForRelocation(
+ relocated_image_filename, original_image_filename, rel_filename, &error_msg)) {
+ FAIL() << "Unrelocating " << relocated_image_filename << " using " << rel_filename
+ << " did not produce the same output as " << original_image_filename << ": " << error_msg;
+ }
+
+ // Assert that the digest of original image in .rel file is as expected
+ std::vector<uint8_t> original;
+ if (!ReadFully(original_image_filename, &original, &error_msg)) {
+ FAIL() << "Failed to read original image " << original_image_filename;
+ }
+ std::vector<uint8_t> rel;
+ if (!ReadFully(rel_filename, &rel, &error_msg)) {
+ FAIL() << "Failed to read image relocation file " << rel_filename;
+ }
+ uint8_t original_image_digest[SHA256_DIGEST_LENGTH];
+ SHA256(original.data(), original.size(), original_image_digest);
+ const uint8_t* original_image_digest_in_rel_file = rel.data();
+ if (memcmp(original_image_digest_in_rel_file, original_image_digest, SHA256_DIGEST_LENGTH)) {
+ FAIL() << "Digest of original image in " << rel_filename << " does not match the original"
+ " image " << original_image_filename;
+ }
+ }
+
+ ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true);
+ ClearDirectory(rel_dir.c_str(), /*recursive*/ true);
+ ClearDirectory(relocated_dir.c_str(), /*recursive*/ true);
+
+ rmdir(dex2oat_orig_dir.c_str());
+ rmdir(rel_dir.c_str());
+ rmdir(relocated_dir.c_str());
+}
+
} // namespace art
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index bdebe2d9e9..c9a77331a7 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -398,6 +398,7 @@ inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) {
bool is_default_conflict = IsDefaultConflicting();
bool is_compilable = IsCompilable();
bool must_count_locks = MustCountLocks();
+ HiddenApiAccessFlags::ApiList hidden_api_list = GetHiddenApiAccessFlags();
SetAccessFlags(new_value);
DCHECK_EQ(java_flags, (GetAccessFlags() & kAccJavaFlagsMask));
DCHECK_EQ(is_constructor, IsConstructor());
@@ -411,6 +412,7 @@ inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) {
DCHECK_EQ(is_default_conflict, IsDefaultConflicting());
DCHECK_EQ(is_compilable, IsCompilable());
DCHECK_EQ(must_count_locks, MustCountLocks());
+ DCHECK_EQ(hidden_api_list, GetHiddenApiAccessFlags());
} else {
SetAccessFlags(new_value);
}
diff --git a/runtime/art_method.h b/runtime/art_method.h
index cd06354859..4501450e05 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -336,6 +336,10 @@ class ArtMethod FINAL {
AddAccessFlags(kAccMustCountLocks);
}
+ HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags() {
+ return HiddenApiAccessFlags::DecodeFromRuntime(GetAccessFlags());
+ }
+
// Returns true if this method could be overridden by a default method.
bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 877654247c..b61fb4afe9 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3286,7 +3286,15 @@ void ClassLinker::LoadField(const ClassDataItemIterator& it,
const uint32_t field_idx = it.GetMemberIndex();
dst->SetDexFieldIndex(field_idx);
dst->SetDeclaringClass(klass.Get());
- dst->SetAccessFlags(it.GetFieldAccessFlags());
+
+ // Get access flags from the DexFile. If this is a boot class path class,
+ // also set its runtime hidden API access flags.
+ uint32_t access_flags = it.GetFieldAccessFlags();
+ if (klass->IsBootStrapClassLoaded()) {
+ access_flags =
+ HiddenApiAccessFlags::EncodeForRuntime(access_flags, it.DecodeHiddenAccessFlags());
+ }
+ dst->SetAccessFlags(access_flags);
}
void ClassLinker::LoadMethod(const DexFile& dex_file,
@@ -3302,8 +3310,15 @@ void ClassLinker::LoadMethod(const DexFile& dex_file,
dst->SetDeclaringClass(klass.Get());
dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
+ // Get access flags from the DexFile. If this is a boot class path class,
+ // also set its runtime hidden API access flags.
uint32_t access_flags = it.GetMethodAccessFlags();
+ if (klass->IsBootStrapClassLoaded()) {
+ access_flags =
+ HiddenApiAccessFlags::EncodeForRuntime(access_flags, it.DecodeHiddenAccessFlags());
+ }
+
if (UNLIKELY(strcmp("finalize", method_name) == 0)) {
// Set finalizable flag on declaring class.
if (strcmp("V", dex_file.GetShorty(method_id.proto_idx_)) == 0) {
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 1c73240eea..0aed70a330 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -311,6 +311,12 @@ class CheckJniAbortCatcher {
printf("WARNING: TEST DISABLED FOR COMPACT DEX\n"); \
return; \
}
+
+#define TEST_DISABLED_FOR_HEAP_POISONING() \
+ if (kPoisonHeapReferences) { \
+ printf("WARNING: TEST DISABLED FOR HEAP POISONING\n"); \
+ return; \
+ }
} // namespace art
#endif // ART_RUNTIME_COMMON_RUNTIME_TEST_H_
diff --git a/runtime/dex/dex_file.h b/runtime/dex/dex_file.h
index 2055f848d9..1ee48f71bc 100644
--- a/runtime/dex/dex_file.h
+++ b/runtime/dex/dex_file.h
@@ -27,9 +27,9 @@
#include "base/macros.h"
#include "base/value_object.h"
#include "dex_file_types.h"
-#include "dex_hidden_access_flags.h"
#include "dex_instruction_iterator.h"
#include "globals.h"
+#include "hidden_api_access_flags.h"
#include "jni.h"
#include "modifiers.h"
@@ -1261,10 +1261,10 @@ class ClassDataItemIterator {
return GetMemberAccessFlags() & kAccValidMethodFlags;
}
uint32_t GetMemberAccessFlags() const {
- return DexHiddenAccessFlags::RemoveHiddenFlags(GetRawMemberAccessFlags());
+ return HiddenApiAccessFlags::RemoveFromDex(GetRawMemberAccessFlags());
}
- DexHiddenAccessFlags::ApiList DecodeHiddenAccessFlags() const {
- return DexHiddenAccessFlags::Decode(GetRawMemberAccessFlags());
+ HiddenApiAccessFlags::ApiList DecodeHiddenAccessFlags() const {
+ return HiddenApiAccessFlags::DecodeFromDex(GetRawMemberAccessFlags());
}
bool MemberIsNative() const {
return GetRawMemberAccessFlags() & kAccNative;
diff --git a/runtime/dex/dex_hidden_access_flags.h b/runtime/dex/dex_hidden_access_flags.h
deleted file mode 100644
index 16fae86b24..0000000000
--- a/runtime/dex/dex_hidden_access_flags.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_DEX_DEX_HIDDEN_ACCESS_FLAGS_H_
-#define ART_RUNTIME_DEX_DEX_HIDDEN_ACCESS_FLAGS_H_
-
-#include "base/bit_utils.h"
-#include "modifiers.h"
-
-namespace art {
-
-/* This class is used for encoding and decoding access flags of DexFile members
- * from the boot class path. These access flags might contain additional two bits
- * of information on whether the given class member should be hidden from apps.
- *
- * First bit is encoded as inversion of visibility flags (public/private/protected).
- * At most one can be set for any given class member. If two or three are set,
- * this is interpreted as the first bit being set and actual visibility flags
- * being the complement of the encoded flags.
- *
- * Second bit is either encoded as bit 5 for fields and non-native methods, where
- * it carries no other meaning. If a method is native, bit 9 is used.
- *
- * Bits were selected so that they never increase the length of unsigned LEB-128
- * encoding of the access flags.
- */
-class DexHiddenAccessFlags {
- public:
- enum ApiList {
- kWhitelist = 0,
- kLightGreylist,
- kDarkGreylist,
- kBlacklist,
- };
-
- static ALWAYS_INLINE ApiList Decode(uint32_t access_flags) {
- DexHiddenAccessFlags flags(access_flags);
- uint32_t int_value = (flags.IsFirstBitSet() ? 1 : 0) + (flags.IsSecondBitSet() ? 2 : 0);
- return static_cast<ApiList>(int_value);
- }
-
- static ALWAYS_INLINE uint32_t RemoveHiddenFlags(uint32_t access_flags) {
- DexHiddenAccessFlags flags(access_flags);
- flags.SetFirstBit(false);
- flags.SetSecondBit(false);
- return flags.GetEncoding();
- }
-
- static ALWAYS_INLINE uint32_t Encode(uint32_t access_flags, ApiList value) {
- DexHiddenAccessFlags flags(access_flags);
- uint32_t int_value = static_cast<uint32_t>(value);
- flags.SetFirstBit((int_value & 1) != 0);
- flags.SetSecondBit((int_value & 2) != 0);
- return flags.GetEncoding();
- }
-
- private:
- explicit DexHiddenAccessFlags(uint32_t access_flags) : access_flags_(access_flags) {}
-
- ALWAYS_INLINE uint32_t GetSecondFlag() {
- return ((access_flags_ & kAccNative) != 0) ? kAccDexHiddenBitNative : kAccDexHiddenBit;
- }
-
- ALWAYS_INLINE bool IsFirstBitSet() {
- static_assert(IsPowerOfTwo(0u), "Following statement checks if *at most* one bit is set");
- return !IsPowerOfTwo(access_flags_ & kAccVisibilityFlags);
- }
-
- ALWAYS_INLINE void SetFirstBit(bool value) {
- if (IsFirstBitSet() != value) {
- access_flags_ ^= kAccVisibilityFlags;
- }
- }
-
- ALWAYS_INLINE bool IsSecondBitSet() {
- return (access_flags_ & GetSecondFlag()) != 0;
- }
-
- ALWAYS_INLINE void SetSecondBit(bool value) {
- if (value) {
- access_flags_ |= GetSecondFlag();
- } else {
- access_flags_ &= ~GetSecondFlag();
- }
- }
-
- ALWAYS_INLINE uint32_t GetEncoding() const {
- return access_flags_;
- }
-
- uint32_t access_flags_;
-};
-
-} // namespace art
-
-
-#endif // ART_RUNTIME_DEX_DEX_HIDDEN_ACCESS_FLAGS_H_
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 49c2a15e86..9d6e5de803 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -201,13 +201,13 @@ bool FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
return true;
}
}
+ }
- // We hit a signal we didn't handle. This might be something for which
- // we can give more information about so call all registered handlers to
- // see if it is.
- if (HandleFaultByOtherHandlers(sig, info, context)) {
- return true;
- }
+ // We hit a signal we didn't handle. This might be something for which
+ // we can give more information about so call all registered handlers to
+ // see if it is.
+ if (HandleFaultByOtherHandlers(sig, info, context)) {
+ return true;
}
// Set a breakpoint in this function to catch unhandled signals.
@@ -232,7 +232,7 @@ void FaultManager::RemoveHandler(FaultHandler* handler) {
}
auto it2 = std::find(other_handlers_.begin(), other_handlers_.end(), handler);
if (it2 != other_handlers_.end()) {
- other_handlers_.erase(it);
+ other_handlers_.erase(it2);
return;
}
LOG(FATAL) << "Attempted to remove non existent handler " << handler;
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index a68227e0eb..1e0c0b16e4 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -300,7 +300,6 @@ void ConcurrentCopying::InitializePhase() {
objects_moved_.StoreRelaxed(0);
GcCause gc_cause = GetCurrentIteration()->GetGcCause();
if (gc_cause == kGcCauseExplicit ||
- gc_cause == kGcCauseForNativeAllocBlocking ||
gc_cause == kGcCauseCollectorTransition ||
GetCurrentIteration()->GetClearSoftReferences()) {
force_evacuate_all_ = true;
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 3150781a5a..1e136bca2e 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -193,7 +193,6 @@ void SemiSpace::MarkingPhase() {
if (generational_) {
if (GetCurrentIteration()->GetGcCause() == kGcCauseExplicit ||
GetCurrentIteration()->GetGcCause() == kGcCauseForNativeAlloc ||
- GetCurrentIteration()->GetGcCause() == kGcCauseForNativeAllocBlocking ||
GetCurrentIteration()->GetClearSoftReferences()) {
// If an explicit, native allocation-triggered, or last attempt
// collection, collect the whole heap.
diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc
index d88fcdcc95..508d76535e 100644
--- a/runtime/gc/gc_cause.cc
+++ b/runtime/gc/gc_cause.cc
@@ -33,7 +33,6 @@ const char* PrettyCause(GcCause cause) {
case kGcCauseBackground: return "Background";
case kGcCauseExplicit: return "Explicit";
case kGcCauseForNativeAlloc: return "NativeAlloc";
- case kGcCauseForNativeAllocBlocking: return "NativeAllocBlocking";
case kGcCauseCollectorTransition: return "CollectorTransition";
case kGcCauseDisableMovingGc: return "DisableMovingGc";
case kGcCauseHomogeneousSpaceCompact: return "HomogeneousSpaceCompact";
diff --git a/runtime/gc/gc_cause.h b/runtime/gc/gc_cause.h
index 78496f3ead..81781ceeb7 100644
--- a/runtime/gc/gc_cause.h
+++ b/runtime/gc/gc_cause.h
@@ -36,9 +36,6 @@ enum GcCause {
// GC triggered for a native allocation when NativeAllocationGcWatermark is exceeded.
// (This may be a blocking GC depending on whether we run a non-concurrent collector).
kGcCauseForNativeAlloc,
- // GC triggered for a native allocation when NativeAllocationBlockingGcWatermark is exceeded.
- // (This is always a blocking GC).
- kGcCauseForNativeAllocBlocking,
// GC triggered for a collector transition.
kGcCauseCollectorTransition,
// Not a real GC cause, used when we disable moving GC (currently for GetPrimitiveArrayCritical).
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 9edba96ddd..6da092c365 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -128,9 +128,6 @@ static constexpr size_t kVerifyObjectAllocationStackSize = 16 * KB /
sizeof(mirror::HeapReference<mirror::Object>);
static constexpr size_t kDefaultAllocationStackSize = 8 * MB /
sizeof(mirror::HeapReference<mirror::Object>);
-// System.runFinalization can deadlock with native allocations, to deal with this, we have a
-// timeout on how long we wait for finalizers to run. b/21544853
-static constexpr uint64_t kNativeAllocationFinalizeTimeout = MsToNs(250u);
// For deterministic compilation, we need the heap to be at a well-known address.
static constexpr uint32_t kAllocSpaceBeginForDeterministicAoT = 0x40000000;
@@ -561,12 +558,6 @@ Heap::Heap(size_t initial_size,
gc_complete_lock_ = new Mutex("GC complete lock");
gc_complete_cond_.reset(new ConditionVariable("GC complete condition variable",
*gc_complete_lock_));
- native_blocking_gc_lock_ = new Mutex("Native blocking GC lock");
- native_blocking_gc_cond_.reset(new ConditionVariable("Native blocking GC condition variable",
- *native_blocking_gc_lock_));
- native_blocking_gc_is_assigned_ = false;
- native_blocking_gc_in_progress_ = false;
- native_blocking_gcs_finished_ = 0;
thread_flip_lock_ = new Mutex("GC thread flip lock");
thread_flip_cond_.reset(new ConditionVariable("GC thread flip condition variable",
@@ -1143,7 +1134,6 @@ Heap::~Heap() {
STLDeleteElements(&continuous_spaces_);
STLDeleteElements(&discontinuous_spaces_);
delete gc_complete_lock_;
- delete native_blocking_gc_lock_;
delete thread_flip_lock_;
delete pending_task_lock_;
delete backtrace_lock_;
@@ -2556,10 +2546,6 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type,
// old_native_bytes_allocated_ now that GC has been triggered, resetting
// new_native_bytes_allocated_ to zero in the process.
old_native_bytes_allocated_.FetchAndAddRelaxed(new_native_bytes_allocated_.ExchangeRelaxed(0));
- if (gc_cause == kGcCauseForNativeAllocBlocking) {
- MutexLock mu(self, *native_blocking_gc_lock_);
- native_blocking_gc_in_progress_ = true;
- }
}
DCHECK_LT(gc_type, collector::kGcTypeMax);
@@ -3386,7 +3372,6 @@ collector::GcType Heap::WaitForGcToCompleteLocked(GcCause cause, Thread* self) {
// it results in log spam. kGcCauseExplicit is already logged in LogGC, so avoid it here too.
if (cause == kGcCauseForAlloc ||
cause == kGcCauseForNativeAlloc ||
- cause == kGcCauseForNativeAllocBlocking ||
cause == kGcCauseDisableMovingGc) {
VLOG(gc) << "Starting a blocking GC " << cause;
}
@@ -3772,59 +3757,9 @@ void Heap::RunFinalization(JNIEnv* env, uint64_t timeout) {
}
void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) {
- // See the REDESIGN section of go/understanding-register-native-allocation
- // for an explanation of how RegisterNativeAllocation works.
- size_t new_value = bytes + new_native_bytes_allocated_.FetchAndAddRelaxed(bytes);
- if (new_value > NativeAllocationBlockingGcWatermark()) {
- // Wait for a new GC to finish and finalizers to run, because the
- // allocation rate is too high.
- Thread* self = ThreadForEnv(env);
-
- bool run_gc = false;
- {
- MutexLock mu(self, *native_blocking_gc_lock_);
- uint32_t initial_gcs_finished = native_blocking_gcs_finished_;
- if (native_blocking_gc_in_progress_) {
- // A native blocking GC is in progress from the last time the native
- // allocation blocking GC watermark was exceeded. Wait for that GC to
- // finish before addressing the fact that we exceeded the blocking
- // watermark again.
- do {
- ScopedTrace trace("RegisterNativeAllocation: Wait For Prior Blocking GC Completion");
- native_blocking_gc_cond_->Wait(self);
- } while (native_blocking_gcs_finished_ == initial_gcs_finished);
- initial_gcs_finished++;
- }
-
- // It's possible multiple threads have seen that we exceeded the
- // blocking watermark. Ensure that only one of those threads is assigned
- // to run the blocking GC. The rest of the threads should instead wait
- // for the blocking GC to complete.
- if (native_blocking_gcs_finished_ == initial_gcs_finished) {
- if (native_blocking_gc_is_assigned_) {
- do {
- ScopedTrace trace("RegisterNativeAllocation: Wait For Blocking GC Completion");
- native_blocking_gc_cond_->Wait(self);
- } while (native_blocking_gcs_finished_ == initial_gcs_finished);
- } else {
- native_blocking_gc_is_assigned_ = true;
- run_gc = true;
- }
- }
- }
+ size_t old_value = new_native_bytes_allocated_.FetchAndAddRelaxed(bytes);
- if (run_gc) {
- CollectGarbageInternal(NonStickyGcType(), kGcCauseForNativeAllocBlocking, false);
- RunFinalization(env, kNativeAllocationFinalizeTimeout);
- CHECK(!env->ExceptionCheck());
-
- MutexLock mu(self, *native_blocking_gc_lock_);
- native_blocking_gc_is_assigned_ = false;
- native_blocking_gc_in_progress_ = false;
- native_blocking_gcs_finished_++;
- native_blocking_gc_cond_->Broadcast(self);
- }
- } else if (new_value > NativeAllocationGcWatermark() * HeapGrowthMultiplier() &&
+ if (old_value > NativeAllocationGcWatermark() * HeapGrowthMultiplier() &&
!IsGCRequestPending()) {
// Trigger another GC because there have been enough native bytes
// allocated since the last GC.
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 7dcf82f415..57d3d506f0 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -268,7 +268,7 @@ class Heap {
REQUIRES_SHARED(Locks::mutator_lock_);
void RegisterNativeAllocation(JNIEnv* env, size_t bytes)
- REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*native_blocking_gc_lock_);
+ REQUIRES(!*gc_complete_lock_, !*pending_task_lock_);
void RegisterNativeFree(JNIEnv* env, size_t bytes);
// Change the allocator, updates entrypoints.
@@ -1087,16 +1087,6 @@ class Heap {
return max_free_;
}
- // How large new_native_bytes_allocated_ can grow while GC is in progress
- // before we block the allocating thread to allow GC to catch up.
- ALWAYS_INLINE size_t NativeAllocationBlockingGcWatermark() const {
- // Historically the native allocations were bounded by growth_limit_. This
- // uses that same value, dividing growth_limit_ by 2 to account for
- // the fact that now the bound is relative to the number of retained
- // registered native allocations rather than absolute.
- return growth_limit_ / 2;
- }
-
void TraceHeapSize(size_t heap_size);
// Remove a vlog code from heap-inl.h which is transitively included in half the world.
@@ -1252,23 +1242,6 @@ class Heap {
// old_native_bytes_allocated_ and new_native_bytes_allocated_.
Atomic<size_t> old_native_bytes_allocated_;
- // Used for synchronization when multiple threads call into
- // RegisterNativeAllocation and require blocking GC.
- // * If a previous blocking GC is in progress, all threads will wait for
- // that GC to complete, then wait for one of the threads to complete another
- // blocking GC.
- // * If a blocking GC is assigned but not in progress, a thread has been
- // assigned to run a blocking GC but has not started yet. Threads will wait
- // for the assigned blocking GC to complete.
- // * If a blocking GC is not assigned nor in progress, the first thread will
- // run a blocking GC and signal to other threads that blocking GC has been
- // assigned.
- Mutex* native_blocking_gc_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- std::unique_ptr<ConditionVariable> native_blocking_gc_cond_ GUARDED_BY(native_blocking_gc_lock_);
- bool native_blocking_gc_is_assigned_ GUARDED_BY(native_blocking_gc_lock_);
- bool native_blocking_gc_in_progress_ GUARDED_BY(native_blocking_gc_lock_);
- uint32_t native_blocking_gcs_finished_ GUARDED_BY(native_blocking_gc_lock_);
-
// Number of bytes freed by thread local buffer revokes. This will
// cancel out the ahead-of-time bulk counting of bytes allocated in
// rosalloc thread-local buffers. It is temporarily accumulated
diff --git a/runtime/hidden_api_access_flags.h b/runtime/hidden_api_access_flags.h
new file mode 100644
index 0000000000..80a002d96e
--- /dev/null
+++ b/runtime/hidden_api_access_flags.h
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_HIDDEN_API_ACCESS_FLAGS_H_
+#define ART_RUNTIME_HIDDEN_API_ACCESS_FLAGS_H_
+
+#include "base/bit_utils.h"
+#include "modifiers.h"
+
+namespace art {
+
+/* This class is used for encoding and decoding access flags of class members
+ * from the boot class path. These access flags might contain additional two bits
+ * of information on whether the given class member should be hidden from apps
+ * and under what circumstances.
+ *
+ * The encoding is different inside DexFile, where we are concerned with size,
+ * and at runtime where we want to optimize for speed of access. The class
+ * provides helper functions to decode/encode both of them.
+ *
+ * Encoding in DexFile
+ * ===================
+ *
+ * First bit is encoded as inversion of visibility flags (public/private/protected).
+ * At most one can be set for any given class member. If two or three are set,
+ * this is interpreted as the first bit being set and actual visibility flags
+ * being the complement of the encoded flags.
+ *
+ * Second bit is either encoded as bit 5 for fields and non-native methods, where
+ * it carries no other meaning. If a method is native (bit 8 set), bit 9 is used.
+ *
+ * Bits were selected so that they never increase the length of unsigned LEB-128
+ * encoding of the access flags.
+ *
+ * Encoding at runtime
+ * ===================
+ *
+ * Two bits are set aside in the uint32_t access flags in the intrinsics ordinal
+ * space (thus intrinsics need to be special-cased). These are two consecutive
+ * bits and they are directly used to store the integer value of the ApiList
+ * enum values.
+ *
+ */
+class HiddenApiAccessFlags {
+ public:
+ enum ApiList {
+ kWhitelist = 0,
+ kLightGreylist,
+ kDarkGreylist,
+ kBlacklist,
+ };
+
+ static ALWAYS_INLINE ApiList DecodeFromDex(uint32_t dex_access_flags) {
+ DexHiddenAccessFlags flags(dex_access_flags);
+ uint32_t int_value = (flags.IsFirstBitSet() ? 1 : 0) + (flags.IsSecondBitSet() ? 2 : 0);
+ return static_cast<ApiList>(int_value);
+ }
+
+ static ALWAYS_INLINE uint32_t RemoveFromDex(uint32_t dex_access_flags) {
+ DexHiddenAccessFlags flags(dex_access_flags);
+ flags.SetFirstBit(false);
+ flags.SetSecondBit(false);
+ return flags.GetEncoding();
+ }
+
+ static ALWAYS_INLINE uint32_t EncodeForDex(uint32_t dex_access_flags, ApiList value) {
+ DexHiddenAccessFlags flags(RemoveFromDex(dex_access_flags));
+ uint32_t int_value = static_cast<uint32_t>(value);
+ flags.SetFirstBit((int_value & 1) != 0);
+ flags.SetSecondBit((int_value & 2) != 0);
+ return flags.GetEncoding();
+ }
+
+ static ALWAYS_INLINE ApiList DecodeFromRuntime(uint32_t runtime_access_flags) {
+ if ((runtime_access_flags & kAccIntrinsic) != 0) {
+ return kWhitelist;
+ } else {
+ uint32_t int_value = (runtime_access_flags & kAccHiddenApiBits) >> kAccFlagsShift;
+ return static_cast<ApiList>(int_value);
+ }
+ }
+
+ static ALWAYS_INLINE uint32_t EncodeForRuntime(uint32_t runtime_access_flags, ApiList value) {
+ CHECK_EQ(runtime_access_flags & kAccIntrinsic, 0u);
+
+ uint32_t hidden_api_flags = static_cast<uint32_t>(value) << kAccFlagsShift;
+ CHECK_EQ(hidden_api_flags & ~kAccHiddenApiBits, 0u);
+
+ runtime_access_flags &= ~kAccHiddenApiBits;
+ return runtime_access_flags | hidden_api_flags;
+ }
+
+ private:
+ static const int kAccFlagsShift = CTZ(kAccHiddenApiBits);
+ static_assert(IsPowerOfTwo((kAccHiddenApiBits >> kAccFlagsShift) + 1),
+ "kAccHiddenApiBits are not continuous");
+
+ struct DexHiddenAccessFlags {
+ explicit DexHiddenAccessFlags(uint32_t access_flags) : access_flags_(access_flags) {}
+
+ ALWAYS_INLINE uint32_t GetSecondFlag() {
+ return ((access_flags_ & kAccNative) != 0) ? kAccDexHiddenBitNative : kAccDexHiddenBit;
+ }
+
+ ALWAYS_INLINE bool IsFirstBitSet() {
+ static_assert(IsPowerOfTwo(0u), "Following statement checks if *at most* one bit is set");
+ return !IsPowerOfTwo(access_flags_ & kAccVisibilityFlags);
+ }
+
+ ALWAYS_INLINE void SetFirstBit(bool value) {
+ if (IsFirstBitSet() != value) {
+ access_flags_ ^= kAccVisibilityFlags;
+ }
+ }
+
+ ALWAYS_INLINE bool IsSecondBitSet() {
+ return (access_flags_ & GetSecondFlag()) != 0;
+ }
+
+ ALWAYS_INLINE void SetSecondBit(bool value) {
+ if (value) {
+ access_flags_ |= GetSecondFlag();
+ } else {
+ access_flags_ &= ~GetSecondFlag();
+ }
+ }
+
+ ALWAYS_INLINE uint32_t GetEncoding() const {
+ return access_flags_;
+ }
+
+ uint32_t access_flags_;
+ };
+};
+
+} // namespace art
+
+
+#endif // ART_RUNTIME_HIDDEN_API_ACCESS_FLAGS_H_
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index a72f9daad6..0e2db932bb 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -89,9 +89,11 @@ static constexpr uint32_t kAccMustCountLocks = 0x04000000; // method (ru
// virtual call.
static constexpr uint32_t kAccSingleImplementation = 0x08000000; // method (runtime)
+static constexpr uint32_t kAccHiddenApiBits = 0x30000000; // field, method
+
// Not currently used, except for intrinsic methods where these bits
// are part of the intrinsic ordinal.
-static constexpr uint32_t kAccMayBeUnusedBits = 0x70000000;
+static constexpr uint32_t kAccMayBeUnusedBits = 0x40000000;
// Set by the compiler driver when compiling boot classes with instrinsic methods.
static constexpr uint32_t kAccIntrinsic = 0x80000000; // method (runtime)
@@ -106,8 +108,9 @@ static constexpr uint32_t kAccClassIsFinalizable = 0x80000000;
// Continuous sequence of bits used to hold the ordinal of an intrinsic method. Flags
// which overlap are not valid when kAccIntrinsic is set.
-static constexpr uint32_t kAccIntrinsicBits = kAccMayBeUnusedBits | kAccSingleImplementation |
- kAccMustCountLocks | kAccCompileDontBother | kAccDefaultConflict | kAccPreviouslyWarm;
+static constexpr uint32_t kAccIntrinsicBits = kAccMayBeUnusedBits | kAccHiddenApiBits |
+ kAccSingleImplementation | kAccMustCountLocks | kAccCompileDontBother | kAccDefaultConflict |
+ kAccPreviouslyWarm;
// Valid (meaningful) bits for a field.
static constexpr uint32_t kAccValidFieldFlags = kAccPublic | kAccPrivate | kAccProtected |
diff --git a/test/004-NativeAllocations/src-art/Main.java b/test/004-NativeAllocations/src-art/Main.java
index 8712755125..29f907de0b 100644
--- a/test/004-NativeAllocations/src-art/Main.java
+++ b/test/004-NativeAllocations/src-art/Main.java
@@ -14,82 +14,109 @@
* limitations under the License.
*/
-import java.lang.reflect.*;
import java.lang.Runtime;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.PhantomReference;
import dalvik.system.VMRuntime;
public class Main {
- static Object nativeLock = new Object();
static Object deadlockLock = new Object();
- static boolean aboutToDeadlockLock = false;
- static int nativeBytes = 0;
- static Object runtime;
- static Method register_native_allocation;
- static Method register_native_free;
- static long maxMem = 0;
-
- static class NativeAllocation {
- private int bytes;
-
- NativeAllocation(int bytes, boolean testingDeadlock) throws Exception {
- this.bytes = bytes;
- register_native_allocation.invoke(runtime, bytes);
-
- // Register native allocation can only provide guarantees bounding
- // the maximum outstanding allocations if finalizers don't time
- // out. In case finalizers have timed out, wait longer for them
- // now to complete so we can test the guarantees.
- if (!testingDeadlock) {
- VMRuntime.runFinalization(0);
- }
+ static VMRuntime runtime = VMRuntime.getRuntime();
+ static volatile boolean aboutToDeadlock = false;
- synchronized (nativeLock) {
- if (!testingDeadlock) {
- nativeBytes += bytes;
- if (nativeBytes > 2 * maxMem) {
- throw new OutOfMemoryError();
- }
- }
- }
- }
+ // Save ref as a static field to ensure it doesn't get GC'd before the
+ // referent is enqueued.
+ static PhantomReference ref = null;
+ static class DeadlockingFinalizer {
protected void finalize() throws Exception {
- synchronized (nativeLock) {
- nativeBytes -= bytes;
- }
- register_native_free.invoke(runtime, bytes);
- aboutToDeadlockLock = true;
- synchronized (deadlockLock) {
- }
+ aboutToDeadlock = true;
+ synchronized (deadlockLock) { }
+ }
+ }
+
+ private static void allocateDeadlockingFinalizer() {
+ new DeadlockingFinalizer();
+ }
+
+ public static PhantomReference allocPhantom(ReferenceQueue<Object> queue) {
+ return new PhantomReference(new Object(), queue);
+ }
+
+ // Test that calling registerNativeAllocation triggers a GC eventually
+ // after a substantial number of registered native bytes.
+ private static void checkRegisterNativeAllocation() throws Exception {
+ long maxMem = Runtime.getRuntime().maxMemory();
+ int size = (int)(maxMem / 32);
+ int allocationCount = 256;
+ int maxExpectedGcDurationMs = 2000;
+
+ ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+ ref = allocPhantom(queue);
+ long total = 0;
+ for (int i = 0; !ref.isEnqueued() && i < allocationCount; ++i) {
+ runtime.registerNativeAllocation(size);
+ total += size;
+
+ // Sleep a little bit to ensure not all of the calls to
+ // registerNativeAllocation complete while GC is in the process of
+ // running.
+ Thread.sleep(maxExpectedGcDurationMs / allocationCount);
+ }
+
+ // Wait up to maxExpectedGcDurationMs to give GC a chance to finish
+ // running. If the reference isn't enqueued after that, then it is
+ // pretty unlikely (though technically still possible) that GC was
+ // triggered as intended.
+ if (queue.remove(maxExpectedGcDurationMs) == null) {
+ throw new RuntimeException("GC failed to complete");
+ }
+
+ while (total > 0) {
+ runtime.registerNativeFree(size);
+ total -= size;
+ }
+ }
+
+ // Call registerNativeAllocation repeatedly at a high rate to trigger the
+ // case of blocking registerNativeAllocation.
+ private static void triggerBlockingRegisterNativeAllocation() throws Exception {
+ long maxMem = Runtime.getRuntime().maxMemory();
+ int size = (int)(maxMem / 32);
+ int allocationCount = 256;
+
+ long total = 0;
+ for (int i = 0; i < allocationCount; ++i) {
+ runtime.registerNativeAllocation(size);
+ total += size;
+ }
+
+ while (total > 0) {
+ runtime.registerNativeFree(size);
+ total -= size;
}
}
public static void main(String[] args) throws Exception {
- Class<?> vm_runtime = Class.forName("dalvik.system.VMRuntime");
- Method get_runtime = vm_runtime.getDeclaredMethod("getRuntime");
- runtime = get_runtime.invoke(null);
- register_native_allocation = vm_runtime.getDeclaredMethod("registerNativeAllocation", Integer.TYPE);
- register_native_free = vm_runtime.getDeclaredMethod("registerNativeFree", Integer.TYPE);
- maxMem = Runtime.getRuntime().maxMemory();
- int count = 16;
- int size = (int)(maxMem / 2 / count);
- int allocation_count = 256;
- NativeAllocation[] allocations = new NativeAllocation[count];
- for (int i = 0; i < allocation_count; ++i) {
- allocations[i % count] = new NativeAllocation(size, false);
+ // Test that registerNativeAllocation triggers GC.
+ // Run this a few times in a loop to reduce the chances that the test
+ // is flaky and make sure registerNativeAllocation continues to work
+ // after the first GC is triggered.
+ for (int i = 0; i < 20; ++i) {
+ checkRegisterNativeAllocation();
}
- // Test that we don't get a deadlock if we are holding nativeLock. If there is no timeout,
- // then we will get a finalizer timeout exception.
- aboutToDeadlockLock = false;
+
+ // Test that we don't get a deadlock if we call
+ // registerNativeAllocation with a blocked finalizer.
synchronized (deadlockLock) {
- for (int i = 0; aboutToDeadlockLock != true; ++i) {
- allocations[i % count] = new NativeAllocation(size, true);
+ allocateDeadlockingFinalizer();
+ while (!aboutToDeadlock) {
+ checkRegisterNativeAllocation();
}
+
// Do more allocations now that the finalizer thread is deadlocked so that we force
- // finalization and timeout.
- for (int i = 0; i < 10; ++i) {
- allocations[i % count] = new NativeAllocation(size, true);
- }
+ // finalization and timeout.
+ triggerBlockingRegisterNativeAllocation();
}
System.out.println("Test complete");
}
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index ef758e86e1..bdfb44a87e 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -71,7 +71,7 @@ static bool CheckStack(Backtrace* bt, const std::vector<std::string>& seq) {
for (Backtrace::const_iterator it = bt->begin(); it != bt->end(); ++it) {
if (BacktraceMap::IsValid(it->map)) {
LOG(INFO) << "Got " << it->func_name << ", looking for " << seq[cur_search_index];
- if (it->func_name == seq[cur_search_index]) {
+ if (it->func_name.find(seq[cur_search_index]) != std::string::npos) {
cur_search_index++;
if (cur_search_index == seq.size()) {
return true;
@@ -107,7 +107,7 @@ static void MoreErrorInfo(pid_t pid, bool sig_quit_on_fail) {
extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(
JNIEnv*,
jobject,
- jboolean full_signatrues,
+ jboolean,
jint,
jboolean) {
#if __linux__
@@ -129,17 +129,11 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(
std::vector<std::string> seq = {
"Java_Main_unwindInProcess", // This function.
"Main.unwindInProcess", // The corresponding Java native method frame.
- "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method.
+ "java.util.Arrays.binarySearch0", // Framework method.
"Main.main" // The Java entry method.
};
- std::vector<std::string> full_seq = {
- "Java_Main_unwindInProcess", // This function.
- "boolean Main.unwindInProcess(boolean, int, boolean)", // The corresponding Java native method frame.
- "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method.
- "void Main.main(java.lang.String[])" // The Java entry method.
- };
- bool result = CheckStack(bt.get(), full_signatrues ? full_seq : seq);
+ bool result = CheckStack(bt.get(), seq);
if (!kCauseSegfault) {
return result ? JNI_TRUE : JNI_FALSE;
} else {
@@ -191,7 +185,7 @@ int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed
extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(
JNIEnv*,
jobject,
- jboolean full_signatrues,
+ jboolean,
jint pid_int) {
#if __linux__
// TODO: What to do on Valgrind?
@@ -235,20 +229,11 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(
// Note: For some reason, the name isn't
// resolved, so don't look for it right now.
"Main.sleep", // The corresponding Java native method frame.
- "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method.
+ "java.util.Arrays.binarySearch0", // Framework method.
"Main.main" // The Java entry method.
};
- std::vector<std::string> full_seq = {
- // "Java_Main_sleep", // The sleep function being executed in the
- // other runtime.
- // Note: For some reason, the name isn't
- // resolved, so don't look for it right now.
- "boolean Main.sleep(int, boolean, double)", // The corresponding Java native method frame.
- "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method.
- "void Main.main(java.lang.String[])" // The Java entry method.
- };
- result = CheckStack(bt.get(), full_signatrues ? full_seq : seq);
+ result = CheckStack(bt.get(), seq);
}
constexpr bool kSigQuitOnFail = true;
diff --git a/test/305-other-fault-handler/expected.txt b/test/305-other-fault-handler/expected.txt
new file mode 100644
index 0000000000..6221e8e853
--- /dev/null
+++ b/test/305-other-fault-handler/expected.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+Passed!
diff --git a/test/305-other-fault-handler/fault_handler.cc b/test/305-other-fault-handler/fault_handler.cc
new file mode 100644
index 0000000000..f04832613b
--- /dev/null
+++ b/test/305-other-fault-handler/fault_handler.cc
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+
+#include <atomic>
+#include <memory>
+
+#include <jni.h>
+#include <signal.h>
+#include <stdint.h>
+#include <sys/mman.h>
+
+#include "fault_handler.h"
+#include "globals.h"
+#include "mem_map.h"
+
+namespace art {
+
+class TestFaultHandler FINAL : public FaultHandler {
+ public:
+ explicit TestFaultHandler(FaultManager* manager)
+ : FaultHandler(manager),
+ map_error_(""),
+ target_map_(MemMap::MapAnonymous("test-305-mmap",
+ /* addr */ nullptr,
+ /* byte_count */ kPageSize,
+ /* prot */ PROT_NONE,
+ /* low_4gb */ false,
+ /* reuse */ false,
+ /* error_msg */ &map_error_,
+ /* use_ashmem */ false)),
+ was_hit_(false) {
+ CHECK(target_map_ != nullptr) << "Unable to create segfault target address " << map_error_;
+ manager_->AddHandler(this, /*in_generated_code*/false);
+ }
+
+ virtual ~TestFaultHandler() {
+ manager_->RemoveHandler(this);
+ }
+
+ bool Action(int sig, siginfo_t* siginfo, void* context ATTRIBUTE_UNUSED) OVERRIDE {
+ CHECK_EQ(sig, SIGSEGV);
+ CHECK_EQ(reinterpret_cast<uint32_t*>(siginfo->si_addr),
+ GetTargetPointer()) << "Segfault on unexpected address!";
+ CHECK(!was_hit_) << "Recursive signal!";
+ was_hit_ = true;
+
+ LOG(INFO) << "SEGV Caught. mprotecting map.";
+ CHECK(target_map_->Protect(PROT_READ | PROT_WRITE)) << "Failed to mprotect R/W";
+ LOG(INFO) << "Setting value to be read.";
+ *GetTargetPointer() = kDataValue;
+ LOG(INFO) << "Changing prot to be read-only.";
+ CHECK(target_map_->Protect(PROT_READ)) << "Failed to mprotect R-only";
+ return true;
+ }
+
+ void CauseSegfault() {
+ CHECK_EQ(target_map_->GetProtect(), PROT_NONE);
+
+ // This will segfault. The handler should deal with it though and we will get a value out of it.
+ uint32_t data = *GetTargetPointer();
+
+ // Prevent re-ordering around the *GetTargetPointer by the compiler
+ std::atomic_signal_fence(std::memory_order_seq_cst);
+
+ CHECK(was_hit_);
+ CHECK_EQ(data, kDataValue) << "Unexpected read value from mmap";
+ CHECK_EQ(target_map_->GetProtect(), PROT_READ);
+ LOG(INFO) << "Success!";
+ }
+
+ private:
+ uint32_t* GetTargetPointer() {
+ return reinterpret_cast<uint32_t*>(target_map_->Begin() + 8);
+ }
+
+ static constexpr uint32_t kDataValue = 0xDEADBEEF;
+
+ std::string map_error_;
+ std::unique_ptr<MemMap> target_map_;
+ bool was_hit_;
+};
+
+extern "C" JNIEXPORT void JNICALL Java_Main_runFaultHandlerTest(JNIEnv*, jclass) {
+ std::unique_ptr<TestFaultHandler> handler(new TestFaultHandler(&fault_manager));
+ handler->CauseSegfault();
+}
+
+} // namespace art
diff --git a/test/305-other-fault-handler/info.txt b/test/305-other-fault-handler/info.txt
new file mode 100644
index 0000000000..656c8bd406
--- /dev/null
+++ b/test/305-other-fault-handler/info.txt
@@ -0,0 +1,3 @@
+Test that we correctly handle basic non-generated-code fault handlers
+
+Tests that we can use and remove these handlers and they can change mappings.
diff --git a/test/305-other-fault-handler/src/Main.java b/test/305-other-fault-handler/src/Main.java
new file mode 100644
index 0000000000..13a6fef730
--- /dev/null
+++ b/test/305-other-fault-handler/src/Main.java
@@ -0,0 +1,25 @@
+/*
+ * 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 {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ runFaultHandlerTest();
+ System.out.println("Passed!");
+ }
+
+ public static native void runFaultHandlerTest();
+}
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index 60e653c72f..3506649d3c 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -1057,6 +1057,64 @@ public class Main {
}
}
+ /// CHECK-START: void Main.lengthAlias1(int[], int) BCE (before)
+ /// CHECK-DAG: <<Arr:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Nul:l\d+>> NullCheck [<<Arr>>] loop:none
+ /// CHECK-DAG: <<Len:i\d+>> ArrayLength [<<Nul>>] loop:none
+ /// CHECK-DAG: NotEqual [<<Par>>,<<Len>>] loop:none
+ /// CHECK-DAG: <<Idx:i\d+>> Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: BoundsCheck [<<Idx>>,<<Len>>] loop:<<Loop>>
+ //
+ /// CHECK-START: void Main.lengthAlias1(int[], int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ public static void lengthAlias1(int[] a, int len) {
+ if (len == a.length) {
+ for (int i = 0; i < len; i++) {
+ a[i] = 1;
+ }
+ }
+ }
+
+ /// CHECK-START: void Main.lengthAlias2(int[], int) BCE (before)
+ /// CHECK-DAG: <<Arr:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Nul:l\d+>> NullCheck [<<Arr>>] loop:none
+ /// CHECK-DAG: <<Len:i\d+>> ArrayLength [<<Nul>>] loop:none
+ /// CHECK-DAG: Equal [<<Par>>,<<Len>>] loop:none
+ /// CHECK-DAG: <<Idx:i\d+>> Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: BoundsCheck [<<Idx>>,<<Len>>] loop:<<Loop>>
+ //
+ /// CHECK-START: void Main.lengthAlias2(int[], int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ public static void lengthAlias2(int[] a, int len) {
+ if (len != a.length) {
+ return;
+ }
+ for (int i = 0; i < len; i++) {
+ a[i] = 2;
+ }
+ }
+
+ /// CHECK-START: void Main.lengthAlias3(int[], int) BCE (before)
+ /// CHECK-DAG: <<Arr:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Nul:l\d+>> NullCheck [<<Arr>>] loop:none
+ /// CHECK-DAG: <<Len:i\d+>> ArrayLength [<<Nul>>] loop:none
+ /// CHECK-DAG: NotEqual [<<Par>>,<<Len>>] loop:none
+ /// CHECK-DAG: <<Idx:i\d+>> Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: BoundsCheck [<<Idx>>,<<Len>>] loop:<<Loop>>
+ //
+ /// CHECK-START: void Main.lengthAlias3(int[], int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ public static void lengthAlias3(int[] a, int len) {
+ if (a.length == len) {
+ for (int i = 0; i < len; i++) {
+ a[i] = 3;
+ }
+ }
+ }
+
static int[][] mA;
/// CHECK-START: void Main.dynamicBCEAndIntrinsic(int) BCE (before)
@@ -1747,10 +1805,40 @@ public class Main {
System.out.println("nonzero length failed!");
}
+ array = new int[8];
+ lengthAlias1(array, 8);
+ for (int i = 0; i < 8; i++) {
+ if (array[i] != 1) {
+ System.out.println("alias1 failed!");
+ }
+ }
+ lengthAlias2(array, 8);
+ for (int i = 0; i < 8; i++) {
+ if (array[i] != 2) {
+ System.out.println("alias2 failed!");
+ }
+ }
+ lengthAlias3(array, 8);
+ for (int i = 0; i < 8; i++) {
+ if (array[i] != 3) {
+ System.out.println("alias3 failed!");
+ }
+ }
+
+ lengthAlias1(array, /*mismatched value*/ 32);
+ for (int i = 0; i < 8; i++) {
+ if (array[i] != 3) {
+ System.out.println("mismatch failed!");
+ }
+ }
+
// Zero length array does not break.
array = new int[0];
nonzeroLength(array);
knownLength(array);
+ lengthAlias1(array, 0);
+ lengthAlias2(array, 0);
+ lengthAlias3(array, 0);
mA = new int[4][4];
for (int i = 0; i < 4; i++) {
diff --git a/test/673-checker-throw-vmethod/expected.txt b/test/673-checker-throw-vmethod/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/673-checker-throw-vmethod/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/673-checker-throw-vmethod/info.txt b/test/673-checker-throw-vmethod/info.txt
new file mode 100644
index 0000000000..250810be15
--- /dev/null
+++ b/test/673-checker-throw-vmethod/info.txt
@@ -0,0 +1 @@
+Test detecting throwing methods for code sinking.
diff --git a/test/673-checker-throw-vmethod/src/Main.java b/test/673-checker-throw-vmethod/src/Main.java
new file mode 100644
index 0000000000..d0e1591bdb
--- /dev/null
+++ b/test/673-checker-throw-vmethod/src/Main.java
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+/**
+ * Tests for detecting throwing methods for code sinking.
+ */
+public class Main {
+
+ //
+ // Some "runtime library" methods.
+ //
+
+ public final void doThrow(String par) {
+ throw new Error("you are null: " + par);
+ }
+
+ public final void checkNotNullDirect(Object obj, String par) {
+ if (obj == null)
+ throw new Error("you are null: " + par);
+ }
+
+ public final void checkNotNullSplit(Object obj, String par) {
+ if (obj == null)
+ doThrow(par);
+ }
+
+ //
+ // Various ways of enforcing non-null parameter.
+ // In all cases, par should be subject to code sinking.
+ //
+
+ /// CHECK-START: void Main.doit1(int[]) code_sinking (before)
+ /// CHECK: begin_block
+ /// CHECK: <<Str:l\d+>> LoadString
+ /// CHECK: <<Tst:z\d+>> NotEqual
+ /// CHECK: If [<<Tst>>]
+ /// CHECK: end_block
+ /// CHECK: begin_block
+ /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>]
+ /// CHECK: Throw
+ /// CHECK: end_block
+ //
+ /// CHECK-START: void Main.doit1(int[]) code_sinking (after)
+ /// CHECK: begin_block
+ /// CHECK: <<Tst:z\d+>> NotEqual
+ /// CHECK: If [<<Tst>>]
+ /// CHECK: end_block
+ /// CHECK: begin_block
+ /// CHECK: <<Str:l\d+>> LoadString
+ /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>]
+ /// CHECK: Throw
+ /// CHECK: end_block
+ public void doit1(int[] a) {
+ String par = "a";
+ if (a == null)
+ throw new Error("you are null: " + par);
+ for (int i = 0; i < a.length; i++) {
+ a[i] = 1;
+ }
+ }
+
+ /// CHECK-START: void Main.doit2(int[]) code_sinking (before)
+ /// CHECK: begin_block
+ /// CHECK: <<Str:l\d+>> LoadString
+ /// CHECK: <<Tst:z\d+>> NotEqual
+ /// CHECK: If [<<Tst>>]
+ /// CHECK: end_block
+ /// CHECK: begin_block
+ /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] method_name:Main.doThrow
+ /// CHECK: end_block
+ //
+ /// CHECK-START: void Main.doit2(int[]) code_sinking (after)
+ /// CHECK: begin_block
+ /// CHECK: <<Tst:z\d+>> NotEqual
+ /// CHECK: If [<<Tst>>]
+ /// CHECK: end_block
+ /// CHECK: begin_block
+ /// CHECK: <<Str:l\d+>> LoadString
+ /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] method_name:Main.doThrow
+ /// CHECK: end_block
+ public void doit2(int[] a) {
+ String par = "a";
+ if (a == null)
+ doThrow(par);
+ for (int i = 0; i < a.length; i++) {
+ a[i] = 2;
+ }
+ }
+
+ /// CHECK-START: void Main.doit3(int[]) code_sinking (before)
+ /// CHECK: begin_block
+ /// CHECK: <<Str:l\d+>> LoadString
+ /// CHECK: <<Tst:z\d+>> NotEqual
+ /// CHECK: If [<<Tst>>]
+ /// CHECK: end_block
+ /// CHECK: begin_block
+ /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>]
+ /// CHECK: Throw
+ /// CHECK: end_block
+ //
+ /// CHECK-START: void Main.doit3(int[]) code_sinking (after)
+ /// CHECK: begin_block
+ /// CHECK: <<Tst:z\d+>> NotEqual
+ /// CHECK: If [<<Tst>>]
+ /// CHECK: end_block
+ /// CHECK: begin_block
+ /// CHECK: <<Str:l\d+>> LoadString
+ /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>]
+ /// CHECK: Throw
+ /// CHECK: end_block
+ public void doit3(int[] a) {
+ String par = "a";
+ checkNotNullDirect(a, par);
+ for (int i = 0; i < a.length; i++) {
+ a[i] = 3;
+ }
+ }
+
+ /// CHECK-START: void Main.doit4(int[]) code_sinking (before)
+ /// CHECK: begin_block
+ /// CHECK: <<Str:l\d+>> LoadString
+ /// CHECK: <<Tst:z\d+>> NotEqual
+ /// CHECK: If [<<Tst>>]
+ /// CHECK: end_block
+ /// CHECK: begin_block
+ /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] method_name:Main.doThrow
+ /// CHECK: end_block
+ //
+ /// CHECK-START: void Main.doit4(int[]) code_sinking (after)
+ /// CHECK: begin_block
+ /// CHECK: <<Tst:z\d+>> NotEqual
+ /// CHECK: If [<<Tst>>]
+ /// CHECK: end_block
+ /// CHECK: begin_block
+ /// CHECK: <<Str:l\d+>> LoadString
+ /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] method_name:Main.doThrow
+ /// CHECK: end_block
+ public void doit4(int[] a) {
+ String par = "a";
+ checkNotNullSplit(a, par);
+ for (int i = 0; i < a.length; i++) {
+ a[i] = 4;
+ }
+ }
+
+ //
+ // Test driver.
+ //
+
+ static public void main(String[] args) {
+ int[] a = new int[100];
+ for (int i = 0; i < 100; i++) {
+ a[i] = 0;
+ }
+
+ Main m = new Main();
+
+ try {
+ m.doit1(null);
+ System.out.println("should not reach this!");
+ } catch (Error e) {
+ m.doit1(a);
+ }
+ for (int i = 0; i < 100; i++) {
+ expectEquals(1, a[i]);
+ }
+
+ try {
+ m.doit2(null);
+ System.out.println("should not reach this!");
+ } catch (Error e) {
+ m.doit2(a);
+ }
+ for (int i = 0; i < 100; i++) {
+ expectEquals(2, a[i]);
+ }
+
+ try {
+ m.doit3(null);
+ System.out.println("should not reach this!");
+ } catch (Error e) {
+ m.doit3(a);
+ }
+ for (int i = 0; i < 100; i++) {
+ expectEquals(3, a[i]);
+ }
+
+ try {
+ m.doit4(null);
+ System.out.println("should not reach this!");
+ } catch (Error e) {
+ m.doit4(a);
+ }
+ for (int i = 0; i < 100; i++) {
+ expectEquals(4, a[i]);
+ }
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/Android.bp b/test/Android.bp
index f5ca2f0338..49a34a1246 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -369,6 +369,7 @@ cc_defaults {
"154-gc-loop/heap_interface.cc",
"167-visit-locks/visit_locks.cc",
"203-multi-checkpoint/multi_checkpoint.cc",
+ "305-other-fault-handler/fault_handler.cc",
"454-get-vreg/get_vreg_jni.cc",
"457-regs/regs_jni.cc",
"461-get-reference-vreg/get_reference_vreg_jni.cc",
diff --git a/test/knownfailures.json b/test/knownfailures.json
index b39d6072fa..41d976a174 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -257,10 +257,8 @@
},
{
"tests": "137-cfi",
- "description": ["CFI unwinding expects managed frames, and the test",
- "does not iterate enough to even compile. JIT also",
- "uses Generic JNI instead of the JNI compiler."],
- "variant": "interpreter | jit"
+ "description": ["CFI unwinding expects managed frames"],
+ "variant": "interpreter"
},
{
"tests": "906-iterate-heap",
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index fe72bb0231..a755fdb40b 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -24,7 +24,7 @@
#include "base/unix_file/fd_file.h"
#include "dex/art_dex_file_loader.h"
#include "dex/dex_file-inl.h"
-#include "dex/dex_hidden_access_flags.h"
+#include "hidden_api_access_flags.h"
#include "mem_map.h"
#include "os.h"
@@ -108,9 +108,9 @@ class DexMember {
// Note that this will not update the cached data of ClassDataItemIterator
// until it iterates over this item again and therefore will fail a CHECK if
// it is called multiple times on the same DexMember.
- void SetHidden(DexHiddenAccessFlags::ApiList value) {
+ void SetHidden(HiddenApiAccessFlags::ApiList value) {
const uint32_t old_flags = it_.GetRawMemberAccessFlags();
- const uint32_t new_flags = DexHiddenAccessFlags::Encode(old_flags, value);
+ const uint32_t new_flags = HiddenApiAccessFlags::EncodeForDex(old_flags, value);
CHECK_EQ(UnsignedLeb128Size(new_flags), UnsignedLeb128Size(old_flags));
// Locate the LEB128-encoded access flags in class data.
@@ -350,13 +350,13 @@ class HiddenApi FINAL {
// as the strictest.
bool is_hidden = true;
if (member.IsOnApiList(blacklist_)) {
- member.SetHidden(DexHiddenAccessFlags::kBlacklist);
+ member.SetHidden(HiddenApiAccessFlags::kBlacklist);
} else if (member.IsOnApiList(dark_greylist_)) {
- member.SetHidden(DexHiddenAccessFlags::kDarkGreylist);
+ member.SetHidden(HiddenApiAccessFlags::kDarkGreylist);
} else if (member.IsOnApiList(light_greylist_)) {
- member.SetHidden(DexHiddenAccessFlags::kLightGreylist);
+ member.SetHidden(HiddenApiAccessFlags::kLightGreylist);
} else {
- member.SetHidden(DexHiddenAccessFlags::kWhitelist);
+ member.SetHidden(HiddenApiAccessFlags::kWhitelist);
is_hidden = false;
}
diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc
index 63484053ac..af1439520f 100644
--- a/tools/hiddenapi/hiddenapi_test.cc
+++ b/tools/hiddenapi/hiddenapi_test.cc
@@ -121,7 +121,7 @@ class HiddenApiTest : public CommonRuntimeTest {
UNREACHABLE();
}
- DexHiddenAccessFlags::ApiList GetFieldHiddenFlags(const char* name,
+ HiddenApiAccessFlags::ApiList GetFieldHiddenFlags(const char* name,
uint32_t expected_visibility,
const DexFile::ClassDef& class_def,
const DexFile& dex_file) {
@@ -153,7 +153,7 @@ class HiddenApiTest : public CommonRuntimeTest {
UNREACHABLE();
}
- DexHiddenAccessFlags::ApiList GetMethodHiddenFlags(const char* name,
+ HiddenApiAccessFlags::ApiList GetMethodHiddenFlags(const char* name,
uint32_t expected_visibility,
bool expected_native,
const DexFile::ClassDef& class_def,
@@ -191,30 +191,30 @@ class HiddenApiTest : public CommonRuntimeTest {
UNREACHABLE();
}
- DexHiddenAccessFlags::ApiList GetIFieldHiddenFlags(const DexFile& dex_file) {
+ HiddenApiAccessFlags::ApiList GetIFieldHiddenFlags(const DexFile& dex_file) {
return GetFieldHiddenFlags("ifield", kAccPublic, FindClass("LMain;", dex_file), dex_file);
}
- DexHiddenAccessFlags::ApiList GetSFieldHiddenFlags(const DexFile& dex_file) {
+ HiddenApiAccessFlags::ApiList GetSFieldHiddenFlags(const DexFile& dex_file) {
return GetFieldHiddenFlags("sfield", kAccPrivate, FindClass("LMain;", dex_file), dex_file);
}
- DexHiddenAccessFlags::ApiList GetIMethodHiddenFlags(const DexFile& dex_file) {
+ HiddenApiAccessFlags::ApiList GetIMethodHiddenFlags(const DexFile& dex_file) {
return GetMethodHiddenFlags(
"imethod", 0, /* native */ false, FindClass("LMain;", dex_file), dex_file);
}
- DexHiddenAccessFlags::ApiList GetSMethodHiddenFlags(const DexFile& dex_file) {
+ HiddenApiAccessFlags::ApiList GetSMethodHiddenFlags(const DexFile& dex_file) {
return GetMethodHiddenFlags(
"smethod", kAccPublic, /* native */ false, FindClass("LMain;", dex_file), dex_file);
}
- DexHiddenAccessFlags::ApiList GetINMethodHiddenFlags(const DexFile& dex_file) {
+ HiddenApiAccessFlags::ApiList GetINMethodHiddenFlags(const DexFile& dex_file) {
return GetMethodHiddenFlags(
"inmethod", kAccPublic, /* native */ true, FindClass("LMain;", dex_file), dex_file);
}
- DexHiddenAccessFlags::ApiList GetSNMethodHiddenFlags(const DexFile& dex_file) {
+ HiddenApiAccessFlags::ApiList GetSNMethodHiddenFlags(const DexFile& dex_file) {
return GetMethodHiddenFlags(
"snmethod", kAccProtected, /* native */ true, FindClass("LMain;", dex_file), dex_file);
}
@@ -226,7 +226,7 @@ TEST_F(HiddenApiTest, InstanceFieldNoMatch) {
OpenStream(dark_greylist) << "LMain;->ifield:LBadType2;" << std::endl;
OpenStream(blacklist) << "LMain;->ifield:LBadType3;" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kWhitelist, GetIFieldHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kWhitelist, GetIFieldHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceFieldLightGreylistMatch) {
@@ -235,7 +235,7 @@ TEST_F(HiddenApiTest, InstanceFieldLightGreylistMatch) {
OpenStream(dark_greylist) << "LMain;->ifield:LBadType2;" << std::endl;
OpenStream(blacklist) << "LMain;->ifield:LBadType3;" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kLightGreylist, GetIFieldHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kLightGreylist, GetIFieldHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceFieldDarkGreylistMatch) {
@@ -244,7 +244,7 @@ TEST_F(HiddenApiTest, InstanceFieldDarkGreylistMatch) {
OpenStream(dark_greylist) << "LMain;->ifield:I" << std::endl;
OpenStream(blacklist) << "LMain;->ifield:LBadType3;" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetIFieldHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kDarkGreylist, GetIFieldHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceFieldBlacklistMatch) {
@@ -253,7 +253,7 @@ TEST_F(HiddenApiTest, InstanceFieldBlacklistMatch) {
OpenStream(dark_greylist) << "LMain;->ifield:LBadType2;" << std::endl;
OpenStream(blacklist) << "LMain;->ifield:I" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetIFieldHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetIFieldHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch1) {
@@ -262,7 +262,7 @@ TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch1) {
OpenStream(dark_greylist) << "LMain;->ifield:I" << std::endl;
OpenStream(blacklist) << "LMain;->ifield:I" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetIFieldHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetIFieldHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch2) {
@@ -271,7 +271,7 @@ TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch2) {
OpenStream(dark_greylist) << "LMain;->ifield:LBadType2;" << std::endl;
OpenStream(blacklist) << "LMain;->ifield:I" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetIFieldHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetIFieldHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch3) {
@@ -280,7 +280,7 @@ TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch3) {
OpenStream(dark_greylist) << "LMain;->ifield:I" << std::endl;
OpenStream(blacklist) << "LMain;->ifield:LBadType3;" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetIFieldHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kDarkGreylist, GetIFieldHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticFieldNoMatch) {
@@ -289,7 +289,7 @@ TEST_F(HiddenApiTest, StaticFieldNoMatch) {
OpenStream(dark_greylist) << "LMain;->sfield:LBadType2;" << std::endl;
OpenStream(blacklist) << "LMain;->sfield:LBadType3;" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kWhitelist, GetSFieldHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kWhitelist, GetSFieldHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticFieldLightGreylistMatch) {
@@ -298,7 +298,7 @@ TEST_F(HiddenApiTest, StaticFieldLightGreylistMatch) {
OpenStream(dark_greylist) << "LMain;->sfield:LBadType2;" << std::endl;
OpenStream(blacklist) << "LMain;->sfield:LBadType3;" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kLightGreylist, GetSFieldHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kLightGreylist, GetSFieldHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticFieldDarkGreylistMatch) {
@@ -307,7 +307,7 @@ TEST_F(HiddenApiTest, StaticFieldDarkGreylistMatch) {
OpenStream(dark_greylist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
OpenStream(blacklist) << "LMain;->sfield:LBadType3;" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetSFieldHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kDarkGreylist, GetSFieldHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticFieldBlacklistMatch) {
@@ -316,7 +316,7 @@ TEST_F(HiddenApiTest, StaticFieldBlacklistMatch) {
OpenStream(dark_greylist) << "LMain;->sfield:LBadType2;" << std::endl;
OpenStream(blacklist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSFieldHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetSFieldHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticFieldTwoListsMatch1) {
@@ -325,7 +325,7 @@ TEST_F(HiddenApiTest, StaticFieldTwoListsMatch1) {
OpenStream(dark_greylist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
OpenStream(blacklist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSFieldHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetSFieldHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticFieldTwoListsMatch2) {
@@ -334,7 +334,7 @@ TEST_F(HiddenApiTest, StaticFieldTwoListsMatch2) {
OpenStream(dark_greylist) << "LMain;->sfield:LBadType2;" << std::endl;
OpenStream(blacklist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSFieldHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetSFieldHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticFieldTwoListsMatch3) {
@@ -343,7 +343,7 @@ TEST_F(HiddenApiTest, StaticFieldTwoListsMatch3) {
OpenStream(dark_greylist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
OpenStream(blacklist) << "LMain;->sfield:LBadType3;" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetSFieldHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kDarkGreylist, GetSFieldHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceMethodNoMatch) {
@@ -352,7 +352,7 @@ TEST_F(HiddenApiTest, InstanceMethodNoMatch) {
OpenStream(dark_greylist) << "LMain;->imethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->imethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kWhitelist, GetIMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kWhitelist, GetIMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceMethodLightGreylistMatch) {
@@ -361,7 +361,7 @@ TEST_F(HiddenApiTest, InstanceMethodLightGreylistMatch) {
OpenStream(dark_greylist) << "LMain;->imethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->imethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kLightGreylist, GetIMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kLightGreylist, GetIMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceMethodDarkGreylistMatch) {
@@ -370,7 +370,7 @@ TEST_F(HiddenApiTest, InstanceMethodDarkGreylistMatch) {
OpenStream(dark_greylist) << "LMain;->imethod(J)V" << std::endl;
OpenStream(blacklist) << "LMain;->imethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetIMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kDarkGreylist, GetIMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceMethodBlacklistMatch) {
@@ -379,7 +379,7 @@ TEST_F(HiddenApiTest, InstanceMethodBlacklistMatch) {
OpenStream(dark_greylist) << "LMain;->imethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->imethod(J)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetIMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetIMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch1) {
@@ -388,7 +388,7 @@ TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch1) {
OpenStream(dark_greylist) << "LMain;->imethod(J)V" << std::endl;
OpenStream(blacklist) << "LMain;->imethod(J)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetIMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetIMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch2) {
@@ -397,7 +397,7 @@ TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch2) {
OpenStream(dark_greylist) << "LMain;->imethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->imethod(J)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetIMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetIMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch3) {
@@ -406,7 +406,7 @@ TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch3) {
OpenStream(dark_greylist) << "LMain;->imethod(J)V" << std::endl;
OpenStream(blacklist) << "LMain;->imethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetIMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kDarkGreylist, GetIMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticMethodNoMatch) {
@@ -415,7 +415,7 @@ TEST_F(HiddenApiTest, StaticMethodNoMatch) {
OpenStream(dark_greylist) << "LMain;->smethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->smethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kWhitelist, GetSMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kWhitelist, GetSMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticMethodLightGreylistMatch) {
@@ -424,7 +424,7 @@ TEST_F(HiddenApiTest, StaticMethodLightGreylistMatch) {
OpenStream(dark_greylist) << "LMain;->smethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->smethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kLightGreylist, GetSMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kLightGreylist, GetSMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticMethodDarkGreylistMatch) {
@@ -433,7 +433,7 @@ TEST_F(HiddenApiTest, StaticMethodDarkGreylistMatch) {
OpenStream(dark_greylist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
OpenStream(blacklist) << "LMain;->smethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetSMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kDarkGreylist, GetSMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticMethodBlacklistMatch) {
@@ -442,7 +442,7 @@ TEST_F(HiddenApiTest, StaticMethodBlacklistMatch) {
OpenStream(dark_greylist) << "LMain;->smethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetSMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticMethodTwoListsMatch1) {
@@ -451,7 +451,7 @@ TEST_F(HiddenApiTest, StaticMethodTwoListsMatch1) {
OpenStream(dark_greylist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
OpenStream(blacklist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetSMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticMethodTwoListsMatch2) {
@@ -460,7 +460,7 @@ TEST_F(HiddenApiTest, StaticMethodTwoListsMatch2) {
OpenStream(dark_greylist) << "LMain;->smethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetSMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticMethodTwoListsMatch3) {
@@ -469,7 +469,7 @@ TEST_F(HiddenApiTest, StaticMethodTwoListsMatch3) {
OpenStream(dark_greylist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
OpenStream(blacklist) << "LMain;->smethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetSMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kDarkGreylist, GetSMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceNativeMethodNoMatch) {
@@ -478,7 +478,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodNoMatch) {
OpenStream(dark_greylist) << "LMain;->inmethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->inmethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kWhitelist, GetINMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kWhitelist, GetINMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceNativeMethodLightGreylistMatch) {
@@ -487,7 +487,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodLightGreylistMatch) {
OpenStream(dark_greylist) << "LMain;->inmethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->inmethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kLightGreylist, GetINMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kLightGreylist, GetINMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceNativeMethodDarkGreylistMatch) {
@@ -496,7 +496,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodDarkGreylistMatch) {
OpenStream(dark_greylist) << "LMain;->inmethod(C)V" << std::endl;
OpenStream(blacklist) << "LMain;->inmethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetINMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kDarkGreylist, GetINMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceNativeMethodBlacklistMatch) {
@@ -505,7 +505,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodBlacklistMatch) {
OpenStream(dark_greylist) << "LMain;->inmethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->inmethod(C)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetINMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetINMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch1) {
@@ -514,7 +514,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch1) {
OpenStream(dark_greylist) << "LMain;->inmethod(C)V" << std::endl;
OpenStream(blacklist) << "LMain;->inmethod(C)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetINMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetINMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch2) {
@@ -523,7 +523,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch2) {
OpenStream(dark_greylist) << "LMain;->inmethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->inmethod(C)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetINMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetINMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch3) {
@@ -532,7 +532,7 @@ TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch3) {
OpenStream(dark_greylist) << "LMain;->inmethod(C)V" << std::endl;
OpenStream(blacklist) << "LMain;->inmethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetINMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kDarkGreylist, GetINMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticNativeMethodNoMatch) {
@@ -541,7 +541,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodNoMatch) {
OpenStream(dark_greylist) << "LMain;->snmethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->snmethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kWhitelist, GetSNMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kWhitelist, GetSNMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticNativeMethodLightGreylistMatch) {
@@ -550,7 +550,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodLightGreylistMatch) {
OpenStream(dark_greylist) << "LMain;->snmethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->snmethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kLightGreylist, GetSNMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kLightGreylist, GetSNMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticNativeMethodDarkGreylistMatch) {
@@ -559,7 +559,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodDarkGreylistMatch) {
OpenStream(dark_greylist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
OpenStream(blacklist) << "LMain;->snmethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetSNMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kDarkGreylist, GetSNMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticNativeMethodBlacklistMatch) {
@@ -568,7 +568,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodBlacklistMatch) {
OpenStream(dark_greylist) << "LMain;->snmethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSNMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetSNMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch1) {
@@ -577,7 +577,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch1) {
OpenStream(dark_greylist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
OpenStream(blacklist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSNMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetSNMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch2) {
@@ -586,7 +586,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch2) {
OpenStream(dark_greylist) << "LMain;->snmethod(LBadType2;)V" << std::endl;
OpenStream(blacklist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kBlacklist, GetSNMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kBlacklist, GetSNMethodHiddenFlags(*dex_file));
}
TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch3) {
@@ -595,7 +595,7 @@ TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch3) {
OpenStream(dark_greylist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
OpenStream(blacklist) << "LMain;->snmethod(LBadType3;)V" << std::endl;
auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
- ASSERT_EQ(DexHiddenAccessFlags::kDarkGreylist, GetSNMethodHiddenFlags(*dex_file));
+ ASSERT_EQ(HiddenApiAccessFlags::kDarkGreylist, GetSNMethodHiddenFlags(*dex_file));
}
} // namespace art