fix: make decompiler variables renameable
This commit is contained in:
parent
7cf426ef53
commit
c4d170cdca
@ -7,15 +7,24 @@ import eu.starsong.ghidra.api.ResponseBuilder;
|
||||
import eu.starsong.ghidra.model.FunctionInfo;
|
||||
import eu.starsong.ghidra.util.GhidraUtil;
|
||||
import eu.starsong.ghidra.util.TransactionHelper;
|
||||
import ghidra.app.decompiler.DecompInterface;
|
||||
import ghidra.app.decompiler.DecompileResults;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Parameter;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.pcode.HighFunction;
|
||||
import ghidra.program.model.pcode.HighFunctionDBUtil;
|
||||
import ghidra.program.model.pcode.HighSymbol;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.ConsoleTaskMonitor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.io.IOException;
|
||||
@ -791,6 +800,14 @@ public class FunctionEndpoints extends AbstractEndpoint {
|
||||
handleDisassembleFunction(exchange, function);
|
||||
} else if (resource.equals("variables")) {
|
||||
handleFunctionVariables(exchange, function);
|
||||
} else if (resource.startsWith("variables/")) {
|
||||
// Handle variable operations
|
||||
String variableName = resource.substring("variables/".length());
|
||||
if ("PATCH".equals(exchange.getRequestMethod())) {
|
||||
handleUpdateVariable(exchange, function, variableName);
|
||||
} else {
|
||||
sendErrorResponse(exchange, 405, "Method not allowed for variable operations", "METHOD_NOT_ALLOWED");
|
||||
}
|
||||
} else {
|
||||
sendErrorResponse(exchange, 404, "Function resource not found: " + resource, "RESOURCE_NOT_FOUND");
|
||||
}
|
||||
@ -1251,8 +1268,127 @@ public class FunctionEndpoints extends AbstractEndpoint {
|
||||
* Handle requests to update a function variable
|
||||
*/
|
||||
private void handleUpdateVariable(HttpExchange exchange, Function function, String variableName) throws IOException {
|
||||
// This is a placeholder - actual implementation would update the variable
|
||||
sendErrorResponse(exchange, 501, "Variable update not implemented", "NOT_IMPLEMENTED");
|
||||
// This is a placeholder - we need to implement variable renaming here
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) {
|
||||
sendErrorResponse(exchange, 400, "No program loaded", "NO_PROGRAM_LOADED");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Parse the request body to get the update parameters
|
||||
Map<String, String> params = parseJsonPostParams(exchange);
|
||||
String newName = params.get("name");
|
||||
String newDataType = params.get("data_type");
|
||||
|
||||
if (newName == null && newDataType == null) {
|
||||
sendErrorResponse(exchange, 400, "Missing update parameters - name or data_type required", "MISSING_PARAMETER");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a decompiler-generated variable
|
||||
boolean success = false;
|
||||
String message = "";
|
||||
|
||||
// Use a transaction to update the variable
|
||||
try {
|
||||
int txId = program.startTransaction("Update Function Variable");
|
||||
try {
|
||||
// First check if this is a regular parameter or local variable
|
||||
for (Parameter param : function.getParameters()) {
|
||||
if (param.getName().equals(variableName)) {
|
||||
if (newName != null) {
|
||||
param.setName(newName, ghidra.program.model.symbol.SourceType.USER_DEFINED);
|
||||
success = true;
|
||||
message = "Parameter renamed successfully";
|
||||
}
|
||||
// Handle data type change if needed
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If not a parameter, check if it's a local variable
|
||||
if (!success) {
|
||||
for (ghidra.program.model.listing.Variable var : function.getAllVariables()) {
|
||||
if (var.getName().equals(variableName) && !(var instanceof Parameter)) {
|
||||
if (newName != null) {
|
||||
var.setName(newName, ghidra.program.model.symbol.SourceType.USER_DEFINED);
|
||||
success = true;
|
||||
message = "Local variable renamed successfully";
|
||||
}
|
||||
// Handle data type change if needed
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If not a database variable, try as a decompiler variable
|
||||
if (!success) {
|
||||
// This requires a decompile operation to get the HighFunction
|
||||
DecompInterface decomp = new DecompInterface();
|
||||
try {
|
||||
decomp.openProgram(program);
|
||||
DecompileResults results = decomp.decompileFunction(function, 30, new ConsoleTaskMonitor());
|
||||
|
||||
if (results.decompileCompleted()) {
|
||||
HighFunction highFunc = results.getHighFunction();
|
||||
if (highFunc != null) {
|
||||
// Find the variable in the high function
|
||||
HighSymbol symbol = null;
|
||||
Iterator<HighSymbol> symbolIter = highFunc.getLocalSymbolMap().getSymbols();
|
||||
while (symbolIter.hasNext()) {
|
||||
HighSymbol hs = symbolIter.next();
|
||||
if (hs.getName().equals(variableName)) {
|
||||
symbol = hs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (symbol != null) {
|
||||
if (newName != null) {
|
||||
// Rename the variable using HighFunctionDBUtil
|
||||
HighFunctionDBUtil.updateDBVariable(
|
||||
symbol, newName, null, SourceType.USER_DEFINED);
|
||||
success = true;
|
||||
message = "Decompiler variable renamed successfully";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
decomp.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
program.endTransaction(txId, true);
|
||||
} catch (Exception e) {
|
||||
program.endTransaction(txId, false);
|
||||
throw e;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendErrorResponse(exchange, 500, "Error updating variable: " + e.getMessage(), "UPDATE_FAILED");
|
||||
return;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
// Create a successful response
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("name", newName != null ? newName : variableName);
|
||||
result.put("function", function.getName());
|
||||
result.put("address", function.getEntryPoint().toString());
|
||||
result.put("message", message);
|
||||
|
||||
ResponseBuilder builder = new ResponseBuilder(exchange, port)
|
||||
.success(true)
|
||||
.result(result);
|
||||
|
||||
sendJsonResponse(exchange, builder.build(), 200);
|
||||
} else {
|
||||
sendErrorResponse(exchange, 404, "Function resource not found: variables/" + variableName, "RESOURCE_NOT_FOUND");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendErrorResponse(exchange, 500, "Error processing variable update request: " + e.getMessage(), "INTERNAL_ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -58,9 +58,9 @@ package eu.starsong.ghidra.endpoints;
|
||||
@Override
|
||||
public void registerEndpoints(HttpServer server) {
|
||||
server.createContext("/variables", this::handleGlobalVariables);
|
||||
// Note: /functions/{name}/variables is handled within FunctionEndpoints for now
|
||||
// to keep related logic together until full refactor.
|
||||
// If needed, we can create a more complex routing mechanism later.
|
||||
server.createContext("/functions/*/variables/*", this::handleFunctionVariableOperation);
|
||||
server.createContext("/functions/by-name/*/variables/*", this::handleFunctionVariableByNameOperation);
|
||||
// Note: /functions/{name}/variables (listing) is still handled within FunctionEndpoints
|
||||
}
|
||||
|
||||
private void handleGlobalVariables(HttpExchange exchange) throws IOException {
|
||||
@ -642,4 +642,421 @@ package eu.starsong.ghidra.endpoints;
|
||||
DataType dt = data.getDataType();
|
||||
return dt != null ? dt.getName() : "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle operations on a specific function variable (by function address or by name)
|
||||
* This handles GET/PATCH operations for /functions/{address}/variables/{varName}
|
||||
* or /functions/by-name/{name}/variables/{varName}
|
||||
*/
|
||||
public void handleFunctionVariableOperation(HttpExchange exchange) throws IOException {
|
||||
try {
|
||||
String path = exchange.getRequestURI().getPath();
|
||||
String[] parts = path.split("/");
|
||||
|
||||
// Path should be like /functions/{address}/variables/{varName}
|
||||
if (parts.length < 5) {
|
||||
sendErrorResponse(exchange, 400, "Invalid URL format", "INVALID_URL_FORMAT");
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract function address from path
|
||||
String functionAddress = parts[2];
|
||||
String variableName = parts[4];
|
||||
|
||||
// Handle different HTTP methods
|
||||
if ("PATCH".equals(exchange.getRequestMethod())) {
|
||||
handleVariablePatch(exchange, functionAddress, variableName, false);
|
||||
} else if ("GET".equals(exchange.getRequestMethod())) {
|
||||
handleVariableGet(exchange, functionAddress, variableName, false);
|
||||
} else {
|
||||
sendErrorResponse(exchange, 405, "Method Not Allowed");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error handling function variable operation", e);
|
||||
sendErrorResponse(exchange, 500, "Internal server error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle operations on a specific function variable by function name
|
||||
* This handles GET/PATCH operations for /functions/by-name/{functionName}/variables/{varName}
|
||||
*/
|
||||
private void handleFunctionVariableByNameOperation(HttpExchange exchange) throws IOException {
|
||||
try {
|
||||
String path = exchange.getRequestURI().getPath();
|
||||
String[] parts = path.split("/");
|
||||
|
||||
// Path should be like /functions/by-name/{functionName}/variables/{varName}
|
||||
if (parts.length < 6) {
|
||||
sendErrorResponse(exchange, 400, "Invalid URL format", "INVALID_URL_FORMAT");
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract function name from path
|
||||
String functionName = parts[3];
|
||||
String variableName = parts[5];
|
||||
|
||||
// Handle different HTTP methods
|
||||
if ("PATCH".equals(exchange.getRequestMethod())) {
|
||||
handleVariablePatch(exchange, functionName, variableName, true);
|
||||
} else if ("GET".equals(exchange.getRequestMethod())) {
|
||||
handleVariableGet(exchange, functionName, variableName, true);
|
||||
} else {
|
||||
sendErrorResponse(exchange, 405, "Method Not Allowed");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error handling function variable by name operation", e);
|
||||
sendErrorResponse(exchange, 500, "Internal server error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle GET request for a specific variable
|
||||
*/
|
||||
private void handleVariableGet(HttpExchange exchange, String functionIdentifier, String variableName, boolean isByName) throws IOException {
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) {
|
||||
sendErrorResponse(exchange, 400, "No program loaded", "NO_PROGRAM_LOADED");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the function
|
||||
Function function = null;
|
||||
if (isByName) {
|
||||
for (Function f : program.getFunctionManager().getFunctions(true)) {
|
||||
if (f.getName().equals(functionIdentifier)) {
|
||||
function = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Address address = program.getAddressFactory().getAddress(functionIdentifier);
|
||||
function = program.getFunctionManager().getFunctionAt(address);
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error getting function at address " + functionIdentifier, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (function == null) {
|
||||
sendErrorResponse(exchange, 404, "Function not found", "FUNCTION_NOT_FOUND");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find variable in function
|
||||
Map<String, Object> variableInfo = findVariableInFunction(function, variableName);
|
||||
|
||||
if (variableInfo == null) {
|
||||
sendErrorResponse(exchange, 404, "Function resource not found: variables/" + variableName, "RESOURCE_NOT_FOUND");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create HATEOAS-compliant response
|
||||
String functionPathBase = isByName ? "/functions/by-name/" + function.getName() : "/functions/" + function.getEntryPoint();
|
||||
|
||||
ResponseBuilder builder = new ResponseBuilder(exchange, port)
|
||||
.success(true)
|
||||
.addLink("self", functionPathBase + "/variables/" + variableName)
|
||||
.addLink("function", functionPathBase)
|
||||
.addLink("variables", functionPathBase + "/variables")
|
||||
.result(variableInfo);
|
||||
|
||||
sendJsonResponse(exchange, builder.build(), 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle PATCH request to update a variable (rename or change data type)
|
||||
*/
|
||||
private void handleVariablePatch(HttpExchange exchange, String functionIdentifier, String variableName, boolean isByName) throws IOException {
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) {
|
||||
sendErrorResponse(exchange, 400, "No program loaded", "NO_PROGRAM_LOADED");
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the request body to get the update data
|
||||
String requestBody = new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8);
|
||||
JsonObject jsonRequest;
|
||||
try {
|
||||
jsonRequest = new Gson().fromJson(requestBody, JsonObject.class);
|
||||
} catch (Exception e) {
|
||||
sendErrorResponse(exchange, 400, "Invalid JSON payload", "INVALID_JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the function
|
||||
Function function = null;
|
||||
if (isByName) {
|
||||
for (Function f : program.getFunctionManager().getFunctions(true)) {
|
||||
if (f.getName().equals(functionIdentifier)) {
|
||||
function = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Address address = program.getAddressFactory().getAddress(functionIdentifier);
|
||||
function = program.getFunctionManager().getFunctionAt(address);
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error getting function at address " + functionIdentifier, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (function == null) {
|
||||
sendErrorResponse(exchange, 404, "Function not found", "FUNCTION_NOT_FOUND");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we have a variable to update
|
||||
Map<String, Object> variableInfo = findVariableInFunction(function, variableName);
|
||||
if (variableInfo == null) {
|
||||
sendErrorResponse(exchange, 404, "Function resource not found: variables/" + variableName, "RESOURCE_NOT_FOUND");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a decompiler-only variable
|
||||
boolean isDecompilerOnly = Boolean.TRUE.equals(variableInfo.get("decompilerOnly"));
|
||||
|
||||
// Get requested changes
|
||||
String newName = null;
|
||||
String newDataType = null;
|
||||
|
||||
if (jsonRequest.has("name")) {
|
||||
newName = jsonRequest.get("name").getAsString();
|
||||
}
|
||||
|
||||
if (jsonRequest.has("data_type")) {
|
||||
newDataType = jsonRequest.get("data_type").getAsString();
|
||||
}
|
||||
|
||||
boolean success = false;
|
||||
String message = "";
|
||||
|
||||
try {
|
||||
if (isDecompilerOnly) {
|
||||
// For decompiler-only variables, use HighFunctionDBUtil
|
||||
success = updateDecompilerVariable(function, variableName, newName, newDataType);
|
||||
message = success ? "Updated decompiler variable" : "Failed to update decompiler variable";
|
||||
} else {
|
||||
// For regular variables, use the existing parameter/local variable mechanism
|
||||
success = updateRegularVariable(function, variableName, newName, newDataType);
|
||||
message = success ? "Updated variable" : "Failed to update variable";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error updating variable: " + e.getMessage(), e);
|
||||
sendErrorResponse(exchange, 500, "Error updating variable: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
// Get updated variable info
|
||||
Map<String, Object> updatedInfo;
|
||||
if (newName != null) {
|
||||
// If renamed, use the new name to find variable
|
||||
updatedInfo = findVariableInFunction(function, newName);
|
||||
} else {
|
||||
// Otherwise use the original name
|
||||
updatedInfo = findVariableInFunction(function, variableName);
|
||||
}
|
||||
|
||||
if (updatedInfo == null) {
|
||||
// This shouldn't happen if the update was successful
|
||||
updatedInfo = new HashMap<>();
|
||||
updatedInfo.put("message", message);
|
||||
}
|
||||
|
||||
// Create HATEOAS-compliant response
|
||||
String functionPathBase = isByName ? "/functions/by-name/" + function.getName() : "/functions/" + function.getEntryPoint();
|
||||
|
||||
ResponseBuilder builder = new ResponseBuilder(exchange, port)
|
||||
.success(true)
|
||||
.addLink("self", functionPathBase + "/variables/" + (newName != null ? newName : variableName))
|
||||
.addLink("function", functionPathBase)
|
||||
.addLink("variables", functionPathBase + "/variables")
|
||||
.result(updatedInfo);
|
||||
|
||||
sendJsonResponse(exchange, builder.build(), 200);
|
||||
} else {
|
||||
sendErrorResponse(exchange, 500, "Failed to update variable: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a regular variable (parameter or local variable)
|
||||
*/
|
||||
private boolean updateRegularVariable(Function function, String variableName, String newName, String newDataType) {
|
||||
try {
|
||||
// Find and update parameter
|
||||
for (Parameter param : function.getParameters()) {
|
||||
if (param.getName().equals(variableName)) {
|
||||
if (newName != null) {
|
||||
param.setName(newName, SourceType.USER_DEFINED);
|
||||
}
|
||||
// Updating data type for parameters would go here
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Find and update local variable (use ghidra.program.model.listing.Variable)
|
||||
for (ghidra.program.model.listing.Variable var : function.getAllVariables()) {
|
||||
if (var.getName().equals(variableName) && !(var instanceof Parameter)) {
|
||||
if (newName != null) {
|
||||
var.setName(newName, SourceType.USER_DEFINED);
|
||||
}
|
||||
// Updating data type for local variables would go here
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error updating regular variable", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a decompiler-generated variable
|
||||
*/
|
||||
private boolean updateDecompilerVariable(Function function, String variableName, String newName, String newDataType) {
|
||||
if (newName == null) {
|
||||
// Nothing to do
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Msg.info(this, "Attempting to rename decompiler variable: " + variableName + " to " + newName);
|
||||
|
||||
// This requires a decompile operation to get the HighFunction
|
||||
DecompInterface decomp = new DecompInterface();
|
||||
try {
|
||||
decomp.openProgram(getCurrentProgram());
|
||||
DecompileResults results = decomp.decompileFunction(function, 30, new ConsoleTaskMonitor());
|
||||
|
||||
if (results.decompileCompleted()) {
|
||||
HighFunction highFunc = results.getHighFunction();
|
||||
if (highFunc != null) {
|
||||
// Get the local symbol map
|
||||
LocalSymbolMap symbolMap = highFunc.getLocalSymbolMap();
|
||||
|
||||
// Find the variable in the high function
|
||||
HighSymbol symbol = null;
|
||||
Iterator<HighSymbol> symbolIter = symbolMap.getSymbols();
|
||||
while (symbolIter.hasNext()) {
|
||||
HighSymbol hs = symbolIter.next();
|
||||
if (hs.getName().equals(variableName)) {
|
||||
symbol = hs;
|
||||
Msg.info(this, "Found decompiler variable: " + variableName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (symbol != null) {
|
||||
Msg.info(this, "Starting transaction to rename: " + variableName);
|
||||
// Use transaction to update the variable
|
||||
int txId = getCurrentProgram().startTransaction("Update Decompiler Variable");
|
||||
try {
|
||||
// Rename the variable using HighFunctionDBUtil
|
||||
// This method returns void, so we assume success if no exception is thrown
|
||||
HighFunctionDBUtil.updateDBVariable(
|
||||
symbol, newName, null, SourceType.USER_DEFINED);
|
||||
|
||||
// If we reach here, it was successful
|
||||
Msg.info(this, "Successfully renamed variable to: " + newName);
|
||||
getCurrentProgram().endTransaction(txId, true);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
getCurrentProgram().endTransaction(txId, false);
|
||||
Msg.error(this, "Error updating decompiler variable: " + e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
Msg.error(this, "Could not find decompiler variable: " + variableName);
|
||||
}
|
||||
} else {
|
||||
Msg.error(this, "HighFunction is null after decompilation");
|
||||
}
|
||||
} else {
|
||||
Msg.error(this, "Decompilation did not complete successfully for function: " + function.getName());
|
||||
}
|
||||
} finally {
|
||||
decomp.dispose();
|
||||
}
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error updating decompiler variable", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a variable in the function, including decompiler-generated variables
|
||||
*/
|
||||
private Map<String, Object> findVariableInFunction(Function function, String variableName) {
|
||||
// First check regular parameters and local variables
|
||||
for (Parameter param : function.getParameters()) {
|
||||
if (param.getName().equals(variableName)) {
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("name", param.getName());
|
||||
info.put("dataType", param.getDataType().getName());
|
||||
info.put("type", "parameter");
|
||||
info.put("storage", param.getVariableStorage().toString());
|
||||
info.put("ordinal", param.getOrdinal());
|
||||
info.put("decompilerOnly", false);
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
for (ghidra.program.model.listing.Variable var : function.getAllVariables()) {
|
||||
if (var.getName().equals(variableName) && !(var instanceof Parameter)) {
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("name", var.getName());
|
||||
info.put("dataType", var.getDataType().getName());
|
||||
info.put("type", "local");
|
||||
info.put("storage", var.getVariableStorage().toString());
|
||||
info.put("decompilerOnly", false);
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
// Then check decompiler-generated variables
|
||||
try {
|
||||
// This requires a decompile operation to get the HighFunction
|
||||
DecompInterface decomp = new DecompInterface();
|
||||
try {
|
||||
decomp.openProgram(getCurrentProgram());
|
||||
DecompileResults results = decomp.decompileFunction(function, 30, new ConsoleTaskMonitor());
|
||||
|
||||
if (results.decompileCompleted()) {
|
||||
HighFunction highFunc = results.getHighFunction();
|
||||
if (highFunc != null) {
|
||||
LocalSymbolMap localSymbolMap = highFunc.getLocalSymbolMap();
|
||||
|
||||
// Check local symbol map for the variable
|
||||
Iterator<HighSymbol> symbolIter = localSymbolMap.getSymbols();
|
||||
while (symbolIter.hasNext()) {
|
||||
HighSymbol symbol = symbolIter.next();
|
||||
if (symbol.getName().equals(variableName)) {
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("name", symbol.getName());
|
||||
info.put("dataType", symbol.getDataType() != null ? symbol.getDataType().getName() : "unknown");
|
||||
info.put("type", symbol.isParameter() ? "parameter" : "local");
|
||||
info.put("decompilerOnly", true);
|
||||
info.put("pcAddress", symbol.getPCAddress() != null ? symbol.getPCAddress().toString() : "N/A");
|
||||
info.put("storage", symbol.getStorage() != null ? symbol.getStorage().toString() : "N/A");
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
decomp.dispose();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error examining decompiler variables", e);
|
||||
}
|
||||
|
||||
// Variable not found
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -364,7 +364,7 @@ public class GhidraUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about variables in a function.
|
||||
* Gets information about variables in a function, including decompiler variables.
|
||||
* @param function The function to get variables from.
|
||||
* @return A list of maps containing information about each variable.
|
||||
*/
|
||||
@ -381,10 +381,12 @@ public class GhidraUtil {
|
||||
varInfo.put("name", param.getName());
|
||||
varInfo.put("type", param.getDataType().getName());
|
||||
varInfo.put("isParameter", true);
|
||||
varInfo.put("storage", param.getVariableStorage().toString());
|
||||
varInfo.put("source", "database");
|
||||
variables.add(varInfo);
|
||||
}
|
||||
|
||||
// Add local variables
|
||||
// Add local variables from database
|
||||
for (Variable var : function.getAllVariables()) {
|
||||
if (var instanceof Parameter) {
|
||||
continue; // Skip parameters, already added
|
||||
@ -394,9 +396,63 @@ public class GhidraUtil {
|
||||
varInfo.put("name", var.getName());
|
||||
varInfo.put("type", var.getDataType().getName());
|
||||
varInfo.put("isParameter", false);
|
||||
varInfo.put("storage", var.getVariableStorage().toString());
|
||||
varInfo.put("source", "database");
|
||||
variables.add(varInfo);
|
||||
}
|
||||
|
||||
// Add decompiler-generated variables
|
||||
DecompInterface decompiler = new DecompInterface();
|
||||
try {
|
||||
decompiler.openProgram(function.getProgram());
|
||||
DecompileResults results = decompiler.decompileFunction(function, 30, TaskMonitor.DUMMY);
|
||||
|
||||
if (results.decompileCompleted()) {
|
||||
HighFunction highFunc = results.getHighFunction();
|
||||
if (highFunc != null) {
|
||||
// Iterate over local variables from decompiler
|
||||
for (java.util.Iterator<ghidra.program.model.pcode.HighSymbol> iter =
|
||||
highFunc.getLocalSymbolMap().getSymbols(); iter.hasNext(); ) {
|
||||
|
||||
ghidra.program.model.pcode.HighSymbol highSymbol = iter.next();
|
||||
|
||||
// Skip if this is already a tracked variable
|
||||
boolean alreadyAdded = false;
|
||||
for (Map<String, Object> var : variables) {
|
||||
if (var.get("name").equals(highSymbol.getName())) {
|
||||
alreadyAdded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyAdded) {
|
||||
Map<String, Object> varInfo = new HashMap<>();
|
||||
varInfo.put("name", highSymbol.getName());
|
||||
varInfo.put("type", highSymbol.getDataType() != null ?
|
||||
highSymbol.getDataType().getName() : "unknown");
|
||||
varInfo.put("isParameter", highSymbol.isParameter());
|
||||
varInfo.put("storage", highSymbol.getStorage() != null ?
|
||||
highSymbol.getStorage().toString() : "unknown");
|
||||
varInfo.put("source", "decompiler");
|
||||
|
||||
// Add PC address if available
|
||||
if (highSymbol.getPCAddress() != null) {
|
||||
varInfo.put("pcAddress", highSymbol.getPCAddress().toString());
|
||||
}
|
||||
|
||||
variables.add(varInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(GhidraUtil.class, "Error analyzing decompiler variables", e);
|
||||
}
|
||||
finally {
|
||||
decompiler.dispose();
|
||||
}
|
||||
|
||||
return variables;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user