diff options
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 |