Unverified Commit 9fa65999 by Melvin Klimke Committed by GitHub

Better parameter parsing for external Symbols (#176)

parent 275c13aa
......@@ -428,7 +428,11 @@ impl<'a> Context<'a> {
// We assume here that we do not know the parameters and approximate them by all possible parameter registers.
// This approximation is wrong if the function is known but has neither parameters nor return values.
// We cannot distinguish these two cases yet.
for parameter_register_name in calling_conv.parameter_register.iter() {
for parameter_register_name in calling_conv
.integer_parameter_register
.iter()
.chain(calling_conv.float_parameter_register.iter())
{
if let Some(register_value) = state.get_register_by_name(parameter_register_name) {
possible_referenced_ids.append(&mut register_value.referenced_ids());
}
......@@ -469,7 +473,11 @@ impl<'a> Context<'a> {
self.adjust_stack_register_on_extern_call(state_before_call, &mut new_state);
let mut possible_referenced_ids = BTreeSet::new();
for parameter_register_name in calling_conv.parameter_register.iter() {
for parameter_register_name in calling_conv
.integer_parameter_register
.iter()
.chain(calling_conv.float_parameter_register.iter())
{
if let Some(register_value) =
state_before_call.get_register_by_name(parameter_register_name)
{
......
......@@ -84,7 +84,8 @@ fn mock_project() -> (Project, Config) {
};
let cconv = CallingConvention {
name: "default".to_string(),
parameter_register: vec!["RDX".to_string()],
integer_parameter_register: vec!["RDX".to_string()],
float_parameter_register: vec!["XMM0".to_string()],
return_register: vec!["RDX".to_string()],
callee_saved_register: vec!["callee_saved_reg".to_string()],
};
......
......@@ -297,10 +297,9 @@ impl State {
pi_state_option: Option<&PointerInferenceState>,
) -> bool {
if let Some(calling_conv) = project.get_standard_calling_convention() {
self.check_register_list_for_taint(
&calling_conv.parameter_register[..],
pi_state_option,
)
let mut all_parameters = calling_conv.integer_parameter_register.clone();
all_parameters.append(&mut calling_conv.float_parameter_register.clone());
self.check_register_list_for_taint(&all_parameters, pi_state_option)
} else {
// No standard calling convention found. Assume everything may be parameters or referenced by parameters.
!self.is_empty()
......
......@@ -385,8 +385,12 @@ impl State {
/// we approximate the parameters with all parameter registers of the standard calling convention of the project.
pub fn remove_non_parameter_taints_for_generic_function(&mut self, project: &Project) {
if let Some(calling_conv) = project.get_standard_calling_convention() {
let register_names: HashSet<String> =
calling_conv.parameter_register.iter().cloned().collect();
let register_names: HashSet<String> = calling_conv
.integer_parameter_register
.iter()
.chain(calling_conv.float_parameter_register.iter())
.cloned()
.collect();
let taints = self.register_taint.clone();
for (register, _) in taints.iter() {
if register_names.get(&register.name).is_none() {
......
......@@ -423,8 +423,10 @@ pub struct CallingConvention {
/// The name of the calling convention
#[serde(rename = "calling_convention")]
pub name: String,
/// A list of possible parameter register
pub parameter_register: Vec<String>,
/// Possible integer parameter registers.
pub integer_parameter_register: Vec<String>,
/// Possible float parameter registers.
pub float_parameter_register: Vec<String>,
/// A list of possible return register
pub return_register: Vec<String>,
/// A list of callee-saved register,
......@@ -616,7 +618,8 @@ mod tests {
pub fn mock() -> CallingConvention {
CallingConvention {
name: "__stdcall".to_string(), // so that the mock is useable as standard calling convention in tests
parameter_register: vec!["RDI".to_string()],
integer_parameter_register: vec!["RDI".to_string()],
float_parameter_register: vec!["XMMO".to_string()],
return_register: vec!["RAX".to_string()],
callee_saved_register: vec!["RBP".to_string()],
}
......
......@@ -495,8 +495,10 @@ pub struct CallingConvention {
/// The name of the calling convention.
#[serde(rename = "calling_convention")]
pub name: String,
/// Possible parameter registers.
parameter_register: Vec<String>,
/// Possible integer parameter registers.
integer_parameter_register: Vec<String>,
/// Possible float parameter registers.
float_parameter_register: Vec<String>,
/// Possible return registers.
return_register: Vec<String>,
/// Callee-saved registers.
......@@ -509,7 +511,8 @@ impl From<CallingConvention> for IrCallingConvention {
fn from(cconv: CallingConvention) -> IrCallingConvention {
IrCallingConvention {
name: cconv.name,
parameter_register: cconv.parameter_register,
integer_parameter_register: cconv.integer_parameter_register,
float_parameter_register: cconv.float_parameter_register,
return_register: cconv.return_register,
callee_saved_register: cconv.unaffected_register,
}
......
......@@ -85,7 +85,8 @@ impl Setup {
"register_calling_convention": [
{
"calling_convention": "default",
"parameter_register": [],
"integer_parameter_register": [],
"float_parameter_register": [],
"return_register": [],
"unaffected_register": [],
"killed_by_call_register": []
......
......@@ -254,7 +254,6 @@ public class PcodeExtractor extends GhidraScript {
try {
HashMap<String, RegisterConvention> conventions = new HashMap<String, RegisterConvention>();
ParseCspecContent.parseSpecs(currentProgram, conventions);
addParameterRegister(conventions);
project.setRegisterConvention(new ArrayList<RegisterConvention>(conventions.values()));
} catch (FileNotFoundException e) {
System.out.println(e);
......@@ -267,23 +266,6 @@ public class PcodeExtractor extends GhidraScript {
/**
* Adds parameter register to the RegisterCallingConvention object
*/
protected void addParameterRegister(HashMap<String, RegisterConvention> conventions) {
PrototypeModel[] models = currentProgram.getCompilerSpec().getCallingConventions();
for(PrototypeModel model : models) {
String cconv = model.getName();
if(conventions.get(cconv) != null) {
ArrayList<String> parameters = conventions.get(cconv).getParameter();
for(VariableStorage storage : model.getPotentialInputRegisterStorage(currentProgram)) {
parameters.add(storage.getRegister().getName());
}
}
}
}
/**
* Adds all entry points of internal and external function to a global hash map
* This will later speed up the cast of indirect Calls.
*/
......
......@@ -267,10 +267,10 @@ public class ParseCspecContent {
RegisterConvention convention = new RegisterConvention();
if(name.equals("default_proto")) {
parser.start();
getUnaffectedKilledByCallAndOutput(parser, convention);
getCconvRegister(parser, convention);
parser.end();
} else if(name.equals("prototype")) {
getUnaffectedKilledByCallAndOutput(parser, convention);
getCconvRegister(parser, convention);
}
// Using the hashmap this way will simplify the addition of parameter registers which are not parsed here
......@@ -286,17 +286,19 @@ public class ParseCspecContent {
*
* Sets the convention's unaffected, killed by call and return registers as well as the calling convention
*/
public static void getUnaffectedKilledByCallAndOutput(XmlPullParser parser, RegisterConvention convention) {
public static void getCconvRegister(XmlPullParser parser, RegisterConvention convention) {
XmlElement protoElement = parser.start();
String cconv = protoElement.getAttribute("name");
convention.setCconv(cconv);
while(parser.peek().isStart()) {
XmlElement registers = parser.peek();
if(registers.getName().equals("unaffected")) {
XmlElement entries = parser.peek();
if(entries.getName().equals("input")) {
parseInput(parser, convention);
} else if(entries.getName().equals("unaffected")) {
convention.setUnaffected(getRegisters(parser));
} else if (registers.getName().equals("killedbycall")) {
} else if (entries.getName().equals("killedbycall")) {
convention.setKilledByCall(getRegisters(parser));
} else if (registers.getName().equals("output")) {
} else if (entries.getName().equals("output")) {
convention.setReturn(parseOutput(parser));
} else {
discardSubTree(parser);
......@@ -305,6 +307,55 @@ public class ParseCspecContent {
parser.end(protoElement);
}
/**
*
* @param parser: parser for .cspec file
* @param convention: convention object for later serialization
*
* Parses the parameter registers for an external symbol.
* Differentiates between integer and float registers.
*/
public static void parseInput(XmlPullParser parser, RegisterConvention convention) {
ArrayList<String> integerRegisters = new ArrayList<String>();
ArrayList<String> floatRegisters = new ArrayList<String>();
parser.start("input");
while(parser.peek().isStart()) {
XmlElement pentry = parser.peek();
parser.start("pentry");
XmlElement entry = parser.peek();
if(entry.getName().equals("register")) {
parser.start("register");
if(isFloatRegister(pentry)) {
floatRegisters.add(entry.getAttribute("name"));
} else {
integerRegisters.add(entry.getAttribute("name"));
}
parser.end();
} else {
discardSubTree(parser);
}
parser.end();
}
parser.end();
convention.setFloatParameter(floatRegisters);
convention.setIntegerParameter(integerRegisters);
}
/**
*
* @param pentry: Parameter register entry
* @return: indicates whether the current entry is a float register.
*/
public static Boolean isFloatRegister(XmlElement pentry) {
if(pentry.hasAttribute("metatype")) {
if(pentry.getAttribute("metatype").equals("float")) {
return true;
}
}
return false;
}
/**
*
......
package internal;
import java.util.ArrayList;
......@@ -9,8 +8,10 @@ public class RegisterConvention {
@SerializedName("calling_convention")
private String cconv;
@SerializedName("parameter_register")
private ArrayList<String> parameter;
@SerializedName("integer_parameter_register")
private ArrayList<String> integerParameter;
@SerializedName("float_parameter_register")
private ArrayList<String> floatParameter;
@SerializedName("return_register")
private ArrayList<String> return_;
@SerializedName("unaffected_register")
......@@ -19,15 +20,24 @@ public class RegisterConvention {
private ArrayList<String> killedByCall;
public RegisterConvention() {
this.setParameter(new ArrayList<String>());
this.setIntegerParameter(new ArrayList<String>());
this.setFloatParameter(new ArrayList<String>());
this.setReturn(new ArrayList<String>());
this.setUnaffected(new ArrayList<String>());
this.setKilledByCall(new ArrayList<String>());
}
public RegisterConvention(String cconv, ArrayList<String> parameter, ArrayList<String> return_, ArrayList<String> unaffected, ArrayList<String> killedByCall) {
public RegisterConvention(
String cconv,
ArrayList<String> integerParameter,
ArrayList<String> floatParameter,
ArrayList<String> return_,
ArrayList<String> unaffected,
ArrayList<String> killedByCall
) {
this.setCconv(cconv);
this.setParameter(parameter);
this.setIntegerParameter(integerParameter);
this.setFloatParameter(floatParameter);
this.setReturn(return_);
this.setUnaffected(unaffected);
this.setKilledByCall(killedByCall);
......@@ -41,12 +51,20 @@ public class RegisterConvention {
this.cconv = cconv;
}
public ArrayList<String> getParameter() {
return parameter;
public ArrayList<String> getIntegerParameter() {
return integerParameter;
}
public void setIntegerParameter(ArrayList<String> integerParameter) {
this.integerParameter = integerParameter;
}
public ArrayList<String> getFloatParameter() {
return floatParameter;
}
public void setParameter(ArrayList<String> parameter) {
this.parameter = parameter;
public void setFloatParameter(ArrayList<String> floatParameter) {
this.floatParameter = floatParameter;
}
public ArrayList<String> getReturn() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment