• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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