Andreas Gampe | 85ac360 | 2018-08-03 03:42:08 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include <string> |
| 18 | #include <vector> |
| 19 | |
| 20 | #include "android-base/logging.h" |
| 21 | |
| 22 | #include "base/logging.h" |
| 23 | #include "base/os.h" |
| 24 | #include "class_linker-inl.h" |
| 25 | #include "dex/art_dex_file_loader.h" |
| 26 | #include "dex/class_accessor-inl.h" |
| 27 | #include "dex/dex_file-inl.h" |
| 28 | #include "interpreter/unstarted_runtime.h" |
| 29 | #include "mirror/class-inl.h" |
| 30 | #include "mirror/dex_cache-inl.h" |
| 31 | #include "runtime.h" |
| 32 | #include "scoped_thread_state_change-inl.h" |
| 33 | #include "verifier/method_verifier.h" |
| 34 | #include "well_known_classes.h" |
| 35 | |
| 36 | #include <sys/stat.h> |
| 37 | #include "cmdline.h" |
| 38 | |
| 39 | namespace art { |
| 40 | |
| 41 | namespace { |
| 42 | |
| 43 | bool LoadDexFile(const std::string& dex_filename, |
| 44 | std::vector<std::unique_ptr<const DexFile>>* dex_files) { |
| 45 | const ArtDexFileLoader dex_file_loader; |
| 46 | std::string error_msg; |
| 47 | if (!dex_file_loader.Open(dex_filename.c_str(), |
| 48 | dex_filename.c_str(), |
Andreas Gampe | 9b031f7 | 2018-10-04 11:03:34 -0700 | [diff] [blame] | 49 | /* verify= */ true, |
| 50 | /* verify_checksum= */ true, |
Andreas Gampe | 85ac360 | 2018-08-03 03:42:08 -0700 | [diff] [blame] | 51 | &error_msg, |
| 52 | dex_files)) { |
| 53 | LOG(ERROR) << error_msg; |
| 54 | return false; |
| 55 | } |
| 56 | return true; |
| 57 | } |
| 58 | |
| 59 | jobject Install(Runtime* runtime, |
| 60 | std::vector<std::unique_ptr<const DexFile>>& in, |
| 61 | std::vector<const DexFile*>* out) |
| 62 | REQUIRES_SHARED(Locks::mutator_lock_) { |
| 63 | Thread* self = Thread::Current(); |
| 64 | CHECK(self != nullptr); |
| 65 | |
| 66 | // Need well-known-classes. |
| 67 | WellKnownClasses::Init(self->GetJniEnv()); |
| 68 | // Need a class loader. Fake that we're a compiler. |
| 69 | // Note: this will run initializers through the unstarted runtime, so make sure it's |
| 70 | // initialized. |
| 71 | interpreter::UnstartedRuntime::Initialize(); |
| 72 | |
| 73 | for (std::unique_ptr<const DexFile>& dex_file : in) { |
| 74 | out->push_back(dex_file.release()); |
| 75 | } |
| 76 | |
| 77 | ClassLinker* class_linker = runtime->GetClassLinker(); |
| 78 | |
| 79 | jobject class_loader = class_linker->CreatePathClassLoader(self, *out); |
| 80 | |
| 81 | // Need to register dex files to get a working dex cache. |
| 82 | for (const DexFile* dex_file : *out) { |
| 83 | ObjPtr<mirror::DexCache> dex_cache = class_linker->RegisterDexFile( |
| 84 | *dex_file, self->DecodeJObject(class_loader)->AsClassLoader()); |
| 85 | CHECK(dex_cache != nullptr); |
| 86 | } |
| 87 | |
| 88 | return class_loader; |
| 89 | } |
| 90 | |
| 91 | struct MethodVerifierArgs : public CmdlineArgs { |
| 92 | protected: |
| 93 | using Base = CmdlineArgs; |
| 94 | |
Roland Levillain | f73caca | 2018-08-24 17:19:07 +0100 | [diff] [blame] | 95 | ParseStatus ParseCustom(const StringPiece& option, std::string* error_msg) override { |
Andreas Gampe | 85ac360 | 2018-08-03 03:42:08 -0700 | [diff] [blame] | 96 | { |
| 97 | ParseStatus base_parse = Base::ParseCustom(option, error_msg); |
| 98 | if (base_parse != kParseUnknownArgument) { |
| 99 | return base_parse; |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | if (option.starts_with("--dex-file=")) { |
| 104 | dex_filename_ = option.substr(strlen("--dex-file=")).data(); |
| 105 | } else if (option == "--dex-file-verifier") { |
| 106 | dex_file_verifier_ = true; |
| 107 | } else if (option == "--verbose") { |
| 108 | method_verifier_verbose_ = true; |
| 109 | } else if (option == "--verbose-debug") { |
| 110 | method_verifier_verbose_debug_ = true; |
| 111 | } else if (option.starts_with("--repetitions=")) { |
| 112 | char* end; |
| 113 | repetitions_ = strtoul(option.substr(strlen("--repetitions=")).data(), &end, 10); |
Andreas Gampe | 6cc23ac | 2018-08-24 15:22:43 -0700 | [diff] [blame] | 114 | } else if (option.starts_with("--api-level=")) { |
| 115 | char* end; |
| 116 | api_level_ = strtoul(option.substr(strlen("--api-level=")).data(), &end, 10); |
Andreas Gampe | 85ac360 | 2018-08-03 03:42:08 -0700 | [diff] [blame] | 117 | } else { |
| 118 | return kParseUnknownArgument; |
| 119 | } |
| 120 | |
| 121 | return kParseOk; |
| 122 | } |
| 123 | |
Roland Levillain | f73caca | 2018-08-24 17:19:07 +0100 | [diff] [blame] | 124 | ParseStatus ParseChecks(std::string* error_msg) override { |
Andreas Gampe | 85ac360 | 2018-08-03 03:42:08 -0700 | [diff] [blame] | 125 | // Perform the parent checks. |
| 126 | ParseStatus parent_checks = Base::ParseChecks(error_msg); |
| 127 | if (parent_checks != kParseOk) { |
| 128 | return parent_checks; |
| 129 | } |
| 130 | |
| 131 | // Perform our own checks. |
| 132 | if (dex_filename_ == nullptr) { |
| 133 | *error_msg = "--dex-filename not set"; |
| 134 | return kParseError; |
| 135 | } |
| 136 | |
| 137 | return kParseOk; |
| 138 | } |
| 139 | |
Andreas Gampe | fa6a1b0 | 2018-09-07 08:11:55 -0700 | [diff] [blame] | 140 | std::string GetUsage() const override { |
Andreas Gampe | 85ac360 | 2018-08-03 03:42:08 -0700 | [diff] [blame] | 141 | std::string usage; |
| 142 | |
| 143 | usage += |
| 144 | "Usage: method_verifier_cmd [options] ...\n" |
| 145 | // Dex file is required. |
| 146 | " --dex-file=<file.dex>: specifies an input dex file.\n" |
| 147 | " Example: --dex-file=app.apk\n" |
| 148 | " --dex-file-verifier: only run dex file verifier.\n" |
| 149 | " --verbose: use verbose verifier mode.\n" |
| 150 | " --verbose-debug: use verbose verifier debug mode.\n" |
| 151 | " --repetitions=<count>: repeat the verification count times.\n" |
Andreas Gampe | 6cc23ac | 2018-08-24 15:22:43 -0700 | [diff] [blame] | 152 | " --api-level=<level>: use API level for verification.\n" |
Andreas Gampe | 85ac360 | 2018-08-03 03:42:08 -0700 | [diff] [blame] | 153 | "\n"; |
| 154 | |
| 155 | usage += Base::GetUsage(); |
| 156 | |
| 157 | return usage; |
| 158 | } |
| 159 | |
| 160 | public: |
| 161 | const char* dex_filename_ = nullptr; |
| 162 | |
| 163 | bool dex_file_verifier_ = false; |
| 164 | |
| 165 | bool method_verifier_verbose_ = false; |
| 166 | bool method_verifier_verbose_debug_ = false; |
| 167 | |
| 168 | size_t repetitions_ = 0u; |
Andreas Gampe | 6cc23ac | 2018-08-24 15:22:43 -0700 | [diff] [blame] | 169 | |
| 170 | uint32_t api_level_ = 0u; |
Andreas Gampe | 85ac360 | 2018-08-03 03:42:08 -0700 | [diff] [blame] | 171 | }; |
| 172 | |
| 173 | struct MethodVerifierMain : public CmdlineMain<MethodVerifierArgs> { |
Roland Levillain | bbc6e7e | 2018-08-24 16:58:47 +0100 | [diff] [blame] | 174 | bool NeedsRuntime() override { |
Andreas Gampe | 85ac360 | 2018-08-03 03:42:08 -0700 | [diff] [blame] | 175 | return true; |
| 176 | } |
| 177 | |
Roland Levillain | bbc6e7e | 2018-08-24 16:58:47 +0100 | [diff] [blame] | 178 | bool ExecuteWithoutRuntime() override { |
Andreas Gampe | 85ac360 | 2018-08-03 03:42:08 -0700 | [diff] [blame] | 179 | LOG(FATAL) << "Unreachable"; |
| 180 | UNREACHABLE(); |
| 181 | } |
| 182 | |
Roland Levillain | bbc6e7e | 2018-08-24 16:58:47 +0100 | [diff] [blame] | 183 | bool ExecuteWithRuntime(Runtime* runtime) override { |
Andreas Gampe | 85ac360 | 2018-08-03 03:42:08 -0700 | [diff] [blame] | 184 | CHECK(args_ != nullptr); |
| 185 | |
| 186 | const size_t dex_reps = args_->dex_file_verifier_ |
| 187 | // If we're focused on the dex file verifier, use the |
| 188 | // repetitions parameter. |
| 189 | ? std::max(static_cast<size_t>(1u), args_->repetitions_) |
| 190 | // Otherwise just load the dex files once. |
| 191 | : 1; |
| 192 | |
| 193 | std::vector<std::unique_ptr<const DexFile>> unique_dex_files; |
| 194 | for (size_t i = 0; i != dex_reps; ++i) { |
| 195 | if (args_->dex_file_verifier_ && args_->repetitions_ != 0) { |
| 196 | LOG(INFO) << "Repetition " << (i + 1); |
| 197 | } |
| 198 | unique_dex_files.clear(); |
| 199 | if (!LoadDexFile(args_->dex_filename_, &unique_dex_files)) { |
| 200 | return false; |
| 201 | } |
| 202 | } |
| 203 | if (args_->dex_file_verifier_) { |
| 204 | // We're done here. |
| 205 | return true; |
| 206 | } |
| 207 | |
| 208 | ScopedObjectAccess soa(Thread::Current()); |
| 209 | std::vector<const DexFile*> dex_files; |
| 210 | jobject class_loader = Install(runtime, unique_dex_files, &dex_files); |
| 211 | CHECK(class_loader != nullptr); |
| 212 | |
| 213 | StackHandleScope<2> scope(soa.Self()); |
| 214 | Handle<mirror::ClassLoader> h_loader = scope.NewHandle( |
| 215 | soa.Decode<mirror::ClassLoader>(class_loader)); |
| 216 | MutableHandle<mirror::Class> h_klass(scope.NewHandle<mirror::Class>(nullptr)); |
| 217 | |
| 218 | if (args_->method_verifier_verbose_) { |
| 219 | gLogVerbosity.verifier = true; |
| 220 | } |
| 221 | if (args_->method_verifier_verbose_debug_) { |
| 222 | gLogVerbosity.verifier_debug = true; |
| 223 | } |
| 224 | |
| 225 | const size_t verifier_reps = std::max(static_cast<size_t>(1u), args_->repetitions_); |
| 226 | |
| 227 | ClassLinker* class_linker = runtime->GetClassLinker(); |
| 228 | for (size_t i = 0; i != verifier_reps; ++i) { |
| 229 | if (args_->repetitions_ != 0) { |
| 230 | LOG(INFO) << "Repetition " << (i + 1); |
| 231 | } |
| 232 | for (const DexFile* dex_file : dex_files) { |
| 233 | for (ClassAccessor accessor : dex_file->GetClasses()) { |
| 234 | const char* descriptor = accessor.GetDescriptor(); |
| 235 | h_klass.Assign(class_linker->FindClass(soa.Self(), descriptor, h_loader)); |
| 236 | if (h_klass == nullptr || h_klass->IsErroneous()) { |
| 237 | if (args_->repetitions_ == 0) { |
| 238 | LOG(ERROR) << "Warning: could not load " << descriptor; |
| 239 | } |
| 240 | soa.Self()->ClearException(); |
| 241 | continue; |
| 242 | } |
| 243 | std::string error_msg; |
| 244 | verifier::FailureKind res = |
| 245 | verifier::MethodVerifier::VerifyClass(soa.Self(), |
| 246 | h_klass.Get(), |
| 247 | runtime->GetCompilerCallbacks(), |
| 248 | true, |
| 249 | verifier::HardFailLogMode::kLogWarning, |
Andreas Gampe | 6cc23ac | 2018-08-24 15:22:43 -0700 | [diff] [blame] | 250 | args_->api_level_, |
Andreas Gampe | 85ac360 | 2018-08-03 03:42:08 -0700 | [diff] [blame] | 251 | &error_msg); |
| 252 | if (args_->repetitions_ == 0) { |
| 253 | LOG(INFO) << descriptor << ": " << res << " " << error_msg; |
| 254 | } |
| 255 | } |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | return true; |
| 260 | } |
| 261 | }; |
| 262 | |
| 263 | } // namespace |
| 264 | |
| 265 | } // namespace art |
| 266 | |
| 267 | int main(int argc, char** argv) { |
| 268 | // Output all logging to stderr. |
| 269 | android::base::SetLogger(android::base::StderrLogger); |
| 270 | |
| 271 | art::MethodVerifierMain main; |
| 272 | return main.Main(argc, argv); |
| 273 | } |