1/* 2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "runtime/include/thread_scopes.h" 17#include "runtime/include/class.h" 18#include "runtime/include/class_linker.h" 19#include "runtime/include/class_linker-inl.h" 20#include "runtime/include/method.h" 21#include "runtime/include/runtime.h" 22#include "verification/jobs/job.h" 23 24namespace ark::verifier { 25 26// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 27#define LOG_INST() \ 28 LOG(DEBUG, VERIFIER) << "JOBFILL: " << std::hex << std::setw(sizeof(inst.GetOffset())) << std::setfill('0') \ 29 << inst.GetOffset() << std::dec << ": " << inst 30 31// NOLINTNEXTLINE(readability-function-size) 32inline bool Job::ResolveIdentifiers() { 33#if defined(__clang__) 34#pragma clang diagnostic push 35#pragma clang diagnostic ignored "-Wvoid-ptr-dereference" 36#pragma clang diagnostic ignored "-Wgnu-label-as-value" 37#elif defined(__GNUC__) 38#pragma GCC diagnostic push 39#pragma GCC diagnostic ignored "-Wpedantic" 40#endif 41 42 LOG(DEBUG, VERIFIER) << "JOBFILL: Filling Job cache for method '" << method_->GetFullName() << "'"; 43 44constexpr std::size_t DISPATCH_TABLE_HANDLER_NAMES_SIZE = <%= Panda::dispatch_table.handler_names.size %>; 45std::array<const void*, DISPATCH_TABLE_HANDLER_NAMES_SIZE> dispatchTable{ 46% Panda::dispatch_table.handler_names.each do |name| 47 &&HANDLE_<%= name %>, 48% end 49 }; 50 51 ErrorHandler errorHandler; 52 const uint8_t *start = method_->GetInstructions(); 53 size_t codeSize = method_->GetCodeSize(); 54 if (codeSize == 0) { 55 LOG(ERROR, VERIFIER) << "Zero code size is not valid. Function must contain return instruction"; 56 return false; 57 } 58 const uint8_t *end = &start[codeSize]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) 59 60 BytecodeInstructionSafe inst{start, start, end}; 61 uint8_t secondaryOpcode; 62 63 if (!inst.IsPrimaryOpcodeValid()) { 64 LOG(ERROR, VERIFIER) << "Incorrect opcode"; 65 return false; 66 } 67 goto* dispatchTable[inst.GetPrimaryOpcode()]; 68 69% dt_non_prefixed_min_minus = (Panda::dispatch_table.invalid_non_prefixed_interval.min - 1).to_s 70% dt_non_prefixed_max_plus = (Panda::dispatch_table.invalid_non_prefixed_interval.max + 1).to_s 71% dt_prefixed_min_plus = (Panda::dispatch_table.invalid_prefixes_interval.min + 1).to_s 72% dt_prefixed_max_plus = (Panda::dispatch_table.invalid_prefixes_interval.max + 1).to_s 73% 74% dispatch_table_hash = Hash.new() 75% Panda::instructions.each do |i| 76% combination_flags = "" 77% mnemonic = i.mnemonic.split('.').map { |p| p == '64' ? 'Wide' : p.capitalize }.join 78% value_dispatch = "HANDLE_" + i.handler_name 79% if i.properties.any? { |prop| ['method_id', 'field_id', 'type_id', 'literalarray_id'].include?(prop)} 80% combination_flags += "Prop_" 81% end 82% if (['method_id', 'field_id', 'type_id', 'string_id', 'literalarray_id'] & i.properties).size > 1 83% cache_api = "cache_api" 84% combination_flags += "CacheApi_" 85% else 86% cache_api = "cache.FastAPI()" 87% combination_flags += "CacheFastApi_" 88% end 89% if i.properties.include?('literalarray_id') 90% combination_flags += "LiteralliId_" 91% end 92% if i.properties.include?('method_id') 93% combination_flags += "MethodId_" 94% end 95% if i.properties.include?('field_id') 96% combination_flags += "FieldId_" 97% end 98% if i.properties.include?('type_id') 99% if i.verification.include?('type_id_class') 100% combination_flags += "ClassId_" 101% else 102% combination_flags += "TypeId_" 103% end 104% end 105% if i.properties.include?('string_id') 106% combination_flags += "StringId_" 107% end 108% combination_flags += "GetNext_" 109% flag = dispatch_table_hash.include?(combination_flags) 110% if flag == false 111% dispatch_table_hash[combination_flags] = [] 112% end 113% dispatch_table_hash[combination_flags].push(value_dispatch) 114% end 115% body_gen_parts = Hash.new() 116% body_gen_parts = { 117% "Prop_" => %( 118% LOG_INST(); 119% [[maybe_unused]] auto id = inst.GetId(); 120% ), 121% "CacheApi_" => %(), 122% "CacheFastApi_" => %(), 123% "LiteralliId_" => %({ 124% const auto& pf = *method_->GetPandaFile(); 125% panda_file::LiteralTag tag; 126% panda_file::LiteralDataAccessor::LiteralValue value; 127% if (!Runtime::GetLiteralTagAndValue(pf, pf.GetLiteralArrays()[id.AsIndex()], &tag, &value)) { 128% LOG(DEBUG, VERIFIER) << "JOBFILL: Cannot get literal tag with id=" << std::hex << id << " for offset 0x" << std::hex << inst.GetOffset(); 129% } else { 130% Class const *cachedClass = nullptr; 131% auto resolveAndLink = [&](const char *descr) { 132% ScopedChangeThreadStatus st(ManagedThread::GetCurrent(), ThreadStatus::RUNNING); 133% cachedClass = classLinker_->GetClass(utf::CStringAsMutf8(descr), false, classLinkerCtx_, &errorHandler); 134% }; 135% // descriptors for primitive types get from libpandafile/templates/type 136% switch (tag) { 137% case panda_file::LiteralTag::ARRAY_U1: 138% resolveAndLink("[Z"); 139% break; 140% case panda_file::LiteralTag::ARRAY_U8: 141% resolveAndLink("[H"); 142% break; 143% case panda_file::LiteralTag::ARRAY_I8: 144% resolveAndLink("[B"); 145% break; 146% case panda_file::LiteralTag::ARRAY_I16: 147% resolveAndLink("[S"); 148% break; 149% case panda_file::LiteralTag::ARRAY_U16: 150% resolveAndLink("[C"); 151% break; 152% case panda_file::LiteralTag::ARRAY_U32: 153% resolveAndLink("[U"); 154% break; 155% case panda_file::LiteralTag::ARRAY_I32: 156% resolveAndLink("[I"); 157% break; 158% case panda_file::LiteralTag::ARRAY_U64: 159% resolveAndLink("[Q"); 160% break; 161% case panda_file::LiteralTag::ARRAY_I64: 162% resolveAndLink("[J"); 163% break; 164% case panda_file::LiteralTag::ARRAY_F32: 165% resolveAndLink("[F"); 166% break; 167% case panda_file::LiteralTag::ARRAY_F64: 168% resolveAndLink("[D"); 169% break; 170% case panda_file::LiteralTag::ARRAY_STRING: { 171% ScopedChangeThreadStatus st(ManagedThread::GetCurrent(), ThreadStatus::RUNNING); 172% cachedClass = classLinker_->GetClass(langContext_.GetStringArrayClassDescriptor(), false, classLinkerCtx_, &errorHandler); 173% break; 174% } 175% default: 176% break; 177% } 178% if (cachedClass != nullptr) { 179% auto type = Type {cachedClass}; 180% AddType(inst.GetOffset(), &type); 181% } else { 182% LOG(DEBUG, VERIFIER) << "JOBFILL: Cannot find class for literal with id=" << std::hex << id << " for offset 0x" << std::hex << inst.GetOffset(); 183% } 184% } 185% }), 186% "MethodId_" => %({ 187% auto methodId = method_->GetClass()->ResolveMethodIndex(id.AsIndex()); 188% ScopedChangeThreadStatus st(ManagedThread::GetCurrent(), ThreadStatus::RUNNING); 189% const auto *pf = method_->GetPandaFile(); 190% Method *calledMethod = pf->GetPandaCache()->GetMethodFromCache(methodId); 191% // Special case for array constructors, which are fake methods that other 192% // Panda code treats in a special way 193% bool isArrayConstructorCall = false; 194% if (calledMethod == nullptr) { 195% panda_file::MethodDataAccessor const mda(*pf, methodId); 196% Class * cls = classLinker_->GetClass(*method_, mda.GetClassId()); 197% 198% auto opcode = inst.GetOpcode(); 199% if (UNLIKELY((opcode == BytecodeInstructionSafe::Opcode::INITOBJ_SHORT_V4_V4_ID16 || 200% opcode == BytecodeInstructionSafe::Opcode::INITOBJ_V4_V4_V4_V4_ID16 || 201% opcode == BytecodeInstructionSafe::Opcode::INITOBJ_RANGE_V8_ID16) && 202% cls != nullptr && cls->IsArrayClass())) { 203% isArrayConstructorCall = true; 204% Type tp = Type {cls}; 205% AddType(inst.GetOffset(), &tp); 206% } else { 207% calledMethod = classLinker_->GetMethod(*method_, methodId, &errorHandler); 208% } 209% } 210% if (calledMethod != nullptr) { 211% AddMethod(inst.GetOffset(), calledMethod); 212% auto classType = Type {calledMethod->GetClass()}; 213% AddType(inst.GetOffset(), &classType); 214% } else if (!isArrayConstructorCall) { 215% LOG(DEBUG, VERIFIER) << "JOBFILL: Cannot resolve method with id " << id << " in method " 216% << method_->GetFullName(); 217% } 218% }), 219% "FieldId_" => %({ 220% ScopedChangeThreadStatus st(ManagedThread::GetCurrent(), ThreadStatus::RUNNING); 221% auto fieldIdx = method_->GetClass()->ResolveFieldIndex(id.AsIndex()); 222% auto *cachedField = classLinker_->GetField(*method_, fieldIdx, &errorHandler); 223% if (cachedField != nullptr) { 224% AddField(inst.GetOffset(), cachedField); 225% auto classType = Type {cachedField->GetClass()}; 226% AddType(inst.GetOffset(), &classType); 227% } else { 228% LOG(DEBUG, VERIFIER) << "JOBFILL: Cannot resolve field with id " << id << " in method " 229% << method_->GetFullName(); 230% } 231% }), 232% "TypeId_" => %({ 233% auto classIdx = method_->GetClass()->ResolveClassIndex(id.AsIndex()); 234% ScopedChangeThreadStatus st(ManagedThread::GetCurrent(), ThreadStatus::RUNNING); 235% auto *cachedClass = classLinker_->GetClass(*method_, classIdx, &errorHandler); 236% if (cachedClass != nullptr) { 237% auto classType = Type {cachedClass}; 238% AddType(inst.GetOffset(), &classType); 239% } else { 240% LOG(DEBUG, VERIFIER) << "JOBFILL: Cannot resolve class with id " << id << " in method " 241% << method_->GetFullName(); 242% } 243% }), 244% "StringId_" => %({ 245% ScopedChangeThreadStatus st(ManagedThread::GetCurrent(), ThreadStatus::RUNNING); 246% auto cachedStringClass = classLinker_->GetClass(langContext_.GetStringClassDescriptor(), false, classLinkerCtx_, &errorHandler); 247% if (cachedStringClass != nullptr) { 248% auto cachedStringType = Type {cachedStringClass}; 249% AddType(inst.GetOffset(), &cachedStringType); 250% } else { 251% LOG(DEBUG, VERIFIER) << "JOBFILL: Cannot resolve string class in method " << method_->GetFullName(); 252% } 253% }), 254% "ClassId_" => %({ 255% ScopedChangeThreadStatus st(ManagedThread::GetCurrent(), ThreadStatus::RUNNING); 256% // We are in lda.type bytecode handler. acc type is going to be java.lang.Class 257% auto cachedClassClass = classLinker_->GetClass(langContext_.GetClassClassDescriptor(), false, classLinkerCtx_, &errorHandler); 258% if (cachedClassClass != nullptr) { 259% auto cachedClassType = Type {cachedClassClass}; 260% AddType(inst.GetOffset(), &cachedClassType); 261% } else { 262% LOG(ERROR, VERIFIER) << "JOBFILL: Cannot resolve Class class in method " << method_->GetFullName(); 263% } 264% }), 265% "GetNext_" => %( 266% if (inst.IsLast()) { 267% return true; 268% } 269% 270% auto nextInst = inst.GetNext(); 271% if (!inst.IsPrimaryOpcodeValid()) { 272% LOG(DEBUG, VERIFIER) << "Opcode value is out of range. " 273% << "Current value is: " << static_cast<int>(inst.GetPrimaryOpcode()) 274% << ". Allowed value is in the interval: [0, ) + dt_non_prefixed_min_minus + %(] U " 275% << "[) + dt_non_prefixed_max_plus + %(, ) + dt_prefixed_min_plus + %(] U " 276% << "[) + dt_prefixed_max_plus + %(, 255]"; 277% return false; 278% } 279% if (!nextInst.IsValid()) { 280% LOG(DEBUG, VERIFIER) << "Invalid instruction. " 281% << "Offset of last valid instruction: " << inst.GetOffset() << ". " 282% << "Last valid instrution: " << inst; 283% return false; 284% } 285% inst = nextInst; 286% } 287% goto* dispatchTable[inst.GetPrimaryOpcode()]; 288% ) 289%} 290% 291% full_code_hash = Hash.new("") 292% dispatch_table_hash.each { |key_comb, value_comb| 293% body_gen_parts.each { |key_parts, value_parts| 294% string_to_compare_with = key_comb.to_s 295% flag_compare = string_to_compare_with.include?(key_parts.to_s) 296% if flag_compare == true 297% full_code_hash[key_comb] += value_parts 298% end 299% } 300% } 301% 302% full_code_hash.each { |key_full, code| 303% dispatch_table_hash[key_full].each { |label| 304<%= label%>:; 305% } 306 { 307<%= code %> 308% } 309% 310HANDLE_INVALID: 311 LOG(ERROR, VERIFIER) << "Incorrect opcode"; 312 return false; 313 314% Panda::prefixes.each do |p| 315% dt_sec_opcode_bound = Panda::dispatch_table.secondary_opcode_bound(p) 316% dt_sec_opcode_offset = Panda::dispatch_table.secondary_opcode_offset(p) 317HANDLE_<%= p.handler_name %>: 318 secondaryOpcode = inst.GetSecondaryOpcode(); 319 LOG(DEBUG, VERIFIER) << "Prefix subdispatch: " << "<%= p.name %>, " << static_cast<int32_t>(secondaryOpcode); 320 321 // NOLINTNEXTLINE(readability-magic-numbers) 322 if (secondaryOpcode > <%= dt_sec_opcode_bound %> ) { 323 LOG(ERROR, VERIFIER) << "Incorrect opcode"; 324 return false; 325 } 326 327 // NOLINTNEXTLINE(readability-magic-numbers, cppcoreguidelines-avoid-goto) 328 goto *dispatchTable[<%= dt_sec_opcode_offset %> + secondaryOpcode]; 329 330% end 331 332#if defined(__clang__) 333#pragma clang diagnostic pop 334#elif defined(__GNUC__) 335#pragma GCC diagnostic pop 336#endif 337} 338 339} // namespace ark::verifier 340