1 : // Copyright 2015 Google Inc. All Rights Reserved.
2 : //
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 : #include "syzygy/refinery/analyzers/stack_frame_analyzer_impl.h"
16 :
17 : #include <vector>
18 :
19 : #include "base/strings/stringprintf.h"
20 : #include "base/strings/utf_string_conversions.h"
21 : #include "syzygy/pe/dia_util.h"
22 : #include "syzygy/refinery/core/address.h"
23 : #include "syzygy/refinery/process_state/refinery.pb.h"
24 : #include "syzygy/refinery/types/type.h"
25 : #include "syzygy/refinery/types/type_namer.h"
26 : #include "third_party/cci/files/cvinfo.h"
27 :
28 m : namespace refinery {
29 :
30 m : namespace {
31 :
32 : // We observe that CV_ALLREG_VFRAME register-relative locations actually refer
33 : // to the parent frame's value.
34 m : bool GetRegRelLocationRegisterValue(StackFrameRecordPtr frame_record,
35 m : uint32_t register_id,
36 m : uint32_t* register_value) {
37 m : DCHECK(frame_record); DCHECK(register_value);
38 :
39 m : const RegisterInformation& context = frame_record->data().register_info();
40 : // Note: requests for CV_ALLREG_VFRAME are served with parent_allreg_vframe.
41 m : if (register_id == CV_ALLREG_VFRAME && context.has_parent_allreg_vframe()) {
42 m : *register_value = context.parent_allreg_vframe();
43 m : return true;
44 m : }
45 :
46 m : return false;
47 m : }
48 :
49 m : bool GetTypeName(IDiaSymbol* data, base::string16* type_name) {
50 m : DCHECK(data); DCHECK(type_name);
51 :
52 m : base::win::ScopedComPtr<IDiaSymbol> type;
53 m : if (!pe::GetSymType(data, &type))
54 m : return false;
55 :
56 m : return TypeNamer::GetTypeName(type.get(), type_name);
57 m : }
58 :
59 m : bool IsLocType(IDiaSymbol* data, LocationType type) {
60 m : DCHECK(data);
61 :
62 m : LocationType location_type = LocIsNull;
63 m : if (!pe::GetLocationType(data, &location_type))
64 m : return false;
65 :
66 m : return type == location_type;
67 m : }
68 :
69 m : bool GetDataType(TypeNameIndex* typename_index,
70 m : IDiaSymbol* data,
71 m : TypePtr* type) {
72 m : DCHECK(typename_index); DCHECK(data); DCHECK(type);
73 m : DCHECK(pe::IsSymTag(data, SymTagData));
74 m : *type = nullptr;
75 :
76 : // TODO(manzagop): stop relying on type name for retrieving the type once DIA
77 : // is no longer used and we have a stable id.
78 m : base::string16 type_name;
79 m : if (!GetTypeName(data, &type_name))
80 m : return false;
81 :
82 : // Retrieve symbol information.
83 m : std::vector<TypePtr> matching_types;
84 m : typename_index->GetTypes(type_name, &matching_types);
85 m : if (matching_types.empty()) {
86 m : VLOG(1) << "Type " << type_name << " was not found. Skipping.";
87 m : return true;
88 m : } else if (matching_types.size() > 1) {
89 : // We sometimes observe types that have the same name. See pdb_crawler for
90 : // details. All observed instances were for equivalent types. We therefore
91 : // return the first result.
92 : // TODO(manzagop): Deal with this, either by deduplication or minimally by
93 : // ensuring equality of the types. Also note that dia to TypeRepository
94 : // symbol matching is done using undecorated names; there may be some
95 : // legitimate name collisions.
96 m : VLOG(1) << "Multiple type matches for " << type_name;
97 m : }
98 :
99 m : *type = matching_types[0];
100 m : return true;
101 m : }
102 :
103 m : } // namespace
104 :
105 m : StackFrameDataAnalyzer::StackFrameDataAnalyzer(
106 m : StackFrameRecordPtr frame_record,
107 m : scoped_refptr<TypeNameIndex> typename_index,
108 m : ModuleId module_id,
109 m : ProcessState* process_state)
110 m : : frame_record_(frame_record),
111 m : typename_index_(typename_index),
112 m : module_id_(module_id),
113 m : process_state_(process_state) {
114 m : DCHECK(frame_record.get());
115 m : DCHECK(typename_index.get());
116 m : DCHECK(process_state);
117 m : }
118 :
119 m : bool StackFrameDataAnalyzer::Analyze(IDiaSymbol* data) {
120 m : DCHECK(data);
121 m : DCHECK(pe::IsSymTag(data, SymTagData));
122 :
123 : // Restrict to local variables, parameters and this pointers.
124 : // TODO(manzagop): process other kinds, eg DataIsMember?
125 m : DataKind data_kind = DataIsUnknown;
126 m : if (!pe::GetDataKind(data, &data_kind))
127 m : return false;
128 m : switch (data_kind) {
129 m : case DataIsLocal:
130 m : case DataIsParam:
131 m : case DataIsObjectPtr:
132 m : break;
133 m : case DataIsUnknown:
134 m : return false; // Should not happen.
135 m : case DataIsMember:
136 m : case DataIsStaticLocal:
137 m : case DataIsFileStatic:
138 m : case DataIsGlobal:
139 m : case DataIsStaticMember:
140 m : case DataIsConstant:
141 : // TODO(manzagop): look into these.
142 m : return true; // Ignore these for now.
143 m : }
144 :
145 : // Get the data's information: name, type name and address range.
146 m : base::string16 data_name;
147 m : if (!pe::GetSymName(data, &data_name))
148 m : return false;
149 :
150 : // Get the data's type from the type_repository.
151 m : TypePtr type;
152 m : if (!GetDataType(typename_index_.get(), data, &type))
153 m : return false;
154 m : if (type.get() == nullptr)
155 m : return true; // The type was not found.
156 :
157 m : AddressRange range;
158 m : if (!GetAddressRange(data, type, &range))
159 m : return false;
160 : // Note: successfully returning an invalid address range means the location
161 : // type is not yet supported.
162 : // TODO(manzagop): fully support location types and remove this.
163 m : if (!range.IsValid())
164 m : return true;
165 :
166 : // Add the typed block to the process state's typed block layer.
167 : // TODO(manzagop): handle CV qualifiers.
168 : // TODO(manzagop): avoid duplicating types we already know about.
169 m : return AddTypedBlockRecord(range, data_name, module_id_, type->type_id(),
170 m : process_state_);
171 m : }
172 :
173 m : bool StackFrameDataAnalyzer::GetAddressRange(IDiaSymbol* data,
174 m : TypePtr type,
175 m : AddressRange* range) {
176 m : DCHECK(data); DCHECK(range);
177 :
178 m : LocationType location_type = LocIsNull;
179 m : if (!pe::GetLocationType(data, &location_type))
180 m : return false;
181 :
182 m : switch (location_type) {
183 m : case LocIsRegRel:
184 m : return GetAddressRangeRegRel(data, type, range);
185 m : case LocIsStatic:
186 m : case LocIsTLS:
187 m : case LocIsThisRel:
188 m : case LocIsEnregistered:
189 m : case LocIsBitField:
190 m : case LocIsSlot:
191 m : case LocIsIlRel:
192 m : case LocInMetaData:
193 m : case LocIsConstant:
194 m : VLOG(1) << "Unhandled location type: " << location_type;
195 : // TODO(manzagop): implement.
196 m : AddressRange address_range;
197 m : *range = address_range;
198 m : return true;
199 m : }
200 :
201 m : return false;
202 m : }
203 :
204 m : bool StackFrameDataAnalyzer::GetAddressRangeRegRel(IDiaSymbol* data,
205 m : TypePtr type,
206 m : AddressRange* range) {
207 m : DCHECK(data); DCHECK(range);
208 m : DCHECK(IsLocType(data, LocIsRegRel));
209 :
210 m : *range = AddressRange();
211 :
212 : // Register-relative: determine location.
213 m : uint32_t register_id = 0U;
214 m : if (!pe::GetRegisterId(data, ®ister_id))
215 m : return false;
216 m : ptrdiff_t register_offset = 0;
217 m : if (!pe::GetSymOffset(data, ®ister_offset))
218 m : return false;
219 :
220 : // Figure out the data's range.
221 m : uint32_t register_value = 0U;
222 m : if (!GetRegRelLocationRegisterValue(frame_record_, register_id,
223 m : ®ister_value)) {
224 m : LOG(ERROR) << base::StringPrintf(
225 m : "Failed to retrieve register value (%d). Skipping data.", register_id);
226 m : return true;
227 m : }
228 :
229 : // TODO(manzagop): check validity of operation.
230 m : Address data_va = register_value + register_offset;
231 m : AddressRange address_range(data_va, type->size());
232 m : if (!address_range.IsValid())
233 m : return false;
234 :
235 m : *range = address_range;
236 m : return true;
237 m : }
238 :
239 m : } // namespace refinery
|