Skip to content

Commit 3c57ea8

Browse files
committed
Merge remote-tracking branch 'origin/GP-5583_emteere_DefaultCaseFlowAndLabels' into patch
2 parents 3725eb0 + 2d19520 commit 3c57ea8

File tree

2 files changed

+396
-91
lines changed

2 files changed

+396
-91
lines changed

Ghidra/Features/Decompiler/src/main/java/ghidra/app/cmd/function/DecompilerSwitchAnalysisCmd.java

+169-91
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
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

Comments
 (0)