44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
66 * You may obtain a copy of the License at
7- *
7+ *
88 * http://www.apache.org/licenses/LICENSE-2.0
9- *
9+ *
1010 * Unless required by applicable law or agreed to in writing, software
1111 * distributed under the License is distributed on an "AS IS" BASIS,
1212 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -48,8 +48,8 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
4848 protected DecompInterface decompiler ;
4949 private boolean useArraysForSwitchTables = false ;
5050
51- public DecompilerSwitchAnalysisCmd (DecompileResults decopmileResults ) {
52- this .decompilerResults = decopmileResults ;
51+ public DecompilerSwitchAnalysisCmd (DecompileResults decompileResults ) {
52+ this .decompilerResults = decompileResults ;
5353 }
5454
5555 @ Override
@@ -71,20 +71,18 @@ private void analyzeFunction(TaskMonitor monitor) {
7171 }
7272
7373 try {
74-
7574 monitor .checkCancelled ();
7675
7776 Function f = decompilerResults .getFunction ();
7877 HighFunction hfunction = decompilerResults .getHighFunction ();
79- processBranchIND (f , hfunction , monitor );
80-
81- monitor .checkCancelled ();
82-
78+
8379 String errMsg = getStatusMsg ();
84- if (decompilerResults . getHighFunction () == null ) {
80+ if (hfunction == null ) {
8581 String msg = (errMsg != null && errMsg .length () != 0 ) ? (": " + errMsg ) : "" ;
8682 Msg .debug (this , " Failed to decompile function: " + f .getName () + msg );
8783 }
84+
85+ processBranchIND (f , hfunction , monitor );
8886 }
8987 catch (Exception e ) {
9088 if (!monitor .isCancelled ()) {
@@ -114,34 +112,8 @@ private void processBranchIND(Function f, HighFunction hfunction, TaskMonitor mo
114112 continue ; // skip switch owned by a different defined function
115113 }
116114
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- }
143115 // references already there, ignore this table
144- if (! foundNotThere ) {
116+ if (hasAllReferences ( monitor , table , instr , containingFunction ) ) {
145117 continue ;
146118 }
147119
@@ -158,64 +130,158 @@ private void processBranchIND(Function f, HighFunction hfunction, TaskMonitor mo
158130 labelSwitch (table , monitor );
159131
160132 // 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 ;
170154 }
155+ }
156+ Instruction funcStartInstr =
157+ program .getListing ().getInstructionAt (fixupFunc .getEntryPoint ());
158+ CreateFunctionCmd .fixupFunctionBody (program , funcStartInstr , monitor );
159+ }
171160
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
177192 instr .addMnemonicReference (caseStart , flowType , SourceType .ANALYSIS );
193+ }
178194
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 ;
195202 }
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+ }
196212
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 ;
201259 }
202260
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 ;
211272 }
212273 }
213- Instruction funcStartInstr =
214- program . getListing (). getInstructionAt ( fixupFunc . getEntryPoint ()) ;
215- CreateFunctionCmd . fixupFunctionBody ( program , funcStartInstr , monitor );
274+ else if (! foundit ) {
275+ return false ;
276+ }
216277 }
278+
279+ return true ;
217280 }
218281
282+ /*
283+ * Set the context that should flow to the target so that target instruction will disassemble correctly
284+ */
219285 private void setSwitchTargetContext (ProgramContext programContext , Address targetStart , RegisterValue switchContext ) throws ContextChangeException {
220286 if (switchContext == null ) {
221287 return ;
@@ -236,6 +302,9 @@ private void setSwitchTargetContext(ProgramContext programContext, Address targe
236302 program .getProgramContext ().setRegisterValue (targetStart , targetStart , switchContext );
237303 }
238304
305+ /*
306+ * Label switch table, cases, default with labels in namespace of the switch
307+ */
239308 private void labelSwitch (JumpTable table , TaskMonitor monitor ) throws CancelledException {
240309 AddLabelCmd tableNameLabel =
241310 new AddLabelCmd (table .getSwitchAddress (), "switchD" , SourceType .ANALYSIS );
@@ -266,27 +335,33 @@ private void labelSwitch(JumpTable table, TaskMonitor monitor) throws CancelledE
266335 tableNameLabel .applyTo (program );
267336
268337 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 ];
271340 SymbolTable symTable = program .getSymbolTable ();
272- for (int i = 0 ; i < switchCases .length ; i ++) {
341+
342+ for (int caseIndex = 0 ; caseIndex < switchCases .length ; caseIndex ++) {
273343 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 ) {
277352 caseName = "default" ;
278353 }
279354 AddLabelCmd lcmd =
280- new AddLabelCmd (switchCases [i ], caseName , space , SourceType .ANALYSIS );
355+ new AddLabelCmd (switchCases [caseIndex ], caseName , space , SourceType .ANALYSIS );
281356
282357 Symbol oldSym = symTable .getPrimarySymbol (lcmd .getLabelAddr ());
283358 if (oldSym != null && oldSym .getSource () == SourceType .ANALYSIS &&
284359 oldSym .getName ().startsWith ("Addr" )) {
285360 // cleanup AddressTableAnalyzer label
286361 oldSym .delete ();
287362 }
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 );
290365 }
291366 }
292367
@@ -338,6 +413,9 @@ private Address[] getPointerTable(JumpTable.LoadTable loadtable, Address[] switc
338413 return addresses ;
339414 }
340415
416+ /*
417+ * put labels on the switch table used to compute the target addresses of the switch.
418+ */
341419 private void labelLoadTable (JumpTable .LoadTable loadtable , Address [] switchCases ,
342420 Symbol [] caseSymbols , Namespace space , TaskMonitor monitor ) throws CancelledException {
343421
0 commit comments