315 lines
12 KiB
C++
315 lines
12 KiB
C++
|
/*
|
||
|
* 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 "resolver.h"
|
||
|
|
||
|
#include "dex/class_accessor-inl.h"
|
||
|
#include "dex/dex_file-inl.h"
|
||
|
#include "dex/primitive.h"
|
||
|
#include "dex/signature-inl.h"
|
||
|
#include "hidden_api.h"
|
||
|
#include "veridex.h"
|
||
|
|
||
|
namespace art {
|
||
|
|
||
|
void VeridexResolver::Run() {
|
||
|
for (ClassAccessor accessor : dex_file_.GetClasses()) {
|
||
|
std::string name(accessor.GetDescriptor());
|
||
|
auto existing = type_map_.find(name);
|
||
|
const uint32_t type_idx = accessor.GetClassIdx().index_;
|
||
|
if (existing != type_map_.end()) {
|
||
|
// Class already exists, cache it and move on.
|
||
|
type_infos_[type_idx] = *existing->second;
|
||
|
continue;
|
||
|
}
|
||
|
type_infos_[type_idx] = VeriClass(Primitive::Type::kPrimNot, 0, &accessor.GetClassDef());
|
||
|
type_map_[name] = &type_infos_[type_idx];
|
||
|
for (const ClassAccessor::Field& field : accessor.GetFields()) {
|
||
|
field_infos_[field.GetIndex()] = field.GetDataPointer();
|
||
|
}
|
||
|
for (const ClassAccessor::Method& method : accessor.GetMethods()) {
|
||
|
method_infos_[method.GetIndex()] = method.GetDataPointer();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool HasSameNameAndSignature(const DexFile& dex_file,
|
||
|
const dex::MethodId& method_id,
|
||
|
const char* method_name,
|
||
|
const char* type) {
|
||
|
return strcmp(method_name, dex_file.GetMethodName(method_id)) == 0 &&
|
||
|
strcmp(type, dex_file.GetMethodSignature(method_id).ToString().c_str()) == 0;
|
||
|
}
|
||
|
|
||
|
static bool HasSameNameAndSignature(const DexFile& dex_file,
|
||
|
const dex::MethodId& method_id,
|
||
|
const char* method_name,
|
||
|
const Signature& signature) {
|
||
|
return strcmp(method_name, dex_file.GetMethodName(method_id)) == 0 &&
|
||
|
dex_file.GetMethodSignature(method_id) == signature;
|
||
|
}
|
||
|
|
||
|
static bool HasSameNameAndType(const DexFile& dex_file,
|
||
|
const dex::FieldId& field_id,
|
||
|
const char* field_name,
|
||
|
const char* field_type) {
|
||
|
return strcmp(field_name, dex_file.GetFieldName(field_id)) == 0 &&
|
||
|
strcmp(field_type, dex_file.GetFieldTypeDescriptor(field_id)) == 0;
|
||
|
}
|
||
|
|
||
|
VeriClass* VeridexResolver::GetVeriClass(dex::TypeIndex index) {
|
||
|
CHECK_LT(index.index_, dex_file_.NumTypeIds());
|
||
|
// Lookup in our local cache.
|
||
|
VeriClass* cls = &type_infos_[index.index_];
|
||
|
if (cls->IsUninitialized()) {
|
||
|
// Class is defined in another dex file. Lookup in the global cache.
|
||
|
std::string name(dex_file_.StringByTypeIdx(index));
|
||
|
auto existing = type_map_.find(name);
|
||
|
if (existing == type_map_.end()) {
|
||
|
// Class hasn't been defined, so check if it's an array class.
|
||
|
size_t last_array = name.find_last_of('[');
|
||
|
if (last_array == std::string::npos) {
|
||
|
// There is no such class.
|
||
|
return nullptr;
|
||
|
} else {
|
||
|
// Class is an array class. Check if its most enclosed component type (which is not
|
||
|
// an array class) has been defined.
|
||
|
std::string klass_name = name.substr(last_array + 1);
|
||
|
existing = type_map_.find(klass_name);
|
||
|
if (existing == type_map_.end()) {
|
||
|
// There is no such class, so there is no such array.
|
||
|
return nullptr;
|
||
|
} else {
|
||
|
// Create the type, and cache it locally and globally.
|
||
|
type_infos_[index.index_] = VeriClass(
|
||
|
existing->second->GetKind(), last_array + 1, existing->second->GetClassDef());
|
||
|
cls = &(type_infos_[index.index_]);
|
||
|
type_map_[name] = cls;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
// Cache the found class.
|
||
|
cls = existing->second;
|
||
|
type_infos_[index.index_] = *cls;
|
||
|
}
|
||
|
}
|
||
|
return cls;
|
||
|
}
|
||
|
|
||
|
VeridexResolver* VeridexResolver::GetResolverOf(const VeriClass& kls) const {
|
||
|
auto resolver_it = dex_resolvers_.lower_bound(reinterpret_cast<uintptr_t>(kls.GetClassDef()));
|
||
|
--resolver_it;
|
||
|
|
||
|
// Check the class def pointer is indeed in the mapped dex file range.
|
||
|
const DexFile& dex_file = resolver_it->second->dex_file_;
|
||
|
CHECK_LT(reinterpret_cast<uintptr_t>(dex_file.Begin()),
|
||
|
reinterpret_cast<uintptr_t>(kls.GetClassDef()));
|
||
|
CHECK_GT(reinterpret_cast<uintptr_t>(dex_file.Begin()) + dex_file.Size(),
|
||
|
reinterpret_cast<uintptr_t>(kls.GetClassDef()));
|
||
|
return resolver_it->second;
|
||
|
}
|
||
|
|
||
|
VeriMethod VeridexResolver::LookupMethodIn(const VeriClass& kls,
|
||
|
const char* method_name,
|
||
|
const Signature& method_signature) {
|
||
|
if (kls.IsPrimitive()) {
|
||
|
// Primitive classes don't have methods.
|
||
|
return nullptr;
|
||
|
}
|
||
|
if (kls.IsArray()) {
|
||
|
// Array classes don't have methods, but inherit the ones in j.l.Object.
|
||
|
return LookupMethodIn(*VeriClass::object_, method_name, method_signature);
|
||
|
}
|
||
|
// Get the resolver where `kls` is from.
|
||
|
VeridexResolver* resolver = GetResolverOf(kls);
|
||
|
|
||
|
// Look at methods declared in `kls`.
|
||
|
const DexFile& other_dex_file = resolver->dex_file_;
|
||
|
ClassAccessor other_dex_accessor(other_dex_file, *kls.GetClassDef());
|
||
|
for (const ClassAccessor::Method& method : other_dex_accessor.GetMethods()) {
|
||
|
const dex::MethodId& other_method_id = other_dex_file.GetMethodId(method.GetIndex());
|
||
|
if (HasSameNameAndSignature(other_dex_file,
|
||
|
other_method_id,
|
||
|
method_name,
|
||
|
method_signature)) {
|
||
|
return method.GetDataPointer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Look at methods in `kls`'s super class hierarchy.
|
||
|
if (kls.GetClassDef()->superclass_idx_.IsValid()) {
|
||
|
VeriClass* super = resolver->GetVeriClass(kls.GetClassDef()->superclass_idx_);
|
||
|
if (super != nullptr) {
|
||
|
VeriMethod super_method = resolver->LookupMethodIn(*super, method_name, method_signature);
|
||
|
if (super_method != nullptr) {
|
||
|
return super_method;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Look at methods in `kls`'s interface hierarchy.
|
||
|
const dex::TypeList* interfaces = other_dex_file.GetInterfacesList(*kls.GetClassDef());
|
||
|
if (interfaces != nullptr) {
|
||
|
for (size_t i = 0; i < interfaces->Size(); i++) {
|
||
|
dex::TypeIndex idx = interfaces->GetTypeItem(i).type_idx_;
|
||
|
VeriClass* itf = resolver->GetVeriClass(idx);
|
||
|
if (itf != nullptr) {
|
||
|
VeriMethod itf_method = resolver->LookupMethodIn(*itf, method_name, method_signature);
|
||
|
if (itf_method != nullptr) {
|
||
|
return itf_method;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
VeriField VeridexResolver::LookupFieldIn(const VeriClass& kls,
|
||
|
const char* field_name,
|
||
|
const char* field_type) {
|
||
|
if (kls.IsPrimitive()) {
|
||
|
// Primitive classes don't have fields.
|
||
|
return nullptr;
|
||
|
}
|
||
|
if (kls.IsArray()) {
|
||
|
// Array classes don't have fields.
|
||
|
return nullptr;
|
||
|
}
|
||
|
// Get the resolver where `kls` is from.
|
||
|
VeridexResolver* resolver = GetResolverOf(kls);
|
||
|
|
||
|
// Look at fields declared in `kls`.
|
||
|
const DexFile& other_dex_file = resolver->dex_file_;
|
||
|
ClassAccessor other_dex_accessor(other_dex_file, *kls.GetClassDef());
|
||
|
for (const ClassAccessor::Field& field : other_dex_accessor.GetFields()) {
|
||
|
const dex::FieldId& other_field_id = other_dex_file.GetFieldId(field.GetIndex());
|
||
|
if (HasSameNameAndType(other_dex_file,
|
||
|
other_field_id,
|
||
|
field_name,
|
||
|
field_type)) {
|
||
|
return field.GetDataPointer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Look at fields in `kls`'s interface hierarchy.
|
||
|
const dex::TypeList* interfaces = other_dex_file.GetInterfacesList(*kls.GetClassDef());
|
||
|
if (interfaces != nullptr) {
|
||
|
for (size_t i = 0; i < interfaces->Size(); i++) {
|
||
|
dex::TypeIndex idx = interfaces->GetTypeItem(i).type_idx_;
|
||
|
VeriClass* itf = resolver->GetVeriClass(idx);
|
||
|
if (itf != nullptr) {
|
||
|
VeriField itf_field = resolver->LookupFieldIn(*itf, field_name, field_type);
|
||
|
if (itf_field != nullptr) {
|
||
|
return itf_field;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Look at fields in `kls`'s super class hierarchy.
|
||
|
if (kls.GetClassDef()->superclass_idx_.IsValid()) {
|
||
|
VeriClass* super = resolver->GetVeriClass(kls.GetClassDef()->superclass_idx_);
|
||
|
if (super != nullptr) {
|
||
|
VeriField super_field = resolver->LookupFieldIn(*super, field_name, field_type);
|
||
|
if (super_field != nullptr) {
|
||
|
return super_field;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
VeriMethod VeridexResolver::LookupDeclaredMethodIn(const VeriClass& kls,
|
||
|
const char* method_name,
|
||
|
const char* type) const {
|
||
|
if (kls.IsPrimitive()) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
if (kls.IsArray()) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
VeridexResolver* resolver = GetResolverOf(kls);
|
||
|
const DexFile& other_dex_file = resolver->dex_file_;
|
||
|
ClassAccessor other_dex_accessor(other_dex_file, *kls.GetClassDef());
|
||
|
for (const ClassAccessor::Method& method : other_dex_accessor.GetMethods()) {
|
||
|
if (HasSameNameAndSignature(other_dex_file,
|
||
|
other_dex_file.GetMethodId(method.GetIndex()),
|
||
|
method_name,
|
||
|
type)) {
|
||
|
return method.GetDataPointer();
|
||
|
}
|
||
|
}
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
VeriMethod VeridexResolver::GetMethod(uint32_t method_index) {
|
||
|
VeriMethod method_info = method_infos_[method_index];
|
||
|
if (method_info == nullptr) {
|
||
|
// Method is defined in another dex file.
|
||
|
const dex::MethodId& method_id = dex_file_.GetMethodId(method_index);
|
||
|
VeriClass* kls = GetVeriClass(method_id.class_idx_);
|
||
|
if (kls == nullptr) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
// Class found, now lookup the method in it.
|
||
|
method_info = LookupMethodIn(*kls,
|
||
|
dex_file_.GetMethodName(method_id),
|
||
|
dex_file_.GetMethodSignature(method_id));
|
||
|
method_infos_[method_index] = method_info;
|
||
|
}
|
||
|
return method_info;
|
||
|
}
|
||
|
|
||
|
VeriField VeridexResolver::GetField(uint32_t field_index) {
|
||
|
VeriField field_info = field_infos_[field_index];
|
||
|
if (field_info == nullptr) {
|
||
|
// Field is defined in another dex file.
|
||
|
const dex::FieldId& field_id = dex_file_.GetFieldId(field_index);
|
||
|
VeriClass* kls = GetVeriClass(field_id.class_idx_);
|
||
|
if (kls == nullptr) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
// Class found, now lookup the field in it.
|
||
|
field_info = LookupFieldIn(*kls,
|
||
|
dex_file_.GetFieldName(field_id),
|
||
|
dex_file_.GetFieldTypeDescriptor(field_id));
|
||
|
field_infos_[field_index] = field_info;
|
||
|
}
|
||
|
return field_info;
|
||
|
}
|
||
|
|
||
|
void VeridexResolver::ResolveAll() {
|
||
|
for (uint32_t i = 0; i < dex_file_.NumTypeIds(); ++i) {
|
||
|
if (GetVeriClass(dex::TypeIndex(i)) == nullptr) {
|
||
|
LOG(WARNING) << "Unresolved " << dex_file_.PrettyType(dex::TypeIndex(i));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (uint32_t i = 0; i < dex_file_.NumMethodIds(); ++i) {
|
||
|
if (GetMethod(i) == nullptr) {
|
||
|
LOG(WARNING) << "Unresolved: " << dex_file_.PrettyMethod(i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (uint32_t i = 0; i < dex_file_.NumFieldIds(); ++i) {
|
||
|
if (GetField(i) == nullptr) {
|
||
|
LOG(WARNING) << "Unresolved: " << dex_file_.PrettyField(i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace art
|