GP-6776 additional fixes

This commit is contained in:
James
2026-05-05 15:37:34 -04:00
parent 5259f4b7ae
commit 4fac3553a8
13 changed files with 488 additions and 266 deletions
@@ -327,7 +327,8 @@ public class SymPcodeExecutor extends PcodeExecutor<Sym> {
for (int i = 0; i < arguments.length; i++) {
types[i + 1] = arguments[0].getDataType();
}
VariableStorage[] vsLocs = convention.getStorageLocations(program, types, false);
VariableStorage[] vsLocs =
convention.getStorageLocations(program, types, false, sig.hasVarArgs());
Address min = null;
Address max = null; // Exclusive
for (VariableStorage vs : vsLocs) {
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -189,10 +189,11 @@ class FunctionData extends FunctionDataView {
void setVarArgs(boolean enable) {
this.hasVarArgs = enable;
updateParameterAndReturnStorage();
}
/**
* Update dynamic storage and auto-params when custom storage is disasbled.
* Update dynamic storage and auto-params when custom storage is disabled.
* Returns immediately if custom storage is enabled.
*/
void updateParameterAndReturnStorage() {
@@ -218,7 +219,8 @@ class FunctionData extends FunctionDataView {
}
VariableStorage[] paramStorage =
effectiveCallingConvention.getStorageLocations(getProgram(), dataTypes, true, hasVarArgs);
effectiveCallingConvention.getStorageLocations(getProgram(), dataTypes, true,
hasVarArgs);
returnInfo.setStorage(paramStorage[0]);
File diff suppressed because it is too large Load Diff
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -429,7 +429,8 @@ public class StringParameterPropagator extends GhidraScript {
return false;
}
VariableStorage storage =
convention.getArgLocation(paramIndex, func.getParameters(), dt, currentProgram);
convention.getArgLocation(paramIndex, func.getParameters(), dt, currentProgram,
false);
if (storage.isUnassignedStorage()) {
return false;
}
@@ -498,7 +499,7 @@ public class StringParameterPropagator extends GhidraScript {
continue;
}
VariableStorage storage = convention.getArgLocation(i - 1, f.getParameters(),
DataType.DEFAULT, currentProgram);
DataType.DEFAULT, currentProgram, false);
if (storage.isUnassignedStorage()) {
break;
}
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -234,7 +234,7 @@ public class FillOutStructureHelper {
typeList[i] = inputs[i].getHigh().getDataType();
}
VariableStorage[] storageLocations =
model.getStorageLocations(currentProgram, typeList, false);
model.getStorageLocations(currentProgram, typeList, false, function.hasVarArgs());
return storageLocations[slot].getMinAddress();
}
@@ -86,7 +86,7 @@ public class UseropEmuSyscallDefinition<T> implements EmuSyscallDefinition<T> {
for (int i = 0; i < locs.length; i++) {
locs[i] = dtMachineWord;
}
VariableStorage[] vss = convention.getStorageLocations(program, locs, false);
VariableStorage[] vss = convention.getStorageLocations(program, locs, false, false);
outVar = getSingleVnStorage(vss[0]);
inVars = Arrays.asList(new Varnode[inputCount]);
@@ -615,12 +615,14 @@ public class FunctionManagerDB implements FunctionManager {
* @param returnDataType
* @param currentParams
* @param paramOffset offset within currentParams for first parameter
* @param hasVarArgs if true, any storage rules specific to varargs function will be applied
* @return return variable storage if dynamic storage does not match current custom storage -
* this is done so that this storage can be assigned if currently <UNASSIGNED>. If dynamic
* storage matches null will be returned.
*/
private VariableStorage checkDynamicStorageConversion(DataType returnDataType,
Parameter[] currentParams, int paramOffset, PrototypeModel callingConvention) {
Parameter[] currentParams, int paramOffset, PrototypeModel callingConvention,
boolean hasVarArgs) {
DataType types[] = new DataType[currentParams.length - paramOffset + 1];
types[0] = returnDataType;
int index = 1;
@@ -629,7 +631,7 @@ public class FunctionManagerDB implements FunctionManager {
}
VariableStorage[] paramStorage =
callingConvention.getStorageLocations(program, types, true);
callingConvention.getStorageLocations(program, types, true, hasVarArgs);
// TODO: Only handles a single auto-param insertion (could be more auto-params)
index = (paramStorage.length == types.length) ? 1 : 2; // May have inserted extra parameter
if ((paramStorage.length - 1) != types.length) {
@@ -681,7 +683,8 @@ public class FunctionManagerDB implements FunctionManager {
if (CompilerSpec.CALLING_CONVENTION_thiscall.equals(func.getCallingConventionName())) {
if (params.length != 0 && isLikelyThisParam(params[0])) {
returnStorage =
checkDynamicStorageConversion(returnDataType, params, 1, callingConvention);
checkDynamicStorageConversion(returnDataType, params, 1, callingConvention,
func.hasVarArgs());
if (returnStorage == null) {
useDynamic = true;
func.removeVariable(params[0]);
@@ -690,7 +693,8 @@ public class FunctionManagerDB implements FunctionManager {
}
else {
returnStorage =
checkDynamicStorageConversion(returnDataType, params, 0, callingConvention);
checkDynamicStorageConversion(returnDataType, params, 0, callingConvention,
func.hasVarArgs());
useDynamic = (returnStorage == null);
}
@@ -141,7 +141,8 @@ class FunctionVariables {
}
VariableStorage[] variableStorage =
callingConvention.getStorageLocations(function.getProgram(), dataTypes, true);
callingConvention.getStorageLocations(function.getProgram(), dataTypes, true,
function.hasVarArgs());
returnParam.setDynamicStorage(variableStorage[0]);
int autoIndex = 0;
@@ -285,7 +285,7 @@ public class PrototypeModel {
*/
public VariableStorage getNextArgLocation(Parameter[] params, DataType dataType,
Program program) {
return getArgLocation(params != null ? params.length : 0, params, dataType, program);
return getArgLocation(params != null ? params.length : 0, params, dataType, program, false);
}
/**
@@ -315,9 +315,46 @@ public class PrototypeModel {
* @param program is the Program
* @return parameter location or {@link VariableStorage#UNASSIGNED_STORAGE} if
* unable to determine suitable location
* @deprecated Use {@link #getArgLocation(int, Parameter[], DataType, Program, boolean)}
* instead.
*/
@Deprecated
public VariableStorage getArgLocation(int argIndex, Parameter[] params, DataType dataType,
Program program) {
return getArgLocation(argIndex, params, dataType, program, false);
}
/**
* Get the preferred parameter location for a specified index,
* which will be added/inserted within the set of existing function params.
* If existing parameters use custom storage, this method should not be used.
* <br>
* Note: storage will not be assigned to the {@link DataType#DEFAULT default undefined} datatype,
* zero-length datatype, or any subsequent parameter following such a parameter.
* <br>
* Warning: The use of this method with a null {@code params} argument, or incorrect
* datatypes, is highly discouraged since it will produce inaccurate results.
* It is recommended that a complete function signature be used in
* conjunction with the {@link #getStorageLocations(Program, DataType[], boolean)}
* method. Parameter storage allocation may be affected by the return datatype
* specified (e.g., hidden return storage parameter).
*
* @param argIndex is the index (0: return storage, 1..n: parameter storage)
* @param params existing set parameters to which the parameter specified by
* argIndex will be added/inserted be appended. Element-0 corresponds to the return
* datatype. Parameter elements prior to the argIndex are required for an accurate
* storage determination to be made. Any preceeding parameters not specified will be assumed
* as a 1-byte integer type which could cause an erroneous storage result to be returned.
* A null params list will cause all preceeding params to be assumed in a similar fashion.
* @param dataType dataType associated with next parameter location or null
* for a default undefined type.
* @param program is the Program
* @param hasVarArgs if true, any assignment rules specific to varargs functions will be applied.
* @return parameter location or {@link VariableStorage#UNASSIGNED_STORAGE} if
* unable to determine suitable location
*/
public VariableStorage getArgLocation(int argIndex, Parameter[] params, DataType dataType,
Program program, boolean hasVarArgs) {
if (dataType != null) {
dataType = dataType.clone(program.getDataTypeManager());
@@ -338,7 +375,7 @@ public class PrototypeModel {
}
arr[argIndex + 1] = dataType;
VariableStorage res[] = getStorageLocations(program, arr, false);
VariableStorage res[] = getStorageLocations(program, arr, false, hasVarArgs);
return res[res.length - 1];
}
@@ -378,12 +415,13 @@ public class PrototypeModel {
}
/**
* Compute the variable storage for a given array of return/parameter datatypes. The first array element
* is the return datatype, which is followed by any input parameter datatypes in order.
* If addAutoParams is true, pointer datatypes will automatically be inserted for "this" or "hidden return"
* input parameters, if needed. In this case, the dataTypes array should not include explicit entries for
* these parameters. If addAutoParams is false, the dataTypes array is assumed to already contain explicit
* entries for any of these parameters.
* Compute the variable storage for a given array of return/parameter datatypes.
* The first array element is the return datatype, which is followed by any input parameter
* datatypes in order. If addAutoParams is true, pointer datatypes will automatically be
* inserted for "this" or "hidden return"input parameters, if needed. In this case, the
* dataTypes array should not include explicit entries for these parameters. If addAutoParams
* is false, the dataTypes array is assumed to already contain explicit entries for any of
* these parameters.
* <br>
* Note: storage will not be assigned to the {@link DataType#DEFAULT default undefined} datatype
* or zero-length datatypes or any subsequent parameter following such a parameter.
@@ -395,30 +433,36 @@ public class PrototypeModel {
* @return dynamic storage locations orders by ordinal where first element corresponds to
* return storage. The returned array may also include additional auto-parameter storage
* locations.
* @deprecated Use {@link #getStorageLocations(Program,DataType[],boolean,boolean)} instead.
*/
@Deprecated
public VariableStorage[] getStorageLocations(Program program, DataType[] dataTypes,
boolean addAutoParams) {
return getStorageLocations(program, dataTypes, addAutoParams, false);
}
/**
* Calculate input and output storage locations given a function prototype,
* with optional variable-argument support.
* <p>
* This overload additionally sets {@link PrototypePieces#firstVarArgSlot} when
* {@code isVarArgs} is true, enabling {@code VarargsFilter}-based calling-convention
* rules (e.g. {@code <varargs/>} in a .cspec) to be applied correctly.
*
* Compute the variable storage for a given array of return/parameter datatypes.
* The first array element is the return datatype, which is followed by any input parameter
* datatypes in order. If addAutoParams is true, pointer datatypes will automatically be
* inserted for "this" or "hidden return"input parameters, if needed. In this case, the
* dataTypes array should not include explicit entries for these parameters. If addAutoParams
* is false, the dataTypes array is assumed to already contain explicit entries for any of
* these parameters.
* <br>
* Note: storage will not be assigned to the {@link DataType#DEFAULT default undefined} datatype
* or zero-length datatypes or any subsequent parameter following such a parameter.
*
* @param program is the Program
* @param dataTypes return/parameter datatypes (first element is always the return datatype,
* @param dataTypes return/parameter datatypes (first element is always the return datatype,
* i.e., minimum array length is 1)
* @param addAutoParams true if auto-parameter storage locations can be generated
* @param isVarArgs true if the function takes variable arguments; when true, all
* data-types supplied in {@code dataTypes} are treated as non-optional and the first
* vararg slot is set to immediately follow the last supplied parameter
* @return dynamic storage locations ordered by ordinal where first element corresponds to
* return storage. The returned array may also include additional auto-parameter storage
* locations.
* @param isVarArgs If true, the last input parameter will be treated as the last fixed argument
* of a varargs function (via {@link PrototypePieces#firstVarArgSlot}).
* @return dynamic storage locations orders by ordinal where first element corresponds to
* return storage. The returned array may also include additional auto-parameter storage
* locations.
*
*/
public VariableStorage[] getStorageLocations(Program program, DataType[] dataTypes,
boolean addAutoParams, boolean isVarArgs) {
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -752,7 +752,8 @@ public class VariableUtilities {
arr[0] = DataType.VOID;
arr[1] = dt;
VariableStorage thisStorage =
convention.getStorageLocations(function.getProgram(), arr, true)[1];
convention.getStorageLocations(function.getProgram(), arr, true,
function.hasVarArgs())[1];
try {
return new ParameterImpl("this", 0, dt, thisStorage, false, function.getProgram(),
SourceType.ANALYSIS);
@@ -52,6 +52,10 @@
<datatype name="struct" minsize="1"/>
<convert_to_ptr/>
</rule>
<rule>
<datatype name="union" minsize="1"/>
<convert_to_ptr/>
</rule>
<rule>
<datatype name="any" maxsize="4"/>
<join stackspill="true"/>
@@ -78,6 +82,10 @@
<datatype name="struct"/>
<hidden_return/>
</rule>
<rule>
<datatype name="union"/>
<hidden_return/>
</rule>
<rule>
<datatype name="any" maxsize="8"/>
<join/>
@@ -25,37 +25,6 @@ public class MSP430_CSpecTest extends CSpecPrototypeTest {
private static final String[] EXPECTED_PROTOTYPE_ERRORS = {
"paramsVariadic_I_I_C_C_S_S_I_I_L_L_50",
"paramsUnion_56",
"paramsUnion_57",
"paramsUnion_58",
"paramsUnion_59",
"paramsUnion_60",
"paramsUnion_61",
"paramsUnion_62",
"paramsUnion_63",
"paramsUnion_64",
"paramsUnion_65",
"paramsUnion_66",
"paramsUnion_67",
"paramsUnion_68",
"paramsUnion_69",
"paramsUnion_70",
"paramsUnion_71",
"paramsUnion_72",
"paramsUnion_73",
"paramsUnion_74",
"paramsUnion_75",
"paramsUnion_76",
"returnUnion_82",
"returnUnion_83",
"returnUnion_89",
"returnUnion_90",
"returnUnion_96",
"returnUnion_102",
"returnUnion_104",
"returnUnion_112",
"returnUnion_121",
"returnUnion_130",
};
public MSP430_CSpecTest() throws Exception {
@@ -0,0 +1,139 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang;
import static org.junit.Assert.*;
import org.junit.Test;
import generic.test.AbstractGenericTest;
import ghidra.app.plugin.processors.sleigh.SleighLanguageProvider;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
public class VarargsStorageTest extends AbstractGenericTest {
private Program program;
@Test
public void test_MSP430_stdcall() throws Exception {
Language language = SleighLanguageProvider.getSleighLanguageProvider()
.getLanguage(new LanguageID("TI_MSP430:LE:16:default"));
ProgramBuilder builder = new ProgramBuilder("test", language);
program = builder.getProgram();
PrototypeModel model = program.getCompilerSpec().getCallingConvention("__stdcall");
DataType intType = new UnsignedIntegerDataType(program.getDataTypeManager());
DataType voidType = new VoidDataType(program.getDataTypeManager());
assertNotNull(model);
DataType[] inputs = new DataType[4];
inputs[0] = voidType;
inputs[1] = intType;
inputs[2] = intType;
inputs[3] = intType;
VariableStorage[] storage = model.getStorageLocations(program, inputs, false, false);
assertEquals(1, storage[1].getVarnodeCount());
assertEquals("R12", storage[1].getFirstVarnode().toString(language));
assertEquals(1, storage[2].getVarnodeCount());
assertEquals("R13", storage[2].getFirstVarnode().toString(language));
assertEquals(1, storage[3].getVarnodeCount());
assertEquals("R14", storage[3].getFirstVarnode().toString(language));
// TI_MSP430 __stdcall passes the last fixed argument of a variadic function
// on the stack, so storage[3] should be a stack location instead of R14
storage = model.getStorageLocations(program, inputs, false, true);
assertEquals(1, storage[1].getVarnodeCount());
assertEquals("R12", storage[1].getFirstVarnode().toString(language));
assertEquals(1, storage[2].getVarnodeCount());
assertEquals("R13", storage[2].getFirstVarnode().toString(language));
assertEquals(1, storage[3].getVarnodeCount());
assertTrue(storage[3].isStackStorage());
}
@Test
public void test_avr8__stdcall() throws Exception {
Language language = SleighLanguageProvider.getSleighLanguageProvider()
.getLanguage(new LanguageID("avr8:LE:16:default"));
ProgramBuilder builder = new ProgramBuilder("test", language);
program = builder.getProgram();
PrototypeModel model = program.getCompilerSpec().getCallingConvention("__stdcall");
DataType intType = new UnsignedIntegerDataType(program.getDataTypeManager());
DataType voidType = new VoidDataType(program.getDataTypeManager());
assertNotNull(model);
DataType[] inputs = new DataType[4];
inputs[0] = voidType;
inputs[1] = intType;
inputs[2] = intType;
inputs[3] = intType;
VariableStorage[] storage = model.getStorageLocations(program, inputs, false, false);
assertEquals(1, storage[1].getVarnodeCount());
assertEquals("R25R24", storage[1].getFirstVarnode().toString(language));
assertEquals(1, storage[2].getVarnodeCount());
assertEquals("R23R22", storage[2].getFirstVarnode().toString(language));
assertEquals(1, storage[3].getVarnodeCount());
assertEquals("R21R20", storage[3].getFirstVarnode().toString(language));
// avr8 __stdcall passes all parameters of a variadic function on the stack
storage = model.getStorageLocations(program, inputs, false, true);
assertEquals(1, storage[1].getVarnodeCount());
assertTrue(storage[1].isStackStorage());
assertEquals(1, storage[2].getVarnodeCount());
assertTrue(storage[2].isStackStorage());
assertEquals(1, storage[3].getVarnodeCount());
assertTrue(storage[3].isStackStorage());
}
@Test
public void test_x64__stdcall() throws Exception {
Language language = SleighLanguageProvider.getSleighLanguageProvider()
.getLanguage(new LanguageID("x86:LE:64:default"));
ProgramBuilder builder = new ProgramBuilder("test", "x86:LE:64:default", "gcc", null);
program = builder.getProgram();
PrototypeModel model = program.getCompilerSpec().getCallingConvention("__stdcall");
DataType longType = new UnsignedLongDataType(program.getDataTypeManager());
DataType voidType = new VoidDataType(program.getDataTypeManager());
assertNotNull(model);
DataType[] inputs = new DataType[4];
inputs[0] = voidType;
inputs[1] = longType;
inputs[2] = longType;
inputs[3] = longType;
VariableStorage[] storage = model.getStorageLocations(program, inputs, false, false);
assertEquals(1, storage[1].getVarnodeCount());
assertEquals("RDI", storage[1].getFirstVarnode().toString(language));
assertEquals(1, storage[2].getVarnodeCount());
assertEquals("RSI", storage[2].getFirstVarnode().toString(language));
assertEquals(1, storage[3].getVarnodeCount());
assertEquals("RDX", storage[3].getFirstVarnode().toString(language));
// x64 gcc __stdcall does not assign parameters differently for variadic functions
storage = model.getStorageLocations(program, inputs, false, true);
assertEquals(1, storage[1].getVarnodeCount());
assertEquals("RDI", storage[1].getFirstVarnode().toString(language));
assertEquals(1, storage[2].getVarnodeCount());
assertEquals("RSI", storage[2].getFirstVarnode().toString(language));
assertEquals(1, storage[3].getVarnodeCount());
assertEquals("RDX", storage[3].getFirstVarnode().toString(language));
}
}