Skip to main content
Glama
SymbolManager.java13.1 kB
package com.ghidramcp.services; import ghidra.app.decompiler.DecompInterface; import ghidra.app.decompiler.DecompileOptions; import ghidra.app.decompiler.DecompileResults; import ghidra.program.model.address.Address; import ghidra.program.model.listing.*; import ghidra.program.model.pcode.*; import ghidra.program.model.pcode.HighFunctionDBUtil.ReturnCommitOption; import ghidra.program.model.symbol.*; import ghidra.util.Msg; import ghidra.util.task.ConsoleTaskMonitor; import javax.swing.SwingUtilities; import java.lang.reflect.InvocationTargetException; import java.util.Iterator; import java.util.concurrent.atomic.AtomicBoolean; /** * Service for renaming operations (functions, data, variables) */ public class SymbolManager { private final FunctionNavigator navigator; private final int decompileTimeout; public SymbolManager(FunctionNavigator navigator, int decompileTimeout) { this.navigator = navigator; this.decompileTimeout = decompileTimeout; } /** * Rename a function by old and new names * @param oldName Current function name * @param newName New function name * @return true if successful */ public boolean renameFunction(String oldName, String newName) { if (navigator == null) return false; Program program = navigator.getCurrentProgram(); if (program == null) return false; AtomicBoolean successFlag = new AtomicBoolean(false); try { SwingUtilities.invokeAndWait(() -> { int tx = program.startTransaction("Rename function via HTTP"); try { for (Function func : program.getFunctionManager().getFunctions(true)) { if (func.getName().equals(oldName)) { // Always use applyNamespaceAndName to handle namespace correctly applyNamespaceAndName(program, func, newName); successFlag.set(true); break; } } } catch (Exception e) { Msg.error(this, "Error renaming function", e); } finally { successFlag.set(program.endTransaction(tx, successFlag.get())); } }); } catch (InterruptedException | InvocationTargetException e) { Msg.error(this, "Failed to execute rename on Swing thread", e); } return successFlag.get(); } /** * Rename a function by address * @param functionAddrStr Function address as string * @param newName New function name * @return true if successful */ public boolean renameFunctionByAddress(String functionAddrStr, String newName) { if (navigator == null) return false; Program program = navigator.getCurrentProgram(); if (program == null) return false; if (functionAddrStr == null || functionAddrStr.isEmpty() || newName == null || newName.isEmpty()) { return false; } AtomicBoolean success = new AtomicBoolean(false); try { SwingUtilities.invokeAndWait(() -> { performFunctionRename(program, functionAddrStr, newName, success); }); } catch (InterruptedException | InvocationTargetException e) { Msg.error(this, "Failed to execute rename function on Swing thread", e); } return success.get(); } /** * Rename data at a given address * @param addressStr Address as string * @param newName New name for the data * @return true if successful */ public boolean renameDataAtAddress(String addressStr, String newName) { if (navigator == null) return false; Program program = navigator.getCurrentProgram(); if (program == null) return false; AtomicBoolean success = new AtomicBoolean(false); try { SwingUtilities.invokeAndWait(() -> { int tx = program.startTransaction("Rename data"); try { Address addr = program.getAddressFactory().getAddress(addressStr); if (addr == null) { Msg.error(this, "Invalid address: " + addressStr); return; } Listing listing = program.getListing(); Data data = listing.getDefinedDataAt(addr); if (data != null) { SymbolTable symTable = program.getSymbolTable(); Symbol symbol = symTable.getPrimarySymbol(addr); if (symbol != null) { symbol.setName(newName, SourceType.USER_DEFINED); } else { symTable.createLabel(addr, newName, SourceType.USER_DEFINED); } success.set(true); } else { Msg.error(this, "No data found at address: " + addressStr); } } catch (Exception e) { Msg.error(this, "Rename data error", e); } finally { program.endTransaction(tx, success.get()); } }); } catch (InterruptedException | InvocationTargetException e) { Msg.error(this, "Failed to execute rename data on Swing thread", e); } return success.get(); } /** * Rename a variable within a function * @param functionName Function name * @param oldVarName Current variable name * @param newVarName New variable name * @return Status message */ public String renameVariableInFunction(String functionName, String oldVarName, String newVarName) { if (navigator == null) return "No program loaded"; Program program = navigator.getCurrentProgram(); if (program == null) return "No program loaded"; DecompInterface decomp = new DecompInterface(); DecompileOptions options = new DecompileOptions(); decomp.setOptions(options); decomp.openProgram(program); Function func = null; for (Function f : program.getFunctionManager().getFunctions(true)) { if (f.getName().equals(functionName)) { func = f; break; } } if (func == null) { return "Function not found"; } DecompileResults result = decomp.decompileFunction(func, this.decompileTimeout, new ConsoleTaskMonitor()); decomp.flushCache(); if (result == null || !result.decompileCompleted()) { return "Decompilation failed"; } HighFunction highFunction = result.getHighFunction(); if (highFunction == null) { return "Decompilation failed (no high function)"; } LocalSymbolMap localSymbolMap = highFunction.getLocalSymbolMap(); if (localSymbolMap == null) { return "Decompilation failed (no local symbol map)"; } HighSymbol highSymbol = null; Iterator<HighSymbol> symbols = localSymbolMap.getSymbols(); while (symbols.hasNext()) { HighSymbol symbol = symbols.next(); String symbolName = symbol.getName(); if (symbolName.equals(oldVarName)) { highSymbol = symbol; } if (symbolName.equals(newVarName)) { return "Error: A variable with name '" + newVarName + "' already exists in this function"; } } if (highSymbol == null) { return "Variable not found"; } boolean commitRequired = checkFullCommit(highSymbol, highFunction); final HighSymbol finalHighSymbol = highSymbol; final Function finalFunction = func; AtomicBoolean successFlag = new AtomicBoolean(false); try { SwingUtilities.invokeAndWait(() -> { int tx = program.startTransaction("Rename variable"); try { if (commitRequired) { HighFunctionDBUtil.commitParamsToDatabase(highFunction, false, ReturnCommitOption.NO_COMMIT, finalFunction.getSignatureSource()); } HighFunctionDBUtil.updateDBVariable( finalHighSymbol, newVarName, null, SourceType.USER_DEFINED ); successFlag.set(true); } catch (Exception e) { Msg.error(this, "Failed to rename variable", e); } finally { successFlag.set(program.endTransaction(tx, true)); } }); } catch (InterruptedException | InvocationTargetException e) { String errorMsg = "Failed to execute rename on Swing thread: " + e.getMessage(); Msg.error(this, errorMsg, e); return errorMsg; } return successFlag.get() ? "Variable renamed" : "Failed to rename variable"; } /** * Helper method to perform function rename in a transaction */ private void performFunctionRename(Program program, String functionAddrStr, String newName, AtomicBoolean success) { int tx = program.startTransaction("Rename function by address"); try { Address addr = program.getAddressFactory().getAddress(functionAddrStr); Function func = navigator.getFunctionForAddress(program, addr); if (func == null) { Msg.error(this, "Could not find function at address: " + functionAddrStr); return; } // Always use applyNamespaceAndName to handle namespace correctly applyNamespaceAndName(program, func, newName); success.set(true); } catch (Exception e) { Msg.error(this, "Error renaming function by address", e); } finally { program.endTransaction(tx, success.get()); } } /** * Apply namespace and name to a function when the name contains :: notation * @param program The program * @param func The function to rename * @param fullName The full name including namespace (e.g., "A::B" or "A::B::C") */ private void applyNamespaceAndName(Program program, Function func, String fullName) throws Exception { SymbolTable symbolTable = program.getSymbolTable(); // Split by :: to separate namespace path from function name String[] parts = fullName.split("::"); if (parts.length < 2) { // No namespace qualifier, move to global namespace Symbol funcSymbol = func.getSymbol(); funcSymbol.setNamespace(program.getGlobalNamespace()); funcSymbol.setName(fullName, SourceType.USER_DEFINED); return; } // Last part is the function name, everything before is namespace path String functionName = parts[parts.length - 1]; // Build the namespace hierarchy Namespace currentNamespace = program.getGlobalNamespace(); for (int i = 0; i < parts.length - 1; i++) { currentNamespace = symbolTable.getOrCreateNameSpace( currentNamespace, parts[i], SourceType.USER_DEFINED ); } // Get the function's symbol and update it Symbol funcSymbol = func.getSymbol(); funcSymbol.setNamespace(currentNamespace); funcSymbol.setName(functionName, SourceType.USER_DEFINED); } /** * Check if a full commit is required when renaming variables */ public static boolean checkFullCommit(HighSymbol highSymbol, HighFunction hfunction) { if (highSymbol != null && !highSymbol.isParameter()) { return false; } Function function = hfunction.getFunction(); Parameter[] parameters = function.getParameters(); LocalSymbolMap localSymbolMap = hfunction.getLocalSymbolMap(); int numParams = localSymbolMap.getNumParams(); if (numParams != parameters.length) { return true; } for (int i = 0; i < numParams; i++) { HighSymbol param = localSymbolMap.getParamSymbol(i); if (param.getCategoryIndex() != i) { return true; } VariableStorage storage = param.getStorage(); // Don't compare using the equals method so that DynamicVariableStorage can match if (0 != storage.compareTo(parameters[i].getVariableStorage())) { return true; } } return false; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/HK47196/GhidraMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server