| /* |
| * 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 <string> |
| #include <vector> |
| |
| #include "android-base/logging.h" |
| |
| #include "base/logging.h" |
| #include "base/os.h" |
| #include "class_linker-inl.h" |
| #include "dex/art_dex_file_loader.h" |
| #include "dex/class_accessor-inl.h" |
| #include "dex/dex_file-inl.h" |
| #include "interpreter/unstarted_runtime.h" |
| #include "mirror/class-inl.h" |
| #include "mirror/dex_cache-inl.h" |
| #include "runtime.h" |
| #include "scoped_thread_state_change-inl.h" |
| #include "verifier/class_verifier.h" |
| #include "well_known_classes.h" |
| |
| #include <sys/stat.h> |
| #include "cmdline.h" |
| |
| namespace art { |
| |
| namespace { |
| |
| bool LoadDexFile(const std::string& dex_filename, |
| std::vector<std::unique_ptr<const DexFile>>* dex_files) { |
| ArtDexFileLoader dex_file_loader(dex_filename); |
| std::string error_msg; |
| if (!dex_file_loader.Open(/* verify= */ true, |
| /* verify_checksum= */ true, |
| &error_msg, |
| dex_files)) { |
| LOG(ERROR) << error_msg; |
| return false; |
| } |
| return true; |
| } |
| |
| jobject Install(Runtime* runtime, |
| std::vector<std::unique_ptr<const DexFile>>& in, |
| std::vector<const DexFile*>* out) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| Thread* self = Thread::Current(); |
| CHECK(self != nullptr); |
| |
| // Need well-known-classes. |
| WellKnownClasses::Init(self->GetJniEnv()); |
| // Need a class loader. Fake that we're a compiler. |
| // Note: this will run initializers through the unstarted runtime, so make sure it's |
| // initialized. |
| interpreter::UnstartedRuntime::Initialize(); |
| |
| for (std::unique_ptr<const DexFile>& dex_file : in) { |
| out->push_back(dex_file.release()); |
| } |
| |
| ClassLinker* class_linker = runtime->GetClassLinker(); |
| |
| jobject class_loader = class_linker->CreatePathClassLoader(self, *out); |
| |
| // Need to register dex files to get a working dex cache. |
| for (const DexFile* dex_file : *out) { |
| ObjPtr<mirror::DexCache> dex_cache = class_linker->RegisterDexFile( |
| *dex_file, self->DecodeJObject(class_loader)->AsClassLoader()); |
| CHECK(dex_cache != nullptr); |
| } |
| |
| return class_loader; |
| } |
| |
| struct MethodVerifierArgs : public CmdlineArgs { |
| protected: |
| using Base = CmdlineArgs; |
| |
| ParseStatus ParseCustom(const char* raw_option, |
| size_t raw_option_length, |
| std::string* error_msg) override { |
| DCHECK_EQ(strlen(raw_option), raw_option_length); |
| { |
| ParseStatus base_parse = Base::ParseCustom(raw_option, raw_option_length, error_msg); |
| if (base_parse != kParseUnknownArgument) { |
| return base_parse; |
| } |
| } |
| |
| std::string_view option(raw_option, raw_option_length); |
| if (StartsWith(option, "--dex-file=")) { |
| dex_filename_ = raw_option + strlen("--dex-file="); |
| } else if (option == "--dex-file-verifier") { |
| dex_file_verifier_ = true; |
| } else if (option == "--verbose") { |
| method_verifier_verbose_ = true; |
| } else if (option == "--verbose-debug") { |
| method_verifier_verbose_debug_ = true; |
| } else if (StartsWith(option, "--repetitions=")) { |
| char* end; |
| repetitions_ = strtoul(raw_option + strlen("--repetitions="), &end, 10); |
| } else if (StartsWith(option, "--api-level=")) { |
| char* end; |
| api_level_ = strtoul(raw_option + strlen("--api-level="), &end, 10); |
| } else { |
| return kParseUnknownArgument; |
| } |
| |
| return kParseOk; |
| } |
| |
| ParseStatus ParseChecks(std::string* error_msg) override { |
| // Perform the parent checks. |
| ParseStatus parent_checks = Base::ParseChecks(error_msg); |
| if (parent_checks != kParseOk) { |
| return parent_checks; |
| } |
| |
| // Perform our own checks. |
| if (dex_filename_ == nullptr) { |
| *error_msg = "--dex-filename not set"; |
| return kParseError; |
| } |
| |
| return kParseOk; |
| } |
| |
| std::string GetUsage() const override { |
| std::string usage; |
| |
| usage += |
| "Usage: method_verifier_cmd [options] ...\n" |
| // Dex file is required. |
| " --dex-file=<file.dex>: specifies an input dex file.\n" |
| " Example: --dex-file=app.apk\n" |
| " --dex-file-verifier: only run dex file verifier.\n" |
| " --verbose: use verbose verifier mode.\n" |
| " --verbose-debug: use verbose verifier debug mode.\n" |
| " --repetitions=<count>: repeat the verification count times.\n" |
| " --api-level=<level>: use API level for verification.\n" |
| "\n"; |
| |
| usage += Base::GetUsage(); |
| |
| return usage; |
| } |
| |
| public: |
| const char* dex_filename_ = nullptr; |
| |
| bool dex_file_verifier_ = false; |
| |
| bool method_verifier_verbose_ = false; |
| bool method_verifier_verbose_debug_ = false; |
| |
| size_t repetitions_ = 0u; |
| |
| uint32_t api_level_ = 0u; |
| }; |
| |
| struct MethodVerifierMain : public CmdlineMain<MethodVerifierArgs> { |
| bool NeedsRuntime() override { |
| return true; |
| } |
| |
| bool ExecuteWithoutRuntime() override { |
| LOG(FATAL) << "Unreachable"; |
| UNREACHABLE(); |
| } |
| |
| bool ExecuteWithRuntime(Runtime* runtime) override { |
| CHECK(args_ != nullptr); |
| |
| const size_t dex_reps = args_->dex_file_verifier_ |
| // If we're focused on the dex file verifier, use the |
| // repetitions parameter. |
| ? std::max(static_cast<size_t>(1u), args_->repetitions_) |
| // Otherwise just load the dex files once. |
| : 1; |
| |
| std::vector<std::unique_ptr<const DexFile>> unique_dex_files; |
| for (size_t i = 0; i != dex_reps; ++i) { |
| if (args_->dex_file_verifier_ && args_->repetitions_ != 0) { |
| LOG(INFO) << "Repetition " << (i + 1); |
| } |
| unique_dex_files.clear(); |
| if (!LoadDexFile(args_->dex_filename_, &unique_dex_files)) { |
| return false; |
| } |
| } |
| if (args_->dex_file_verifier_) { |
| // We're done here. |
| return true; |
| } |
| |
| ScopedObjectAccess soa(Thread::Current()); |
| std::vector<const DexFile*> dex_files; |
| jobject class_loader = Install(runtime, unique_dex_files, &dex_files); |
| CHECK(class_loader != nullptr); |
| |
| StackHandleScope<3> scope(soa.Self()); |
| Handle<mirror::ClassLoader> h_loader = scope.NewHandle( |
| soa.Decode<mirror::ClassLoader>(class_loader)); |
| MutableHandle<mirror::Class> h_klass(scope.NewHandle<mirror::Class>(nullptr)); |
| MutableHandle<mirror::DexCache> h_dex_cache(scope.NewHandle<mirror::DexCache>(nullptr)); |
| |
| if (args_->method_verifier_verbose_) { |
| gLogVerbosity.verifier = true; |
| } |
| if (args_->method_verifier_verbose_debug_) { |
| gLogVerbosity.verifier_debug = true; |
| } |
| |
| const size_t verifier_reps = std::max(static_cast<size_t>(1u), args_->repetitions_); |
| |
| ClassLinker* class_linker = runtime->GetClassLinker(); |
| for (size_t i = 0; i != verifier_reps; ++i) { |
| if (args_->repetitions_ != 0) { |
| LOG(INFO) << "Repetition " << (i + 1); |
| } |
| for (const DexFile* dex_file : dex_files) { |
| for (ClassAccessor accessor : dex_file->GetClasses()) { |
| const char* descriptor = accessor.GetDescriptor(); |
| h_klass.Assign(class_linker->FindClass(soa.Self(), descriptor, h_loader)); |
| if (h_klass == nullptr || h_klass->IsErroneous()) { |
| if (args_->repetitions_ == 0) { |
| LOG(ERROR) << "Warning: could not load " << descriptor; |
| } |
| soa.Self()->ClearException(); |
| continue; |
| } |
| h_dex_cache.Assign(h_klass->GetDexCache()); |
| std::string error_msg; |
| verifier::FailureKind res = |
| verifier::ClassVerifier::VerifyClass(soa.Self(), |
| /* verifier_deps= */ nullptr, |
| h_dex_cache->GetDexFile(), |
| h_klass, |
| h_dex_cache, |
| h_loader, |
| *h_klass->GetClassDef(), |
| runtime->GetCompilerCallbacks(), |
| verifier::HardFailLogMode::kLogWarning, |
| args_->api_level_, |
| &error_msg); |
| if (args_->repetitions_ == 0) { |
| LOG(INFO) << descriptor << ": " << res << " " << error_msg; |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| }; |
| |
| } // namespace |
| |
| } // namespace art |
| |
| int main(int argc, char** argv) { |
| // Output all logging to stderr. |
| android::base::SetLogger(android::base::StderrLogger); |
| |
| art::MethodVerifierMain main; |
| return main.Main(argc, argv); |
| } |