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 : }
63 :
64 m : Analyzer::AnalysisResult StackFrameAnalyzer::Analyze(
65 m : const minidump::Minidump& minidump,
66 m : const ProcessAnalysis& process_analysis) {
67 m : DCHECK(process_analysis.process_state() != nullptr);
68 :
69 m : ProcessState* process_state = process_analysis.process_state();
70 m : DCHECK(process_state != nullptr);
71 :
72 : // Ensure the stack frame layer has already been populated.
73 m : StackFrameLayerPtr frame_layer;
74 m : if (!process_state->FindLayer(&frame_layer)) {
75 m : LOG(ERROR) << "StackFrameAnalyzer: no stack frame layer.";
76 m : return ANALYSIS_ERROR;
77 m : }
78 :
79 : // Process each stack frame.
80 m : for (StackFrameRecordPtr frame_record : *frame_layer) {
81 : // TODO(manzagop): figure out the proper return value and handling for
82 : // AnalyzeFrame. We won't always be able to analyze frame (eg no symbols)
83 : // and that's acceptable.
84 m : AnalyzeFrame(frame_record, process_analysis);
85 m : }
86 :
87 m : return ANALYSIS_COMPLETE;
88 m : }
89 :
90 m : bool StackFrameAnalyzer::AnalyzeFrame(StackFrameRecordPtr frame_record,
91 m : const ProcessAnalysis& process_analysis) {
92 m : DCHECK(frame_record.get() != nullptr);
93 m : DCHECK(process_analysis.process_state() != nullptr);
94 :
95 m : const StackFrame& frame_proto = frame_record->data();
96 m : Address instruction_pointer =
97 m : static_cast<Address>(frame_proto.register_info().eip());
98 :
99 : // Retrieve symbol information.
100 m : if (!SetSymbolInformation(instruction_pointer, process_analysis)) {
101 m : LOG(INFO) << "Unable to get symbol information for frame. Skipping.";
102 m : return true; // Not an error.
103 m : }
104 m : ModuleLayerAccessor accessor(process_analysis.process_state());
105 m : ModuleId module_id = accessor.GetModuleId(instruction_pointer);
106 m : if (module_id == kNoModuleId) {
107 m : LOG(INFO) << "No module corresponding to instruction pointer.";
108 m : return false;
109 m : }
110 :
111 : // Get the innermost scope, be it a block or the function itself.
112 : // TODO(manzagop): Identical code folding means there may be more than one
113 : // symbol for a given address. Look into this.
114 m : base::win::ScopedComPtr<IDiaSymbol> scope;
115 m : if (!GetInnerMostScopeForVA(dia_session_.get(), instruction_pointer, &scope))
116 m : return false;
117 :
118 : // Walk up the scopes, processing scope's data.
119 m : StackFrameDataAnalyzer data_analyzer(frame_record, typename_index_, module_id,
120 m : process_analysis.process_state());
121 m : while (true) {
122 : // Process each SymTagData child in the block / function.
123 : // TODO(manzagop): the data visitor will stop visiting at the first error.
124 : // Figure out how to surface issues without preventing processing (eg
125 : // with a callback).
126 m : pe::ChildVisitor data_visitor(scope.get(), SymTagData);
127 m : if (!data_visitor.VisitChildren(
128 m : base::Bind(&StackFrameDataAnalyzer::Analyze,
129 m : base::Unretained(&data_analyzer)))) {
130 m : LOG(ERROR) << "Error while analyzing scope. Continuing to next scope.";
131 m : return false;
132 m : }
133 :
134 : // Stop processing when function has been processed.
135 m : enum SymTagEnum sym_tag_scope = SymTagNull;
136 m : if (!pe::GetSymTag(scope.get(), &sym_tag_scope))
137 m : return false;
138 m : if (sym_tag_scope == SymTagFunction)
139 m : break;
140 :
141 : // Move up to lexical parent.
142 m : base::win::ScopedComPtr<IDiaSymbol> lexical_parent;
143 m : if (!pe::GetSymLexicalParent(scope.get(), &lexical_parent))
144 m : return false; // We should be able to get to a function.
145 m : scope = lexical_parent;
146 m : }
147 :
148 m : return true;
149 m : }
150 :
151 m : bool StackFrameAnalyzer::SetSymbolInformation(
152 m : Address instruction_pointer,
153 m : const ProcessAnalysis& process_analysis) {
154 m : DCHECK(process_analysis.symbol_provider().get() != nullptr);
155 m : DCHECK(process_analysis.dia_symbol_provider().get() != nullptr);
156 :
157 m : dia_session_.Release();
158 m : typename_index_ = nullptr;
159 :
160 : // Get the module's signature.
161 m : ModuleLayerAccessor accessor(process_analysis.process_state());
162 m : pe::PEFile::Signature signature;
163 m : if (!accessor.GetModuleSignature(instruction_pointer, &signature))
164 m : return false;
165 :
166 : // Get the typename index for the module.
167 m : if (!process_analysis.symbol_provider()->FindOrCreateTypeNameIndex(
168 m : signature, &typename_index_)) {
169 m : return false;
170 m : }
171 :
172 : // Get dia session for the module and set its address.
173 m : base::win::ScopedComPtr<IDiaSession> session_tmp;
174 m : if (!process_analysis.dia_symbol_provider()->FindOrCreateDiaSession(
175 m : 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
|