4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
6
6
* You may obtain a copy of the License at
7
- *
7
+ *
8
8
* http://www.apache.org/licenses/LICENSE-2.0
9
- *
9
+ *
10
10
* Unless required by applicable law or agreed to in writing, software
11
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -48,8 +48,8 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
48
48
protected DecompInterface decompiler ;
49
49
private boolean useArraysForSwitchTables = false ;
50
50
51
- public DecompilerSwitchAnalysisCmd (DecompileResults decopmileResults ) {
52
- this .decompilerResults = decopmileResults ;
51
+ public DecompilerSwitchAnalysisCmd (DecompileResults decompileResults ) {
52
+ this .decompilerResults = decompileResults ;
53
53
}
54
54
55
55
@ Override
@@ -71,20 +71,18 @@ private void analyzeFunction(TaskMonitor monitor) {
71
71
}
72
72
73
73
try {
74
-
75
74
monitor .checkCancelled ();
76
75
77
76
Function f = decompilerResults .getFunction ();
78
77
HighFunction hfunction = decompilerResults .getHighFunction ();
79
- processBranchIND (f , hfunction , monitor );
80
-
81
- monitor .checkCancelled ();
82
-
78
+
83
79
String errMsg = getStatusMsg ();
84
- if (decompilerResults . getHighFunction () == null ) {
80
+ if (hfunction == null ) {
85
81
String msg = (errMsg != null && errMsg .length () != 0 ) ? (": " + errMsg ) : "" ;
86
82
Msg .debug (this , " Failed to decompile function: " + f .getName () + msg );
87
83
}
84
+
85
+ processBranchIND (f , hfunction , monitor );
88
86
}
89
87
catch (Exception e ) {
90
88
if (!monitor .isCancelled ()) {
@@ -114,34 +112,8 @@ private void processBranchIND(Function f, HighFunction hfunction, TaskMonitor mo
114
112
continue ; // skip switch owned by a different defined function
115
113
}
116
114
117
- AddressSetView containingBody =
118
- containingFunction != null ? containingFunction .getBody () : null ;
119
-
120
- Reference [] referencesFrom = instr .getReferencesFrom ();
121
- Address [] tableDest = table .getCases ();
122
-
123
- boolean foundNotThere = false ;
124
- int tableIndx ;
125
- for (tableIndx = 0 ; tableIndx < tableDest .length ; tableIndx ++) {
126
- monitor .checkCancelled ();
127
- boolean foundit = false ;
128
- if (containingBody != null && !containingBody .contains (tableDest [tableIndx ])) {
129
- // switch case missing from owner function's body
130
- foundNotThere = true ;
131
- break ;
132
- }
133
- for (Reference element : referencesFrom ) {
134
- if (element .getToAddress ().equals (tableDest [tableIndx ])) {
135
- foundit = true ;
136
- break ;
137
- }
138
- }
139
- if (!foundit ) {
140
- foundNotThere = true ;
141
- }
142
- }
143
115
// references already there, ignore this table
144
- if (! foundNotThere ) {
116
+ if (hasAllReferences ( monitor , table , instr , containingFunction ) ) {
145
117
continue ;
146
118
}
147
119
@@ -158,64 +130,158 @@ private void processBranchIND(Function f, HighFunction hfunction, TaskMonitor mo
158
130
labelSwitch (table , monitor );
159
131
160
132
// disassemble the table
161
- // pull out the current context so we can flow anything that needs to flow
162
- ProgramContext programContext = program .getProgramContext ();
163
- Register baseContextRegister = programContext .getBaseContextRegister ();
164
- RegisterValue switchContext = null ;
165
- if (baseContextRegister != null ) {
166
- // Use disassembler context based upon context register value at switch address (i.e., computed jump)
167
- // Only use flowing context bits
168
- switchContext = programContext .getRegisterValue (baseContextRegister , switchAddr );
169
- switchContext = programContext .getFlowValue (switchContext );
133
+ disassembleTable (monitor , table , instr , flowType );
134
+
135
+ // fixup the function body
136
+ fixupFunction (f , monitor , instr );
137
+ }
138
+ }
139
+
140
+ /*
141
+ * Fix the functions body with any newly reached code from the switch recovery
142
+ */
143
+ private void fixupFunction (Function f , TaskMonitor monitor , Instruction instr )
144
+ throws CancelledException {
145
+ Function fixupFunc = f ;
146
+
147
+ // Make sure this case isn't the result of an undefined function,
148
+ // that somehow one of the cases found a real function.
149
+ if (fixupFunc instanceof UndefinedFunction ) {
150
+ Function realFunc =
151
+ program .getFunctionManager ().getFunctionContaining (instr .getMinAddress ());
152
+ if (realFunc != null ) {
153
+ fixupFunc = realFunc ;
170
154
}
155
+ }
156
+ Instruction funcStartInstr =
157
+ program .getListing ().getInstructionAt (fixupFunc .getEntryPoint ());
158
+ CreateFunctionCmd .fixupFunctionBody (program , funcStartInstr , monitor );
159
+ }
171
160
172
- Listing listing = program .getListing ();
173
- Address [] cases = table .getCases ();
174
- AddressSet disSetList = new AddressSet ();
175
- for (Address caseStart : cases ) {
176
- monitor .checkCancelled ();
161
+ /*
162
+ * Disassemble all code reached from the table.
163
+ * Also adds the case flow references to the switching instruction.
164
+ */
165
+ private void disassembleTable (TaskMonitor monitor , JumpTable table ,
166
+ Instruction instr , FlowType flowType ) throws CancelledException {
167
+
168
+ Address switchAddr = table .getSwitchAddress ();
169
+
170
+ // pull out the current context so we can flow anything that needs to flow
171
+ ProgramContext programContext = program .getProgramContext ();
172
+ Register baseContextRegister = programContext .getBaseContextRegister ();
173
+ RegisterValue switchContext = null ;
174
+ if (baseContextRegister != null ) {
175
+ // Use disassembler context based upon context register value at switch address (i.e., computed jump)
176
+ // Only use flowing context bits
177
+ switchContext = programContext .getRegisterValue (baseContextRegister , switchAddr );
178
+ switchContext = programContext .getFlowValue (switchContext );
179
+ }
180
+
181
+ Listing listing = program .getListing ();
182
+ Address [] cases = table .getCases ();
183
+ Integer [] caseValues = table .getLabelValues ();
184
+ AddressSet disSetList = new AddressSet ();
185
+
186
+ for (int caseIndex = 0 ; caseIndex < cases .length ; caseIndex ++) {
187
+ Address caseStart = cases [caseIndex ];
188
+ monitor .checkCancelled ();
189
+
190
+ if (!isDefaultCase (caseValues , caseIndex )) {
191
+ // only non-default cases should be added to the switching instruction
177
192
instr .addMnemonicReference (caseStart , flowType , SourceType .ANALYSIS );
193
+ }
178
194
179
- // if conflict skip case
180
- if (listing .getUndefinedDataAt (caseStart ) == null ) {
181
- continue ;
182
- }
183
- // already done
184
- if (disSetList .contains (caseStart )) {
185
- continue ;
186
- }
187
- try {
188
- setSwitchTargetContext (programContext , caseStart , switchContext );
189
- }
190
- catch (ContextChangeException e ) {
191
- // This can occur when two or more threads are working on the same function
192
- continue ;
193
- }
194
- disSetList .add (caseStart );
195
+ // if conflict skip case
196
+ if (listing .getUndefinedDataAt (caseStart ) == null ) {
197
+ continue ;
198
+ }
199
+ // already done
200
+ if (disSetList .contains (caseStart )) {
201
+ continue ;
195
202
}
203
+ try {
204
+ setSwitchTargetContext (programContext , caseStart , switchContext );
205
+ }
206
+ catch (ContextChangeException e ) {
207
+ // This can occur when two or more threads are working on the same function
208
+ continue ;
209
+ }
210
+ disSetList .add (caseStart );
211
+ }
196
212
197
- // do all cases at one time
198
- if (!disSetList .isEmpty ()) {
199
- DisassembleCommand cmd = new DisassembleCommand (disSetList , null , true );
200
- cmd .applyTo (program );
213
+ // do all cases at one time
214
+ if (!disSetList .isEmpty ()) {
215
+ DisassembleCommand cmd = new DisassembleCommand (disSetList , null , true );
216
+ cmd .applyTo (program );
217
+ }
218
+ }
219
+
220
+ /*
221
+ * Check if this case index is a default case.
222
+ *
223
+ * In general, each case target address should have an associated caseValue.
224
+ * A case is default if it is first case to not have a case value, or has a magic case value.
225
+ * It is possible that there could be more than one case without a value. The code shouldn't
226
+ * blow up if this is the case.
227
+ *
228
+ * TODO: Should this check if the default case already has a reference to it
229
+ * from a conditional jump?
230
+ */
231
+ private boolean isDefaultCase (Integer [] caseValues , int caseIndex ) {
232
+ return (caseIndex == caseValues .length ) ||
233
+ (caseIndex < caseValues .length && caseValues [caseIndex ] == DEFAULT_CASE_VALUE );
234
+ }
235
+
236
+ /*
237
+ * Check if the switching instruction has all switch references already.
238
+ * Extra check for default case target as part of the table, when it shouldn't be.
239
+ */
240
+ public boolean hasAllReferences (TaskMonitor monitor , JumpTable table , Instruction instr ,
241
+ Function containingFunction ) throws CancelledException {
242
+ AddressSetView containingBody =
243
+ containingFunction != null ? containingFunction .getBody () : null ;
244
+
245
+ Reference [] referencesFrom = instr .getReferencesFrom ();
246
+ Address [] tableDest = table .getCases ();
247
+ Integer [] caseValues = table .getLabelValues ();
248
+
249
+ // check that all cases are already a reference on the instruction, except default
250
+ for (int caseIndex = 0 ; caseIndex < tableDest .length ; caseIndex ++) {
251
+ monitor .checkCancelled ();
252
+
253
+ // a case is default if it is first case to not have a value, or has a magic case value
254
+ boolean isDefaultCase = isDefaultCase (caseValues , caseIndex );
255
+
256
+ if (containingBody != null && !containingBody .contains (tableDest [caseIndex ])) {
257
+ // switch case missing from owner function's body
258
+ return false ;
201
259
}
202
260
203
- // fixup the function body
204
- // make sure this case isn't the result of an undefined function, that somehow one of the cases found a real function.
205
- Function fixupFunc = f ;
206
- if (fixupFunc instanceof UndefinedFunction ) {
207
- Function realFunc =
208
- program .getFunctionManager ().getFunctionContaining (instr .getMinAddress ());
209
- if (realFunc != null ) {
210
- fixupFunc = realFunc ;
261
+ boolean foundit = false ;
262
+ for (Reference element : referencesFrom ) {
263
+ if (element .getToAddress ().equals (tableDest [caseIndex ])) {
264
+ foundit = true ;
265
+ break ;
266
+ }
267
+ }
268
+ if (isDefaultCase ) {
269
+ // default case should not be on switching instruction
270
+ if (foundit ) {
271
+ return false ;
211
272
}
212
273
}
213
- Instruction funcStartInstr =
214
- program . getListing (). getInstructionAt ( fixupFunc . getEntryPoint ()) ;
215
- CreateFunctionCmd . fixupFunctionBody ( program , funcStartInstr , monitor );
274
+ else if (! foundit ) {
275
+ return false ;
276
+ }
216
277
}
278
+
279
+ return true ;
217
280
}
218
281
282
+ /*
283
+ * Set the context that should flow to the target so that target instruction will disassemble correctly
284
+ */
219
285
private void setSwitchTargetContext (ProgramContext programContext , Address targetStart , RegisterValue switchContext ) throws ContextChangeException {
220
286
if (switchContext == null ) {
221
287
return ;
@@ -236,6 +302,9 @@ private void setSwitchTargetContext(ProgramContext programContext, Address targe
236
302
program .getProgramContext ().setRegisterValue (targetStart , targetStart , switchContext );
237
303
}
238
304
305
+ /*
306
+ * Label switch table, cases, default with labels in namespace of the switch
307
+ */
239
308
private void labelSwitch (JumpTable table , TaskMonitor monitor ) throws CancelledException {
240
309
AddLabelCmd tableNameLabel =
241
310
new AddLabelCmd (table .getSwitchAddress (), "switchD" , SourceType .ANALYSIS );
@@ -266,27 +335,33 @@ private void labelSwitch(JumpTable table, TaskMonitor monitor) throws CancelledE
266
335
tableNameLabel .applyTo (program );
267
336
268
337
Address [] switchCases = table .getCases ();
269
- Integer [] caseLabels = table .getLabelValues ();
270
- Symbol [] caseSymbols = new Symbol [caseLabels .length ];
338
+ Integer [] caseValues = table .getLabelValues ();
339
+ Symbol [] caseSymbols = new Symbol [caseValues .length ];
271
340
SymbolTable symTable = program .getSymbolTable ();
272
- for (int i = 0 ; i < switchCases .length ; i ++) {
341
+
342
+ for (int caseIndex = 0 ; caseIndex < switchCases .length ; caseIndex ++) {
273
343
monitor .checkCancelled ();
274
- int offset = (i >= caseLabels .length ? i : caseLabels [i ]);
275
- String caseName = "caseD_" + Integer .toHexString (offset );
276
- if (offset == DEFAULT_CASE_VALUE ) { // magic constant to indicate default case
344
+
345
+ // if there are more switchCases than switch values, just use the caseIndex
346
+ int caseValue = (caseIndex < caseValues .length ) ? caseValues [caseIndex ] : caseIndex ;
347
+
348
+ boolean isDefaultCase = isDefaultCase (caseValues , caseIndex );
349
+
350
+ String caseName = "caseD_" + Integer .toHexString (caseValue );
351
+ if (isDefaultCase ) {
277
352
caseName = "default" ;
278
353
}
279
354
AddLabelCmd lcmd =
280
- new AddLabelCmd (switchCases [i ], caseName , space , SourceType .ANALYSIS );
355
+ new AddLabelCmd (switchCases [caseIndex ], caseName , space , SourceType .ANALYSIS );
281
356
282
357
Symbol oldSym = symTable .getPrimarySymbol (lcmd .getLabelAddr ());
283
358
if (oldSym != null && oldSym .getSource () == SourceType .ANALYSIS &&
284
359
oldSym .getName ().startsWith ("Addr" )) {
285
360
// cleanup AddressTableAnalyzer label
286
361
oldSym .delete ();
287
362
}
288
- if (lcmd .applyTo (program ) && i < caseSymbols .length ) {
289
- caseSymbols [i ] = symTable .getSymbol (caseName , switchCases [i ], space );
363
+ if (lcmd .applyTo (program ) && caseIndex < caseSymbols .length ) {
364
+ caseSymbols [caseIndex ] = symTable .getSymbol (caseName , switchCases [caseIndex ], space );
290
365
}
291
366
}
292
367
@@ -338,6 +413,9 @@ private Address[] getPointerTable(JumpTable.LoadTable loadtable, Address[] switc
338
413
return addresses ;
339
414
}
340
415
416
+ /*
417
+ * put labels on the switch table used to compute the target addresses of the switch.
418
+ */
341
419
private void labelLoadTable (JumpTable .LoadTable loadtable , Address [] switchCases ,
342
420
Symbol [] caseSymbols , Namespace space , TaskMonitor monitor ) throws CancelledException {
343
421
0 commit comments