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.h"
16 :
17 : #include <vector>
18 :
19 : #include "base/bind.h"
20 : #include "base/strings/stringprintf.h"
21 : #include "base/win/scoped_comptr.h"
22 : #include "syzygy/common/com_utils.h"
23 : #include "syzygy/pe/dia_util.h"
24 : #include "syzygy/refinery/analyzers/stack_frame_analyzer_impl.h"
25 : #include "syzygy/refinery/process_state/layer_data.h"
26 : #include "syzygy/refinery/process_state/process_state.h"
27 : #include "syzygy/refinery/process_state/process_state_util.h"
28 : #include "syzygy/refinery/types/type_repository.h"
29 :
30 m : namespace refinery {
31 :
32 m : namespace {
33 :
34 m : bool GetInnerMostScopeForVA(IDiaSession* session,
35 m : Address va,
36 m : base::win::ScopedComPtr<IDiaSymbol>* scope) {
37 m : DCHECK(session);
38 m : DCHECK(scope);
39 :
40 : // Attempt to get a block.
41 m : HRESULT hr = session->findSymbolByVA(va, SymTagBlock, scope->Receive());
42 m : if (hr != S_OK) {
43 : // No SymTagBlock. Attempt to get a SymTagFunction.
44 m : hr = session->findSymbolByVA(va, SymTagFunction, scope->Receive());
45 m : if (hr != S_OK) {
46 m : LOG(ERROR) << base::StringPrintf(
47 m : "Failed to find block or function for VA (%08llx): ",
48 m : va) << common::LogHr(hr);
49 m : return false;
50 m : }
51 m : }
52 :
53 m : return true;
54 m : }
55 :
56 m : } // namespace
57 :
58 : // static
59 m : const char StackFrameAnalyzer::kStackFrameAnalyzerName[] = "StackFrameAnalyzer";
60 :
61 m : StackFrameAnalyzer::StackFrameAnalyzer(
62 m : scoped_refptr<DiaSymbolProvider> dia_symbol_provider,
63 m : scoped_refptr<SymbolProvider> symbol_provider)
64 m : : dia_symbol_provider_(dia_symbol_provider),
65 m : symbol_provider_(symbol_provider) {
66 m : DCHECK(dia_symbol_provider.get() != nullptr);
67 m : DCHECK(symbol_provider.get() != nullptr);
68 m : }
69 :
70 m : Analyzer::AnalysisResult StackFrameAnalyzer::Analyze(
71 m : const minidump::Minidump& minidump,
72 m : ProcessState* process_state) {
73 m : DCHECK(process_state != nullptr);
74 :
75 : // Ensure the stack frame layer has already been populated.
76 m : StackFrameLayerPtr frame_layer;
77 m : if (!process_state->FindLayer(&frame_layer)) {
78 m : LOG(ERROR) << "StackFrameAnalyzer: no stack frame layer.";
79 m : return ANALYSIS_ERROR;
80 m : }
81 :
82 : // Process each stack frame.
83 m : for (StackFrameRecordPtr frame_record : *frame_layer) {
84 : // TODO(manzagop): figure out the proper return value and handling for
85 : // AnalyzeFrame. We won't always be able to analyze frame (eg no symbols)
86 : // and that's acceptable.
87 m : AnalyzeFrame(frame_record, process_state);
88 m : }
89 :
90 m : return ANALYSIS_COMPLETE;
91 m : }
92 :
93 m : bool StackFrameAnalyzer::AnalyzeFrame(StackFrameRecordPtr frame_record,
94 m : ProcessState* process_state) {
95 m : DCHECK(frame_record.get() != nullptr);
96 m : DCHECK(process_state != nullptr);
97 :
98 m : const StackFrame& frame_proto = frame_record->data();
99 m : Address instruction_pointer =
100 m : static_cast<Address>(frame_proto.register_info().eip());
101 :
102 : // Retrieve symbol information.
103 m : if (!SetSymbolInformation(instruction_pointer, process_state)) {
104 m : LOG(INFO) << "Unable to get symbol information for frame. Skipping.";
105 m : return true; // Not an error.
106 m : }
107 m : ModuleLayerAccessor accessor(process_state);
108 m : ModuleId module_id = accessor.GetModuleId(instruction_pointer);
109 m : if (module_id == kNoModuleId) {
110 m : LOG(INFO) << "No module corresponding to instruction pointer.";
111 m : return false;
112 m : }
113 :
114 : // Get the innermost scope, be it a block or the function itself.
115 : // TODO(manzagop): Identical code folding means there may be more than one
116 : // symbol for a given address. Look into this.
117 m : base::win::ScopedComPtr<IDiaSymbol> scope;
118 m : if (!GetInnerMostScopeForVA(dia_session_.get(), instruction_pointer, &scope))
119 m : return false;
120 :
121 : // Walk up the scopes, processing scope's data.
122 m : StackFrameDataAnalyzer data_analyzer(frame_record, typename_index_, module_id,
123 m : process_state);
124 m : while (true) {
125 : // Process each SymTagData child in the block / function.
126 : // TODO(manzagop): the data visitor will stop visiting at the first error.
127 : // Figure out how to surface issues without preventing processing (eg
128 : // with a callback).
129 m : pe::ChildVisitor data_visitor(scope.get(), SymTagData);
130 m : if (!data_visitor.VisitChildren(
131 m : base::Bind(&StackFrameDataAnalyzer::Analyze,
132 m : base::Unretained(&data_analyzer)))) {
133 m : LOG(ERROR) << "Error while analyzing scope. Continuing to next scope.";
134 m : return false;
135 m : }
136 :
137 : // Stop processing when function has been processed.
138 m : enum SymTagEnum sym_tag_scope = SymTagNull;
139 m : if (!pe::GetSymTag(scope.get(), &sym_tag_scope))
140 m : return false;
141 m : if (sym_tag_scope == SymTagFunction)
142 m : break;
143 :
144 : // Move up to lexical parent.
145 m : base::win::ScopedComPtr<IDiaSymbol> lexical_parent;
146 m : if (!pe::GetSymLexicalParent(scope.get(), &lexical_parent))
147 m : return false; // We should be able to get to a function.
148 m : scope = lexical_parent;
149 m : }
150 :
151 m : return true;
152 m : }
153 :
154 m : bool StackFrameAnalyzer::SetSymbolInformation(Address instruction_pointer,
155 m : ProcessState* process_state) {
156 m : DCHECK(process_state != nullptr);
157 :
158 m : dia_session_.Release();
159 m : typename_index_ = nullptr;
160 :
161 : // Get the module's signature.
162 m : ModuleLayerAccessor accessor(process_state);
163 m : pe::PEFile::Signature signature;
164 m : if (!accessor.GetModuleSignature(instruction_pointer, &signature))
165 m : return false;
166 :
167 : // Get the typename index for the module.
168 m : if (!symbol_provider_->FindOrCreateTypeNameIndex(signature,
169 m : &typename_index_)) {
170 m : return false;
171 m : }
172 :
173 : // Get dia session for the module and set its address.
174 m : base::win::ScopedComPtr<IDiaSession> session_tmp;
175 m : if (!dia_symbol_provider_->FindOrCreateDiaSession(signature, &session_tmp))
176 m : return false;
177 m : HRESULT hr = session_tmp->put_loadAddress(signature.base_address.value());
178 m : if (FAILED(hr)) {
179 m : LOG(ERROR) << "Unable to set session's load address: " << common::LogHr(hr);
180 m : return false;
181 m : }
182 m : dia_session_ = session_tmp;
183 :
184 m : return true;
185 m : }
186 :
187 m : } // namespace refinery
|