GP-2412: Improved support for Rust binaries

This commit is contained in:
1635321 2023-08-24 00:11:20 +00:00 committed by ghidra1
parent d686733b35
commit 921247f640
33 changed files with 2627 additions and 85 deletions

View file

@ -18,6 +18,7 @@ data/ExtensionPoint.manifest||GHIDRA||||END|
data/GolangFunctionsThatDoNotReturn||GHIDRA||||END|
data/MachOFunctionsThatDoNotReturn||GHIDRA||||END|
data/PEFunctionsThatDoNotReturn||GHIDRA||||END|
data/RustFunctionsThatDoNotReturn||GHIDRA||||END|
data/base.file.extensions.icons.theme.properties||GHIDRA||||END|
data/base.icons.theme.properties||GHIDRA||||END|
data/base.listing.theme.properties||GHIDRA||||END|
@ -91,6 +92,7 @@ data/typeinfo/golang/golang_1.18_anybit_any.gdt||GHIDRA||||END|
data/typeinfo/golang/golang_1.19_anybit_any.gdt||GHIDRA||||END|
data/typeinfo/golang/golang_1.20_anybit_any.gdt||GHIDRA||||END|
data/typeinfo/mac_10.9/mac_osx.gdt||GHIDRA||||END|
data/typeinfo/rust/rust-common.gdt||GHIDRA||||END|
data/typeinfo/win32/msvcrt/clsids.txt||GHIDRA||reviewed||END|
data/typeinfo/win32/msvcrt/guids.txt||GHIDRA||reviewed||END|
data/typeinfo/win32/msvcrt/iids.txt||GHIDRA||||END|

View file

@ -0,0 +1,20 @@
# Rust function names which do not return
# (names should not start with '_' since these are stripped during checking)
ZN5alloc5alloc18handle_alloc_error17h*
ZN5alloc5alloc18handle_alloc_error8rt_error17h*
ZN5alloc7raw_vec17capacity_overflow17h*
ZN3std5alloc8rust_oom17h*
ZN3std10sys_common9backtrace26__rust_end_short_backtrace17h*
ZN4core9panicking5panic17h*
ZN4core9panicking18panic_bounds_check17h*
ZN4core9panicking9panic_fmt17h*
ZN5alloc5alloc18handle_alloc_error17h*
ZN3std7process5abort17h*
ZN3std3sys4unix14abort_internal17h*
rust_start_panic
rust_panic_cleanup
rust_foreign_exception
rust_drop_panic
rust_begin_unwind
rg_oom
abort

View file

@ -3,6 +3,9 @@
<compiler id="golang">
<functionNamesFile>GolangFunctionsThatDoNotReturn</functionNamesFile>
</compiler>
<compiler name="rustc">
<functionNamesFile>RustFunctionsThatDoNotReturn</functionNamesFile>
</compiler>
<functionNamesFile>ElfFunctionsThatDoNotReturn</functionNamesFile>
</executable_format>
<executable_format name="Mac OS X Mach-O">
@ -15,6 +18,9 @@
<compiler id="golang">
<functionNamesFile>GolangFunctionsThatDoNotReturn</functionNamesFile>
</compiler>
<compiler name="rustc">
<functionNamesFile>RustFunctionsThatDoNotReturn</functionNamesFile>
</compiler>
<functionNamesFile>PEFunctionsThatDoNotReturn</functionNamesFile>
</executable_format>
</noReturnFunctionConstraints>

View file

@ -16,9 +16,7 @@
package ghidra.app.plugin.core.analysis;
import java.io.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import generic.jar.ResourceFile;
import ghidra.app.cmd.function.CreateFunctionCmd;
@ -47,6 +45,7 @@ public class NoReturnFunctionAnalyzer extends AbstractAnalyzer {
private boolean createBookmarksEnabled = OPTION_DEFAULT_CREATE_BOOKMARKS_ENABLED;
private Set<String> functionNames;
private Set<String> wildcardFunctionNames;
public NoReturnFunctionAnalyzer() {
super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
@ -91,62 +90,19 @@ public class NoReturnFunctionAnalyzer extends AbstractAnalyzer {
name = name.substring(startIndex);
}
if (!functionNames.contains(name)) {
// check for exact match
if (functionNames.contains(name)) {
makeNoReturnFunction(program, symbol, monitor, log);
continue;
}
// skip noreturn marking if its namespace is not global, library, or std
// it prevents class methods (e.g. Menu::_exit()) incorrectly marked as noreturn
Namespace parentNamespace = symbol.getParentNamespace();
if (parentNamespace != null && !parentNamespace.isGlobal() && !parentNamespace.isLibrary()) {
List<String> pathList = parentNamespace.getPathList(true);
if (!(pathList.size() == 1 && pathList.get(0) == "std")) {
continue;
// if any wildcarded names, check for prefix match
for (String functionName : wildcardFunctionNames) {
if (name.startsWith(functionName)) {
makeNoReturnFunction(program, symbol, monitor, log);
break;
}
}
// if this is an external entry place holder, create the function in the external entry location
symbol = checkForAssociatedExternalSymbol(symbol);
if (symbol.isExternal()) {
ExternalLocation externalLocation =
program.getExternalManager().getExternalLocation(symbol);
if (externalLocation != null) {
Function functionAt = externalLocation.createFunction();
//Msg.debug(this,
// "Setting \"no return\" flag on external function " + symbol.getName(true));
functionAt.setNoReturn(true);
}
continue;
}
Address address = symbol.getAddress();
if (symbol.getSymbolType() == SymbolType.LABEL) {
if (!SymbolType.FUNCTION.isValidParent(program, parentNamespace, address, false)) {
continue; // skip if parent does not permit function creation
}
CreateFunctionCmd fCommand = new CreateFunctionCmd(address);
fCommand.applyTo(program, monitor);
}
Function functionAt = program.getFunctionManager().getFunctionAt(address);
if (functionAt == null) {
log.appendMsg("Failed to create \"no return\" function " + symbol.getName(true) +
" at " + address);
continue;
}
//Msg.debug(this, "Setting \"no return\" flag on function " + symbol.getName(true) +
// " at " + address);
functionAt.setNoReturn(true);
// disassembled later after all bad functions have been marked
if (createBookmarksEnabled) {
program.getBookmarkManager().setBookmark(address, BookmarkType.ANALYSIS,
"Non-Returning Function", "Non-Returning Function Identified");
}
}
// now that all the functions are set, safe to disassemble
@ -154,6 +110,72 @@ public class NoReturnFunctionAnalyzer extends AbstractAnalyzer {
return true;
}
/**
* Make the symbol into a non-returning function
*
* @param program program
* @param symbol symbol for a function
* @param monitor monitor
* @param log log for errors
*/
private void makeNoReturnFunction(Program program, Symbol symbol, TaskMonitor monitor,
MessageLog log) {
// skip noreturn marking if its namespace is not global, library, or std
// it prevents class methods (e.g. Menu::_exit()) incorrectly marked as noreturn
Namespace parentNamespace = symbol.getParentNamespace();
if (parentNamespace != null && !parentNamespace.isGlobal() &&
!parentNamespace.isLibrary()) {
List<String> pathList = parentNamespace.getPathList(true);
if (!(pathList.size() == 1 && pathList.get(0) == "std")) {
return;
}
}
// if this is an external entry place holder, create the function in the external entry location
symbol = checkForAssociatedExternalSymbol(symbol);
if (symbol.isExternal()) {
ExternalLocation externalLocation =
program.getExternalManager().getExternalLocation(symbol);
if (externalLocation != null) {
Function functionAt = externalLocation.createFunction();
//Msg.debug(this,
// "Setting \"no return\" flag on external function " + symbol.getName(true));
functionAt.setNoReturn(true);
}
return;
}
Address address = symbol.getAddress();
if (symbol.getSymbolType() == SymbolType.LABEL) {
if (!SymbolType.FUNCTION.isValidParent(program, parentNamespace, address, false)) {
return; // skip if parent does not permit function creation
}
CreateFunctionCmd fCommand = new CreateFunctionCmd(address);
fCommand.applyTo(program, monitor);
}
Function functionAt = program.getFunctionManager().getFunctionAt(address);
if (functionAt == null) {
log.appendMsg("Failed to create \"no return\" function " + symbol.getName(true) +
" at " + address);
return;
}
//Msg.debug(this, "Setting \"no return\" flag on function " + symbol.getName(true) +
// " at " + address);
functionAt.setNoReturn(true);
// disassembled later after all bad functions have been marked
if (createBookmarksEnabled) {
program.getBookmarkManager()
.setBookmark(address, BookmarkType.ANALYSIS, "Non-Returning Function",
"Non-Returning Function Identified");
}
}
/**
* If symbol corresponds to a pointer which references an external symbol return
* the referenced external symbol, otherwise return the symbol provided.</li>
@ -192,6 +214,7 @@ public class NoReturnFunctionAnalyzer extends AbstractAnalyzer {
}
functionNames = new HashSet<>();
wildcardFunctionNames = new HashSet<>();
ResourceFile[] files = NonReturningFunctionNames.findDataFiles(program);
for (ResourceFile file : files) {
@ -217,7 +240,16 @@ public class NoReturnFunctionAnalyzer extends AbstractAnalyzer {
"' specified in file: " + file.getAbsolutePath());
line = line.substring(startIndex);
}
functionNames.add(line.trim());
String funcName = line.trim();
if (funcName.endsWith("*")) {
// if funcName has a wildcard at the end, put on wildcard list
funcName = funcName.substring(0, funcName.length() - 1);
wildcardFunctionNames.add(funcName);
}
else {
functionNames.add(funcName);
}
}
}
finally {

View file

@ -0,0 +1,27 @@
/* ###
* 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.app.plugin.core.analysis.rust;
import ghidra.program.model.data.CategoryPath;
public class RustConstants {
public static final CategoryPath RUST_CATEGORYPATH = new CategoryPath("/rust");
public static final byte[] RUST_SIGNATURE_1 = "RUST_BACKTRACE".getBytes();
public static final byte[] RUST_SIGNATURE_2 = "/rustc/".getBytes();
public static final String RUST_EXTENSIONS_PATH = "/extensions/rust/";
public static final String RUST_EXTENSIONS_UNIX = "unix";
public static final String RUST_EXTENSIONS_WINDOWS = "windows";
}

View file

@ -0,0 +1,255 @@
/* ###
* 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.app.plugin.core.analysis.rust;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import docking.options.editor.BooleanEditor;
import ghidra.app.plugin.core.analysis.AbstractDemanglerAnalyzer;
import ghidra.app.plugin.core.analysis.rust.demangler.*;
import ghidra.app.services.AnalysisPriority;
import ghidra.app.util.demangler.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.HelpLocation;
import ghidra.util.SystemUtilities;
import ghidra.util.task.TaskMonitor;
/**
* A version of the demangler analyzer to handle Rust symbols
*/
public class RustDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
private static final String NAME = "Demangler Rust";
private static final String DESCRIPTION = "Attempt to demangle any symbols mangled by rustc.";
private static final String OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS =
"Demangle Only Known Mangled Symbols";
private static final String OPTION_DESCRIPTION_USE_KNOWN_PATTERNS =
"Only demangle symbols that follow known compiler mangling patterns. " +
"Leaving this option off may cause non-mangled symbols to get demangled.";
private static final String OPTION_NAME_APPLY_CALLING_CONVENTION =
"Apply Function Calling Conventions";
private static final String OPTION_DESCRIPTION_APPLY_CALLING_CONVENTION =
"Apply any recovered function signature calling convention";
static final String OPTION_NAME_USE_DEPRECATED_DEMANGLER = "Use Deprecated Demangler";
private static final String OPTION_DESCRIPTION_DEPRECATED_DEMANGLER =
"Use the deprecated demangler when the modern demangler cannot demangle a " +
"given string";
static final String OPTION_NAME_DEMANGLER_FORMAT = "Demangler Format";
private static final String OPTION_DESCRIPTION_DEMANGLER_FORMAT =
"The demangling format to use";
private boolean applyCallingConvention = true;
private boolean demangleOnlyKnownPatterns = true;
private RustDemanglerFormat demanglerFormat = RustDemanglerFormat.AUTO;
private boolean useDeprecatedDemangler = false;
private RustDemangler demangler = new RustDemangler();
public RustDemanglerAnalyzer() {
super(NAME, DESCRIPTION);
// Set priority to one before the default AbstractDemanglerAnalyzer priority
setPriority(AnalysisPriority.DATA_TYPE_PROPOGATION.before().before().before().before());
setDefaultEnablement(true);
}
@Override
public boolean canAnalyze(Program program) {
return demangler.canDemangle(program);
}
@Override
public void registerOptions(Options options, Program program) {
HelpLocation help = new HelpLocation("AutoAnalysisPlugin", "Demangler_Analyzer");
options.registerOption(OPTION_NAME_APPLY_CALLING_CONVENTION, applyCallingConvention, help,
OPTION_DESCRIPTION_APPLY_CALLING_CONVENTION);
options.registerOption(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns,
help, OPTION_DESCRIPTION_USE_KNOWN_PATTERNS);
BooleanEditor deprecatedEditor = null;
FormatEditor formatEditor = null;
if (!SystemUtilities.isInHeadlessMode()) {
// Only add the custom options editor when not headless. The custom editor allows
// the list of choices presented to the user to change depending on the state of the
// useDeprecatedDemangler flag.
deprecatedEditor = new BooleanEditor();
deprecatedEditor.setValue(Boolean.valueOf(useDeprecatedDemangler));
formatEditor = new FormatEditor(demanglerFormat, deprecatedEditor);
deprecatedEditor.addPropertyChangeListener(formatEditor);
}
options.registerOption(OPTION_NAME_USE_DEPRECATED_DEMANGLER, OptionType.BOOLEAN_TYPE,
useDeprecatedDemangler, help, OPTION_DESCRIPTION_DEPRECATED_DEMANGLER,
deprecatedEditor);
options.registerOption(OPTION_NAME_DEMANGLER_FORMAT, OptionType.ENUM_TYPE, demanglerFormat,
help, OPTION_DESCRIPTION_DEMANGLER_FORMAT, formatEditor);
}
@Override
public void optionsChanged(Options options, Program program) {
applyCallingConvention =
options.getBoolean(OPTION_NAME_APPLY_CALLING_CONVENTION, applyCallingConvention);
demangleOnlyKnownPatterns =
options.getBoolean(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns);
demanglerFormat = options.getEnum(OPTION_NAME_DEMANGLER_FORMAT, RustDemanglerFormat.AUTO);
useDeprecatedDemangler =
options.getBoolean(OPTION_NAME_USE_DEPRECATED_DEMANGLER, useDeprecatedDemangler);
}
@Override
protected DemanglerOptions getOptions() {
RustDemanglerOptions options =
new RustDemanglerOptions(demanglerFormat, useDeprecatedDemangler);
options.setDoDisassembly(true);
options.setApplyCallingConvention(applyCallingConvention);
options.setDemangleOnlyKnownPatterns(demangleOnlyKnownPatterns);
return options;
}
@Override
protected DemangledObject doDemangle(String mangled, DemanglerOptions demanglerOptions,
MessageLog log) throws DemangledException {
return demangler.demangle(mangled, demanglerOptions);
}
@Override
protected void apply(Program program, Address address, DemangledObject demangled,
DemanglerOptions options, MessageLog log, TaskMonitor monitor) {
try {
if (demangled instanceof DemangledFunction defunc) {
defunc.applyTo(program, address, options, monitor);
Function func = program.getFunctionManager().getFunctionAt(address);
if (func != null) {
// if has no return type and no parameters, don't trust that it is void and unlock
// the signature so the decompiler can figure it out
if (defunc.getReturnType() == null && defunc.getParameters().size() == 0) {
func.setSignatureSource(SourceType.DEFAULT);
}
return;
}
}
}
catch (Exception e) {
// Failed to apply demangled function
}
// Apply it as a variable instead of as a function
String mangled = demangled.getMangledString();
String original = demangled.getOriginalDemangled();
String name = demangled.getName();
Demangled namespace = demangled.getNamespace();
DemangledVariable demangledVariable = new DemangledVariable(mangled, original, name);
demangledVariable.setNamespace(namespace);
super.apply(program, address, demangledVariable, options, log, monitor);
}
private static class FormatEditor extends EnumEditor implements PropertyChangeListener {
private final FormatSelector selector;
private final BooleanEditor isDeprecated;
FormatEditor(RustDemanglerFormat value, BooleanEditor isDeprecated) {
setValue(value);
this.isDeprecated = isDeprecated;
this.selector = new FormatSelector(this);
}
@Override
public boolean supportsCustomEditor() {
return true;
}
@Override
public FormatSelector getCustomEditor() {
return selector;
}
@Override
public RustDemanglerFormat[] getEnums() {
return Arrays.stream(RustDemanglerFormat.values())
.filter(this::filter)
.toArray(RustDemanglerFormat[]::new);
}
@Override
public String[] getTags() {
return Arrays.stream(RustDemanglerFormat.values())
.filter(this::filter)
.map(RustDemanglerFormat::name)
.toArray(String[]::new);
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
RustDemanglerFormat format = selector.getFormat();
selector.reset(getTags());
if (format.isAvailable(isDeprecatedDemangler())) {
setValue(format);
selector.setFormat(format);
}
else {
setValue(RustDemanglerFormat.AUTO);
}
}
private boolean isDeprecatedDemangler() {
return (Boolean) isDeprecated.getValue();
}
private boolean filter(RustDemanglerFormat f) {
return f.isAvailable(isDeprecatedDemangler());
}
}
private static class FormatSelector extends PropertySelector {
public FormatSelector(FormatEditor fe) {
super(fe);
}
void reset(String[] tags) {
removeAllItems();
for (String tag : tags) {
addItem(tag);
}
}
RustDemanglerFormat getFormat() {
return RustDemanglerFormat.valueOf((String) getSelectedItem());
}
void setFormat(RustDemanglerFormat format) {
setSelectedItem(format.name());
}
}
}

View file

@ -0,0 +1,129 @@
/* ###
* 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.app.plugin.core.analysis.rust;
import ghidra.app.services.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.program.util.DefinedDataIterator;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Splits non-terminated strings into separate strings
*/
public class RustStringAnalyzer extends AbstractAnalyzer {
private final static String NAME = "Rust String Analyzer";
private final static String DESCRIPTION = "Analyzer to split rust static strings into slices";
public RustStringAnalyzer() {
super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
setPriority(AnalysisPriority.LOW_PRIORITY);
setDefaultEnablement(true);
setSupportsOneTimeAnalysis(true);
}
@Override
public boolean canAnalyze(Program program) {
String name = program.getCompiler();
return name.contains("rustc");
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
DefinedDataIterator dataIterator = DefinedDataIterator.definedStrings(program);
for (Data data : dataIterator) {
Address start = data.getAddress();
int length = data.getLength();
recurseString(program, start, length);
monitor.checkCancelled();
}
return true;
}
@Override
public void registerOptions(Options options, Program program) {
// No options
}
@Override
public void optionsChanged(Options options, Program program) {
// Options changed
}
private static void recurseString(Program program, Address start, int maxLen) {
int newLength = getMaxStringLength(program, start, maxLen);
if (newLength <= 0) {
return;
}
DataType dt =
new ArrayDataType(CharDataType.dataType, newLength, CharDataType.dataType.getLength());
try {
DataUtilities.createData(program, start, dt, 0,
DataUtilities.ClearDataMode.CLEAR_ALL_CONFLICT_DATA);
if (newLength < maxLen) {
recurseString(program, start.add(newLength), maxLen - newLength);
}
}
catch (Exception e) {
// Couldn't define string
}
}
/**
* Get the number of bytes to the next reference, or the max length
* @param program
* @param address
* @param maxLen
* @return maximum length to create the string
*/
private static int getMaxStringLength(Program program, Address address, int maxLen) {
AddressIterator refIter =
program.getReferenceManager().getReferenceDestinationIterator(address.next(), true);
Address next = refIter.next();
if (next == null) {
return -1;
}
long len = -1;
try {
len = next.subtract(address);
if (len > maxLen) {
len = maxLen;
}
return (int) len;
}
catch (IllegalArgumentException e) {
// bad address subtraction
}
return (int) len;
}
}

View file

@ -0,0 +1,106 @@
/* ###
* 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.app.plugin.core.analysis.rust;
import java.io.IOException;
import java.io.InputStream;
import org.xml.sax.SAXException;
import generic.jar.ResourceFile;
import ghidra.app.plugin.processors.sleigh.SleighException;
import ghidra.framework.Application;
import ghidra.framework.store.LockException;
import ghidra.program.database.SpecExtension;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.util.Msg;
import ghidra.util.task.TaskMonitor;
import ghidra.xml.XmlParseException;
/**
* Rust utility functions
*/
public class RustUtilities {
/**
* Checks if a given {@link Program} was written in Rust
*
* @param program The {@link Program} to check
* @param blockName The name of the {@link MemoryBlock} to scan for Rust signatures
* @return True if the given {@link Program} was written in Rust; otherwise, false
* @throws IOException if there was an IO-related error
*/
public static boolean isRust(Program program, String blockName) throws IOException {
MemoryBlock[] blocks = program.getMemory().getBlocks();
for (MemoryBlock block : blocks) {
if (block.getName().equals(blockName)) {
byte[] bytes = block.getData().readAllBytes();
if (containsBytes(bytes, RustConstants.RUST_SIGNATURE_1)) {
return true;
}
if (containsBytes(bytes, RustConstants.RUST_SIGNATURE_2)) {
return true;
}
}
}
return false;
}
public static int addExtensions(Program program, TaskMonitor monitor, String subPath)
throws IOException {
var processor = program.getLanguageCompilerSpecPair().getLanguage().getProcessor();
ResourceFile module = Application.getModuleDataSubDirectory(processor.toString(),
RustConstants.RUST_EXTENSIONS_PATH + subPath);
int extensionCount = 0;
ResourceFile[] files = module.listFiles();
for (ResourceFile file : files) {
InputStream stream = file.getInputStream();
byte[] bytes = stream.readAllBytes();
String xml = new String(bytes);
try {
SpecExtension extension = new SpecExtension(program);
extension.addReplaceCompilerSpecExtension(xml, monitor);
extensionCount += 1;
}
catch (SleighException | SAXException | XmlParseException | LockException e) {
Msg.error(program, "Failed to load load cspec extensions");
}
}
return extensionCount;
}
private static boolean containsBytes(byte[] data, byte[] bytes) {
for (int i = 0; i < data.length - bytes.length; i++) {
boolean isMatch = true;
for (int j = 0; j < bytes.length; j++) {
if (Byte.compare(data[i + j], bytes[j]) != 0) {
isMatch = false;
break;
}
}
if (isMatch) {
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,130 @@
/* ###
* 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.app.plugin.core.analysis.rust.demangler;
import ghidra.app.util.demangler.*;
import ghidra.program.model.listing.Program;
/**
* A class for demangling debug symbols created using rustc
*/
public class RustDemangler implements Demangler {
public RustDemangler() {
// needed to instantiate dynamically
}
@Override
public DemanglerOptions createDefaultOptions() {
return new RustDemanglerOptions();
}
@Override
public boolean canDemangle(Program program) {
String name = program.getCompiler();
return name.contains("rustc");
}
@Override
@Deprecated(since = "9.2", forRemoval = true)
public DemangledObject demangle(String mangled, boolean demangleOnlyKnownPatterns)
throws DemangledException {
return null;
}
@Override
public DemangledObject demangle(String mangled, DemanglerOptions options) {
RustDemanglerOptions rustOptions = getRustOptions(options);
if (skip(mangled, rustOptions)) {
return null;
}
String demangled = null;
if (rustOptions.getDemanglerFormat() == RustDemanglerFormat.LEGACY ||
rustOptions.getDemanglerFormat() == RustDemanglerFormat.AUTO) {
demangled = RustDemanglerLegacy.demangle(mangled);
}
if (rustOptions.getDemanglerFormat() == RustDemanglerFormat.V0 ||
(rustOptions.getDemanglerFormat() == RustDemanglerFormat.AUTO && demangled == null)) {
demangled = RustDemanglerV0.demangle(mangled);
}
RustDemanglerParser parser = new RustDemanglerParser();
DemangledObject demangledObject = parser.parse(mangled, demangled);
if (options.applyCallingConvention() && demangledObject instanceof DemangledFunction) {
((DemangledFunction) demangledObject).setCallingConvention("rustcall");
}
return demangledObject;
}
private RustDemanglerOptions getRustOptions(DemanglerOptions options) {
if (options instanceof RustDemanglerOptions) {
return (RustDemanglerOptions) options;
}
return new RustDemanglerOptions(options);
}
/**
* Determines if the given mangled string should not be demangled, on the basis
* of if it has a known start pattern
*
* @param mangled the mangled string
* @param options the options
* @return true if the string should not be demangled
*/
private boolean skip(String mangled, RustDemanglerOptions options) {
// The current list of demangler start patterns
if (!options.demangleOnlyKnownPatterns()) {
return false;
}
return !isRustMangled(mangled);
}
/**
* Return true if the string is a mangled rust string in a rust program
*
* @param mangled potential mangled string
* @return true if the string could be a mangled string in a rust program
*/
public static boolean isRustMangled(String mangled) {
if (mangled.startsWith("_ZN")) {
return true;
}
if (mangled.startsWith("__ZN")) {
return true;
}
if (mangled.startsWith("_R")) {
return true;
}
if (mangled.startsWith("__R")) {
return true;
}
return false;
}
}

View file

@ -0,0 +1,74 @@
/* ###
* 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.app.plugin.core.analysis.rust.demangler;
/**
* Enum representation of the available Rust demangler formats
*/
public enum RustDemanglerFormat {
/** Automatic mangling format detection */
AUTO("", Version.ALL),
/** legacy mangling format */
LEGACY("legacy", Version.ALL),
/** v0 mangling format */
V0("v0", Version.MODERN);
/** the format option string used by the native demangler */
private final String format;
private final Version version;
private RustDemanglerFormat(String format, Version version) {
this.format = format;
this.version = version;
}
/**
* Checks if this format is available in the deprecated rust demangler
* @return true if this format is available in the deprecated rust demangler
*/
public boolean isDeprecatedFormat() {
return version == Version.DEPRECATED || version == Version.ALL;
}
/**
* Checks if this format is available in a modern version of the rust demangler
* @return true if this format is available in a modern version of the rust demangler
*/
public boolean isModernFormat() {
return version == Version.MODERN || version == Version.ALL;
}
/**
* Checks if this format is available for the specified demangler
* @param isDeprecated true for the deprecated demangler, false for the modern demangler
* @return true if the format is available
*/
public boolean isAvailable(boolean isDeprecated) {
return isDeprecated ? isDeprecatedFormat() : isModernFormat();
}
/**
* Gets the format option to be passed to the demangler via the <code>-s</code> option
* @return the format option to be passed to the demangler
*/
public String getFormat() {
return format;
}
private enum Version {
DEPRECATED, MODERN, ALL
}
}

View file

@ -0,0 +1,131 @@
/* ###
* 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.app.plugin.core.analysis.rust.demangler;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RustDemanglerLegacy {
public static String demangle(String symbol) {
if (symbol.startsWith("_ZN")) { // Expected
symbol = symbol.substring(3);
}
else if (symbol.startsWith("ZN")) {
// On windows, dbghelp may strip leading underscore
symbol = symbol.substring(2);
}
else if (symbol.startsWith("__ZN")) {
// On macOS, symbols are prefixed with an extra _
symbol = symbol.substring(4);
}
else {
return null;
}
// Should only contain ASCII characters
if (!symbol.matches("\\A\\p{ASCII}*\\z")) {
return null;
}
ArrayList<String> elements = new ArrayList<String>();
char[] chars = symbol.toCharArray();
int i = 0;
while (chars[i] != 'E') {
if (chars[i] < '0' || chars[i] > '9') {
return null;
}
int l = 0;
while (chars[i + l] >= '0' && chars[i + l] <= '9') {
l += 1;
}
String lengthString = symbol.substring(i, i + l);
int length = Integer.parseInt(lengthString);
String element = symbol.substring(i + l, i + l + length);
elements.add(element);
i = i + l + length;
}
for (int j = 0; j < elements.size(); j++) {
String element = elements.get(j);
element = element.replace("$SP$", "@");
element = element.replace("$BP$", "*");
element = element.replace("$RF$", "&");
element = element.replace("$LT$", "<");
element = element.replace("$GT$", ">");
element = element.replace("$LP$", "(");
element = element.replace("$RP$", ")");
element = element.replace("$C$", ",");
int k = 0;
while (k < element.length()) {
if (element.charAt(k) == '$') {
int l = k + 1;
while (element.charAt(l) != '$') {
l += 1;
}
l += 1;
String inner = element.substring(k, l);
if (inner.startsWith("$u")) {
int num = Integer.parseInt(element.substring(k + 2, l - 1), 16);
char newChar = (char) num;
element = element.substring(0, k) + newChar + element.substring(l);
}
}
k += 1;
}
elements.set(j, element);
}
// remove the last hash
if (elements.size() > 1) {
String string = elements.get(elements.size()-1);
if (isUID(string)) {
elements.remove(elements.size()-1);
}
}
return String.join("::", elements);
}
/**
* Pattern to identify a legacy rust hash id suffix
*
* Legacy mangled rust symbols:
* - start with _ZN
* - end withe E or E.
* - have a 16 digit hash that starts with 17h
*
* The demangled string has the leading '17' and trailing 'E|E.' removed.
*
* Sample: std::io::Read::read_to_end::hb85a0f6802e14499
*/
private static final Pattern RUST_LEGACY_HASHID_PATTERN =
Pattern.compile("h[0-9a-f]{16}");
private static boolean isUID(String string) {
string = string.trim();
Matcher m = RUST_LEGACY_HASHID_PATTERN.matcher(string);
return m.matches();
}
}

View file

@ -0,0 +1,98 @@
/* ###
* 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.app.plugin.core.analysis.rust.demangler;
import ghidra.app.util.demangler.DemanglerOptions;
/**
* Rust demangler options
*/
public class RustDemanglerOptions extends DemanglerOptions {
private final RustDemanglerFormat format;
private final boolean isDeprecated;
/**
* Default constructor to use the modern demangler with auto-detect for the format. This
* constructor will limit demangling to only known symbols.
*/
public RustDemanglerOptions() {
this(RustDemanglerFormat.AUTO);
}
/**
* Constructor to specify a particular format
*
* @param format signals to use the given format
*/
public RustDemanglerOptions(RustDemanglerFormat format) {
this(format, !format.isModernFormat());
}
/**
* Constructor to specify the format to use and whether to prefer the deprecated format when
* both deprecated and modern are available
*
* @param format the format
* @param isDeprecated true if the format is not available in the modern demangler
* @throws IllegalArgumentException if the given format is not available in the deprecated
* demangler
*/
public RustDemanglerOptions(RustDemanglerFormat format, boolean isDeprecated) {
this.format = format;
this.isDeprecated = isDeprecated;
if (!format.isAvailable(isDeprecated)) {
throw new IllegalArgumentException(format.name() + " is not available");
}
}
/**
* Copy constructor to create a version of this class from a more generic set of options
* @param copy the options to copy
*/
public RustDemanglerOptions(DemanglerOptions copy) {
super(copy);
if (copy instanceof RustDemanglerOptions) {
RustDemanglerOptions gCopy = (RustDemanglerOptions) copy;
format = gCopy.format;
isDeprecated = gCopy.isDeprecated;
}
else {
format = RustDemanglerFormat.AUTO;
isDeprecated = false;
}
}
/**
* Gets the current demangler format
* @return the demangler format
*/
public RustDemanglerFormat getDemanglerFormat() {
return format;
}
@Override
public String toString() {
//@formatter:off
return "{\n" +
"\tdoDisassembly: " + doDisassembly() + ",\n" +
"\tapplySignature: " + applySignature() + ",\n" +
"\tdemangleOnlyKnownPatterns: " + demangleOnlyKnownPatterns() + ",\n" +
"}";
//@formatter:on
}
}

View file

@ -0,0 +1,195 @@
/* ###
* 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.app.plugin.core.analysis.rust.demangler;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import generic.json.Json;
import ghidra.app.util.SymbolPathParser;
import ghidra.app.util.demangler.*;
/** Parses a demangled rust string */
public class RustDemanglerParser {
private static final char NULL_CHAR = '\u0000';
private String mangledSource;
private String demangledSource;
/**
* Parses the given demangled string and creates a {@link DemangledObject}
*
* @param mangled the original mangled text
* @param demangled the demangled text
* @return the demangled object
* @throws RuntimeException if there is an unexpected error parsing
*/
public DemangledObject parse(String mangled, String demangled) throws RuntimeException {
this.mangledSource = mangled;
this.demangledSource = demangled;
return parseNext(demangled);
}
private String removeBadSpaces(String text) {
CondensedString condensedString = new CondensedString(text);
return condensedString.getCondensedText();
}
private void setNameAndNamespace(DemangledObject object, String name) {
List<String> names = SymbolPathParser.parse(name, false);
DemangledType namespace = null;
if (names.size() > 1) {
namespace = convertToNamespaces(names.subList(0, names.size() - 1));
}
String objectName = names.get(names.size() - 1);
object.setName(objectName);
object.setNamespace(namespace);
}
private DemangledObject parseNext(String demangled) {
String nameString = removeBadSpaces(demangled).trim();
DemangledFunction variable =
new DemangledFunction(mangledSource, demangledSource, (String) null);
setNameAndNamespace(variable, nameString);
return variable;
}
/**
* Converts the list of names into a namespace demangled type.
* Given names = { "A", "B", "C" }, which represents "A::B::C".
* The following will be created {@literal "Namespace{A}->Namespace{B}->Namespace{C}"}
* and Namespace{C} will be returned.
*
* <p>This method will also escape spaces separators inside of templates
* (see {@link #removeBadSpaces(String)}).
*
* @param names the names to convert
* @return the newly created type
*/
private DemangledType convertToNamespaces(List<String> names) {
if (names.isEmpty()) {
return null;
}
int index = names.size() - 1;
String rawName = names.get(index);
String escapedName = removeBadSpaces(rawName);
DemangledType myNamespace = new DemangledType(mangledSource, demangledSource, escapedName);
DemangledType namespace = myNamespace;
while (--index >= 0) {
rawName = names.get(index);
escapedName = removeBadSpaces(rawName);
DemangledType parentNamespace =
new DemangledType(mangledSource, demangledSource, escapedName);
namespace.setNamespace(parentNamespace);
namespace = parentNamespace;
}
return myNamespace;
}
/**
* A class to handle whitespace manipulation within demangled strings. This class will
* remove bad spaces, which is all whitespace that is not needed to separate distinct objects
* inside of a demangled string.
*
* <p>Generally, this class removes spaces within templates and parameter lists. It will
* remove some spaces, while converting some to underscores.
*/
private class CondensedString {
@SuppressWarnings("unused") // used by toString()
private String sourceText;
private String condensedText;
private List<Part> parts = new ArrayList<>();
CondensedString(String input) {
this.sourceText = input;
this.condensedText = convertGenericSpace(input);
}
private String convertGenericSpace(String name) {
int depth = 0;
char last = NULL_CHAR;
for (int i = 0; i < name.length(); ++i) {
Part part = new Part();
parts.add(part);
char ch = name.charAt(i);
part.original = Character.toString(ch);
part.condensed = part.original; // default case
if (ch == '<' || ch == '(') {
++depth;
}
else if ((ch == '>' || ch == ')') && depth != 0) {
--depth;
}
if (depth > 0 && ch == ' ') {
char next = (i + 1) < name.length() ? name.charAt(i + 1) : NULL_CHAR;
if (isSurroundedByCharacters(last, next)) {
// separate words with a value so they don't run together; drop the other spaces
part.condensed = "_";
}
else {
part.condensed = "";
}
}
last = ch;
}
return parts.stream().map(p -> p.condensed).collect(Collectors.joining()).trim();
}
private boolean isSurroundedByCharacters(char last, char next) {
if (last == NULL_CHAR || next == NULL_CHAR) {
return false;
}
return Character.isLetterOrDigit(last) && Character.isLetterOrDigit(next);
}
/**
* Returns the original string value that has been 'condensed', which means to remove
* internal spaces
* @return the condensed string
*/
String getCondensedText() {
return condensedText;
}
@Override
public String toString() {
return Json.toString(this);
}
private class Part {
String original;
String condensed = "";
@Override
public String toString() {
return Json.toString(this);
}
}
}
}

View file

@ -0,0 +1,934 @@
/* ###
* 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.app.plugin.core.analysis.rust.demangler;
import java.net.IDN;
import java.util.*;
/**
* A class that will demangle Rust symbols mangled according to the V0 format. This class
* implements the grammar that will translate a mangled string into a demangled one.
* Documentation is {@link "https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html"} here.
*/
public class RustDemanglerV0 {
/**
* Demangles a symbol according to the format
* @param symbol the mangled symbol name
* @return the demangled symbol name
*/
public static String demangle(String symbol) {
if (symbol.startsWith("_R")) {
symbol = symbol.substring(2);
}
else if (symbol.startsWith("R")) {
symbol = symbol.substring(1);
}
else if (symbol.startsWith("__R")) {
symbol = symbol.substring(3);
}
if (!symbol.matches("\\A\\p{ASCII}*\\z")) {
return null;
}
Symbol cursor = new Symbol(symbol);
return RustPath.parse(cursor).toString();
}
}
/**
* A class that represents a symbol in the demangling process. It keeps track of
* the current state of the symbol and implements various methods to assist with
* demangling it.
*/
class Symbol {
/** A list of backref objects */
Map<Integer, SymbolNode> backrefs = new HashMap<Integer, SymbolNode>();
/** The mangled symbol */
String mangled;
/** The current position in the mangled symbol */
int pos = 0;
/**
* Creates a symbol object
* @param mangled the mangled symbol name
*/
public Symbol(String mangled) {
this.mangled = mangled;
}
/**
* Adds a backref to the list
* @param index the index of the backref
* @param value the backref object to add
*/
public void backrefAdd(int index, SymbolNode value) {
backrefs.put(Integer.valueOf(index), value);
index += 1;
}
/**
* Gets the backref at a certain index
* @param index the index of he backref to return
* @return the backref object
*/
public String getBackref(int index) {
SymbolNode backref = backrefs.get(index);
if (backref != null) {
return backref.toString();
}
return "{backref " + index + "}";
}
/**
* Returns the number of the encoded backref
* @return the number sting
*/
public String parseBackref() {
if (stripPrefix('B')) {
return parseBase62Number();
}
return null;
}
/**
* Returns the remaining string to be demangled
* @return the mangled string
*/
public String remaining() {
return mangled.substring(pos);
}
/**
* Strips the first char of the mangled string if it's equal to the argument
* @param c the char to strip
* @return if the strip succeeded
*/
public boolean stripPrefix(char c) {
if (c == nextChar()) {
popChar();
return true;
}
return false;
}
/**
* Gets the next char in the mangled string
* @return the next char
*/
public char nextChar() {
return mangled.charAt(pos);
}
/**
* Gets the next int in the mangled string
* @return the next int
*/
public int nextInt() {
return mangled.charAt(pos);
}
/**
* Pops the next char in the mangled string
* @return the next char
*/
public char popChar() {
char c = mangled.charAt(pos);
pos += 1;
return c;
}
/**
* Parses the following numerical digits in the mangled sting
* @return the parsed integer
*/
public int parseDigits() {
String num = "";
if (nextChar() == '0') {
return 0;
}
while (nextChar() >= '0' && nextChar() <= '9') {
num += popChar();
}
return Integer.parseInt(num);
}
/**
* Parses the string until the passed char is reached
* @param c the char to parse until
* @return the parsed string
*/
public String parseUntil(char c) {
String data = "";
while (nextChar() != c) {
data += popChar();
}
return data;
}
/**
* Subtracts one from the position in the mangled string
*/
public void backChar() {
pos -= 1;
}
/**
* Parses the
* @param n number of characters
* @return the parsed string
*/
public String parseString(int n) {
String s = mangled.substring(pos, pos + n);
pos += n;
return s;
}
/**
* Returns if the end of the mangled string has been reached
* @return if the end has been reached
*/
public boolean isEmpty() {
return mangled.length() <= pos;
}
/**
* Parses the following base 62 number
* @return the parsed num string
*/
public String parseBase62Number() {
String numString = parseUntil('_');
popChar();
return numString;
}
}
/**
* A node to be used in symbol parsing
*/
interface SymbolNode {
// Parent class
}
/**
* A class to represent a nested path node
*/
class RustPathNested implements SymbolNode {
SymbolNode parent;
RustIdentifier identifier;
public RustPathNested(SymbolNode parent, RustIdentifier identifier) {
this.parent = parent;
this.identifier = identifier;
}
@Override
public String toString() {
return parent.toString() + "::" + identifier.toString();
}
}
/**
* A class to represent a string node
*/
class RustString implements SymbolNode {
String data;
public RustString(String data) {
this.data = data;
}
@Override
public String toString() {
return data;
}
}
/**
* A class that will represent and parse a backref node
*/
class RustBackref implements SymbolNode {
int backref;
Symbol s;
public RustBackref(int backref, Symbol s) {
this.backref = backref;
this.s = s;
}
@Override
public String toString() {
return s.getBackref(backref);
}
}
/**
* A class to represent and parse a rust symbol path node
*/
class RustPath implements SymbolNode {
SymbolNode child;
public RustPath(SymbolNode child) {
this.child = child;
}
public RustPath(String child) {
this.child = new RustString(child);
}
/**
* Parses a rust path from a mangled symbol
* @param s parse the rust path
* @return the rust path object
*/
public static RustPath parse(Symbol s) {
int pos = s.pos - 1;
if (s.nextChar() == 'B') {
String backref = s.parseBackref();
int i = Integer.parseInt(backref, 16);
RustBackref b = new RustBackref(i, s);
RustPath path = new RustPath(b);
return path;
}
char c = s.popChar();
if (c == 'C') {
// Crate root?
RustIdentifier identifier = RustIdentifier.parse(s, new RustNamespace("crate"));
s.backrefAdd(pos, identifier);
return new RustPath(identifier.toString());
}
else if (c == 'M') {
RustImplPath implPath = RustImplPath.parse(s);
RustType type = RustType.parse(s);
RustPath path = new RustPath("<" + implPath + "::" + type + ">");
s.backrefAdd(pos, path);
return path;
// <impl-path> <type>
// <T> (inherent impl)
}
else if (c == 'X') {
RustImplPath.parse(s);
RustType type = RustType.parse(s);
RustPath parent = RustPath.parse(s);
RustPath path = new RustPath("<" + type + " as " + parent + ">");
s.backrefAdd(pos, path);
return path;
// <impl-path> <type> <data>
// <T as Trait> (trait impl)
}
else if (c == 'Y') {
RustType type = RustType.parse(s);
RustPath parent = RustPath.parse(s);
RustPath path = new RustPath("<" + type + " as " + parent + ">");
s.backrefAdd(pos, path);
return path;
// <type> <data>
// <T as Trait> (trait definition)
}
else if (c == 'N') {
RustNamespace namespace = RustNamespace.parse(s);
RustPath parent = RustPath.parse(s);
RustIdentifier id = RustIdentifier.parse(s, namespace);
RustPathNested nested = new RustPathNested(parent, id);
RustPath path = new RustPath(nested.toString());
s.backrefAdd(pos, path);
return path;
}
else if (c == 'I') {
RustPath parent = RustPath.parse(s);
RustGenericArgs args = RustGenericArgs.parse(s);
if (args == null) {
RustPath path = new RustPath(parent);
s.backrefAdd(pos, path);
return path;
}
RustPath path = new RustPath("" + parent + args);
s.backrefAdd(pos, path);
return path;
}
else if (c == 'B') {
s.backChar();
String b = s.parseBackref();
int i = Integer.parseInt(b, 16);
RustBackref br = new RustBackref(i, s);
return new RustPath(br);
}
return null;
}
@Override
public String toString() {
return child.toString();
}
}
/**
* Parses and represents a rust symbol namespace node
*/
class RustNamespace {
String data;
public RustNamespace(String data) {
this.data = data;
}
/**
* Parses a rust namespace from a mangled symbol
* @param s symbol to parse
* @return the rust path object
*/
public static RustNamespace parse(Symbol s) {
char c = s.popChar();
if (c == 'C') {
// closure
return new RustNamespace("{closure}");
}
else if (c == 'S') {
// shim
return new RustNamespace("{shim}");
}
else if (c >= 'A' && c <= 'Z') {
// other special namespaces
return new RustNamespace(String.valueOf(c));
}
else if (c >= 'a' && c <= 'z') {
// internal namespaces
return new RustNamespace(String.valueOf(c));
}
return null;
}
@Override
public String toString() {
return data;
}
}
/**
* Parses and represents a rust symbol impl path node
*/
class RustImplPath implements SymbolNode {
RustPath path;
RustString disambiguator;
public RustImplPath(RustPath path, RustString disambiguator) {
this.path = path;
this.disambiguator = disambiguator;
}
/**
* Parses a impl rust path from a mangled symbol
* @param s symbol to parse
* @return the rust impl path object
*/
public static RustImplPath parse(Symbol s) {
RustString disambiguator = null;
if (s.nextChar() == 's') {
disambiguator = RustIdentifier.parseDisambiguator(s);
}
RustPath path = RustPath.parse(s);
return new RustImplPath(path, disambiguator);
}
@Override
public String toString() {
String s = path.toString();
if (disambiguator != null && disambiguator.toString() != "") {
s += "::" + "[" + disambiguator.toString() + "]";
}
return s;
}
}
/**
* Parses and represents an rust symbol identifier
*/
class RustIdentifier implements SymbolNode {
String id;
RustNamespace namespace;
RustString disambiguator;
public RustIdentifier(RustNamespace namespace, String id, RustString disambiguator) {
this.id = id;
this.namespace = namespace;
this.disambiguator = disambiguator;
}
/**
* Parses a rust identifier from a mangled symbol
* @param s symbol to parse
* @param namespace namespace of symbol
* @return the rust identifier object
*/
public static RustIdentifier parse(Symbol s, RustNamespace namespace) {
RustString disambiguator = null;
if (s.nextChar() == 's') {
disambiguator = parseDisambiguator(s);
}
String id = parseUndisambiguatedIdentifier(s);
return new RustIdentifier(namespace, id, disambiguator);
}
/**
* Parses a rust disambiguator from a mangled symbol
* @param s symbol to parse
* @return a string representing the disambiguator
*/
public static RustString parseDisambiguator(Symbol s) {
char c = s.popChar();
assert c == 's';
return new RustString(s.parseBase62Number());
}
/**
* Parses a rust undisambiguated identifier from a mangled symbol
* @param s symbol to parse
* @return the corresponding string object
*/
public static String parseUndisambiguatedIdentifier(Symbol s) {
boolean punycode = s.stripPrefix('u');
int num = s.parseDigits();
if (s.nextChar() == '_') {
s.popChar();
}
if (num == 0) {
char c = s.popChar();
return "{closure#" + c + "}";
}
String bytes = s.parseString(num);
if (punycode) {
return IDN.toASCII(bytes, IDN.ALLOW_UNASSIGNED);
}
return bytes;
}
@Override
public String toString() {
return id.toString();
}
}
/**
* Parses and represents rust generic arguments
*/
class RustGenericArgs implements SymbolNode {
ArrayList<RustGenericArg> args;
public RustGenericArgs(ArrayList<RustGenericArg> args) {
this.args = args;
}
/**
* Parses generics arguments from a mangled symbol
* @param s symbol to parse
* @return the rust generic arguments object
*/
public static RustGenericArgs parse(Symbol s) {
ArrayList<RustGenericArg> genericArgs = new ArrayList<RustGenericArg>();
while (s.nextChar() != 'E') {
RustGenericArg arg = RustGenericArg.parse(s);
if (arg == null) {
return null;
}
genericArgs.add(arg);
}
s.popChar();
return new RustGenericArgs(genericArgs);
}
@Override
public String toString() {
String s = "";
for (RustGenericArg arg : args) {
s += arg.toString() + ", ";
}
return "<" + s.substring(0, s.length() - 2) + ">";
}
}
/**
* Parses and represents a generic argument node in a rust symbol
*/
class RustGenericArg implements SymbolNode {
SymbolNode child;
public RustGenericArg(SymbolNode child) {
this.child = child;
}
/**
* Parses a rust generic argument from a mangled symbol
* @param s symbol to parse
* @return the rust generic argument object
*/
public static RustGenericArg parse(Symbol s) {
SymbolNode lifetime = RustLifetime.parse(s);
if (lifetime != null) {
return new RustGenericArg(lifetime);
}
if (s.nextChar() == 'K') {
s.popChar();
SymbolNode constant = RustConst.parse(s);
if (constant != null) {
return new RustGenericArg(constant);
}
}
SymbolNode type = RustType.parse(s);
if (type != null) {
return new RustGenericArg(type);
}
return null;
}
@Override
public String toString() {
return child.toString();
}
}
/**
* Parses a rust lifetime from a mangled symbol
*/
class RustLifetime implements SymbolNode {
String num;
public RustLifetime(String num) {
this.num = num;
}
/**
* Parses a rust lifetime node from a mangled symbol
* @param s symbol to parse
* @return the rust lifetime node
*/
public static SymbolNode parse(Symbol s) {
if (s.nextChar() != 'L') {
return null;
}
s.popChar();
String num = s.parseBase62Number();
if (num != null) {
return new RustLifetime(num);
}
return null;
}
@Override
public String toString() {
return num;
}
}
/**
* Parses and represents a rust symbol type node
*/
class RustType implements SymbolNode {
String typeName;
RustPath path;
public RustType(String typeName) {
this.typeName = typeName;
}
public RustType(RustPath path) {
this.path = path;
}
/**
* Parses a rust type from a mangled symbol
* @param s symbol to parse
* @return the rust type object
*/
public static RustType parse(Symbol s) {
char c = s.popChar();
switch (c) {
case 'a':
return new RustType("i8");
case 'b':
return new RustType("bool");
case 'c':
return new RustType("char");
case 'd':
return new RustType("f64");
case 'e':
return new RustType("str");
case 'f':
return new RustType("f32");
case 'h':
return new RustType("u8");
case 'i':
return new RustType("isize");
case 'j':
return new RustType("usize");
case 'l':
return new RustType("i32");
case 'm':
return new RustType("u32");
case 'n':
return new RustType("i128");
case 'o':
return new RustType("u128");
case 's':
return new RustType("i16");
case 't':
return new RustType("u16");
case 'u':
return new RustType("()");
case 'v':
return new RustType("...");
case 'x':
return new RustType("i64");
case 'y':
return new RustType("u64");
case 'z':
return new RustType("!");
case 'p':
return new RustType("_");
default:
switch (c) {
case 'A': // Array sized
RustType rustType = RustType.parse(s);
RustConst constant = RustConst.parse(s);
return new RustType(
"[" + rustType.toString() + "; " + constant.toString() + "]");
case 'S': // Array unsized
SymbolNode symbolType = RustType.parse(s);
return new RustType("[" + symbolType.toString() + "]");
case 'T': // Tuple
ArrayList<String> types = new ArrayList<String>();
while (s.nextChar() != 'E') {
SymbolNode symbolNode = RustType.parse(s);
if (symbolNode != null) {
types.add(symbolNode.toString());
}
else {
return null; // null type in parse
}
}
s.popChar();
String type = "(" + String.join(", ", types) + ")";
return new RustType(type);
case 'R': // &T
RustLifetime.parse(s);
SymbolNode type1 = RustType.parse(s);
return new RustType("&" + type1);
case 'Q': // &mut T
RustLifetime.parse(s);
SymbolNode type2 = RustType.parse(s);
return new RustType("&mut " + type2);
case 'P': // *const T
SymbolNode type3 = RustType.parse(s);
return new RustType("*const " + type3);
case 'O': // *mut T
SymbolNode type4 = RustType.parse(s);
return new RustType("*mut " + type4);
case 'F': // fn(...) -> ...
// TODO: FnSig type
case 'D': // dyn Trait<Assoc = X> + Send + 'a
String bounds = parseDynBounds(s);
RustLifetime.parse(s);
String data = "dyn Trait<Assoc = X>";
if (bounds != null) {
data += bounds;
}
return new RustType(data);
case 'B':
s.backChar();
String b1 = s.parseBackref();
int b2 = Integer.parseInt(b1);
RustBackref b3 = new RustBackref(b2, s);
return new RustType(new RustPath(b3));
default:
s.backChar();
RustPath path = RustPath.parse(s);
return new RustType(path);
}
}
}
/**
* Parses a rust dyn bounds from a mangled symbol
* @param s symbol to parse
* @return a string representing the dyn bounds
*/
public static String parseDynBounds(Symbol s) {
ArrayList<String> traits = new ArrayList<String>();
@SuppressWarnings("unused")
String binder = parseBinder(s);
while (s.nextChar() != 'E') {
String trait = parseDynTrait(s);
traits.add(trait);
}
s.popChar();
return " + " + String.join(" + ", traits);
}
/**
* Parses a rust dyn trait from a mangled symbol
* @param s symbol to parse
* @return a string representing the dyn trait
*/
public static String parseDynTrait(Symbol s) {
RustPath path = RustPath.parse(s);
@SuppressWarnings("unused")
String bindings = "";
while (s.nextChar() == 'p') {
String binding = parseDynTraitAssocBinding(s);
bindings += binding;
}
if (path == null) {
return "";
}
return path.toString();
}
/**
* Parses a rust dyn trait associated binding from a mangled symbol
* @param s symbol to parse
* @return a string representing the dyn trait associated binding
*/
public static String parseDynTraitAssocBinding(Symbol s) {
s.popChar();
RustIdentifier.parseUndisambiguatedIdentifier(s);
SymbolNode type = RustType.parse(s);
return "dyn " + type.toString();
}
/**
* Parses a rust binding from a mangled symbol
* @param s symbol to parse
* @return a string representing the binding
*/
public static String parseBinder(Symbol s) {
if (s.nextChar() != 'G') {
return null;
}
s.popChar();
return s.parseBase62Number();
}
@Override
public String toString() {
if (path != null) {
return path.toString();
}
return typeName;
}
}
/**
* Parses and represents a a rust symbol const node
*/
class RustConst implements SymbolNode {
String name;
public RustConst(String name) {
this.name = name;
}
/**
* Parses a rust const from a mangled symbol
* @param s symbol to parse
* @return the rust const object
*/
public static RustConst parse(Symbol s) {
SymbolNode type = RustType.parse(s);
String constData = RustConst.parseConstData(s);
return new RustConst(constData + type.toString());
}
/**
* Parses a rust const data from a mangled symbol
* @param s symbol to parse
* @return a string representing the const data
*/
public static String parseConstData(Symbol s) {
if (s.nextChar() == 'n') {
s.popChar();
}
String name = s.parseUntil('_');
s.popChar();
return name;
}
@Override
public String toString() {
return name;
}
}

View file

@ -40,8 +40,8 @@ public class DataTypeArchiveUtility {
public static final Map<String, ResourceFile> GHIDRA_ARCHIVES =
new HashMap<String, ResourceFile>();
static {
for (ResourceFile file : Application.findFilesByExtensionInApplication(
FileDataTypeManager.SUFFIX)) {
for (ResourceFile file : Application
.findFilesByExtensionInApplication(FileDataTypeManager.SUFFIX)) {
String name = file.getName();
ResourceFile resourceFile = GHIDRA_ARCHIVES.get(name);
if (resourceFile == null) {
@ -143,6 +143,11 @@ public class DataTypeArchiveUtility {
else {
list.add("generic_clib");
}
if (program.getCompiler().contains("rustc")) {
list.add("rust-common");
}
return list;
}

View file

@ -93,6 +93,7 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
return validationErrorStr;
}
}
return super.validateOptions(provider, loadSpec, options, program);
}
@ -102,7 +103,7 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
try {
ElfHeader elf = new ElfHeader(provider, null);
// TODO: Why do we convey image base to loader ? This will be managed by each loader !
List<QueryResult> results =
QueryOpinionService.query(getName(), elf.getMachineName(), elf.getFlags());
for (QueryResult result : results) {

View file

@ -25,6 +25,8 @@ import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
import org.apache.commons.lang3.StringUtils;
import ghidra.app.cmd.label.SetLabelPrimaryCmd;
import ghidra.app.plugin.core.analysis.rust.RustConstants;
import ghidra.app.plugin.core.analysis.rust.RustUtilities;
import ghidra.app.util.*;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.MemoryLoadable;
@ -186,6 +188,8 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
markupElfInfoProducers(monitor);
setCompiler(monitor);
success = true;
}
finally {
@ -2436,6 +2440,21 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
}
}
private void setCompiler(TaskMonitor monitor) {
// Check for Rust
try {
if (RustUtilities.isRust(program, ElfSectionHeaderConstants.dot_rodata)) {
int extensionCount = RustUtilities.addExtensions(program, monitor,
RustConstants.RUST_EXTENSIONS_UNIX);
log.appendMsg("Installed " + extensionCount + " Rust cspec extensions");
program.setCompiler("rustc");
}
}
catch (IOException e) {
log.appendException(e);
}
}
private void markupHashTable(TaskMonitor monitor) {
ElfDynamicTable dynamicTable = elf.getDynamicTable();

View file

@ -91,8 +91,8 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
}
}
catch (CancelledException e) {
return;
}
return;
}
catch (IOException e) {
throw e;
}
@ -137,7 +137,7 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
for (FatArch architecture : architectures) {
ByteProvider bp = new ByteProviderWrapper(provider, architecture.getOffset(),
architecture.getSize()) {
@Override // Ensure the parent provider gets closed when the wrapper does
public void close() throws IOException {
super.provider.close();

View file

@ -15,9 +15,12 @@
*/
package ghidra.app.util.opinion;
import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import ghidra.app.plugin.core.analysis.rust.RustConstants;
import ghidra.app.plugin.core.analysis.rust.RustUtilities;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.RelocationException;
@ -149,6 +152,7 @@ public class MachoProgramBuilder {
// Perform additional actions
renameObjMsgSendRtpSymbol();
fixupProgramTree(null); // should be done last to account for new memory blocks
setCompiler();
}
/**
@ -1687,6 +1691,22 @@ public class MachoProgramBuilder {
}
}
protected void setCompiler() {
// Check for Rust
try {
if (RustUtilities.isRust(program,
SegmentNames.SEG_TEXT + "." + SectionNames.TEXT_CONST)) {
int extensionCount = RustUtilities.addExtensions(program, monitor,
RustConstants.RUST_EXTENSIONS_UNIX);
log.appendMsg("Installed " + extensionCount + " Rust cspec extensions");
program.setCompiler("rustc");
}
}
catch (IOException e) {
log.appendException(e);
}
}
protected void renameObjMsgSendRtpSymbol()
throws DuplicateNameException, InvalidInputException {
Address address = space.getAddress(ObjectiveC1_Constants.OBJ_MSGSEND_RTP);

View file

@ -15,13 +15,14 @@
*/
package ghidra.app.util.opinion;
import java.util.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import com.google.common.primitives.Bytes;
import ghidra.app.plugin.core.analysis.rust.RustConstants;
import ghidra.app.plugin.core.analysis.rust.RustUtilities;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.Option;
import ghidra.app.util.bin.BinaryReader;
@ -83,7 +84,8 @@ public class PeLoader extends AbstractPeDebugLoader {
if (ntHeader != null && ntHeader.getOptionalHeader() != null) {
long imageBase = ntHeader.getOptionalHeader().getImageBase();
String machineName = ntHeader.getFileHeader().getMachineName();
String compilerFamily = CompilerOpinion.getOpinion(pe, provider).family;
String compilerFamily = CompilerOpinion.getOpinion(pe, provider, null,
TaskMonitor.DUMMY, new MessageLog()).family;
for (QueryResult result : QueryOpinionService.query(getName(), machineName,
compilerFamily)) {
loadSpecs.add(new LoadSpec(this, imageBase, result));
@ -146,9 +148,9 @@ public class PeLoader extends AbstractPeDebugLoader {
processSymbols(ntHeader, sectionToAddress, program, monitor, log);
processEntryPoints(ntHeader, program, monitor);
String compiler = CompilerOpinion.getOpinion(pe, provider).toString();
String compiler =
CompilerOpinion.getOpinion(pe, provider, program, monitor, log).toString();
program.setCompiler(compiler);
}
catch (AddressOverflowException e) {
throw new IOException(e);
@ -220,8 +222,7 @@ public class PeLoader extends AbstractPeDebugLoader {
return PARSE_CLI_HEADERS_OPTION_DEFAULT;
}
private void layoutHeaders(Program program, PortableExecutable pe,
NTHeader ntHeader,
private void layoutHeaders(Program program, PortableExecutable pe, NTHeader ntHeader,
DataDirectory[] datadirs) {
try {
DataType dt = pe.getDOSHeader().toDataType();
@ -379,8 +380,8 @@ public class PeLoader extends AbstractPeDebugLoader {
try {
ReferenceManager refManager = pointerData.getProgram().getReferenceManager();
refManager.addExternalReference(pointerData.getAddress(),
importInfo.getDLL().toUpperCase(),
importInfo.getName(), extAddr, SourceType.IMPORTED, 0, RefType.DATA);
importInfo.getDLL().toUpperCase(), importInfo.getName(), extAddr,
SourceType.IMPORTED, 0, RefType.DATA);
}
catch (DuplicateNameException e) {
log.appendMsg("External location not created: " + e.getMessage());
@ -868,9 +869,8 @@ public class PeLoader extends AbstractPeDebugLoader {
static final byte[] asm16_Borland =
{ (byte) 0xBA, 0x10, 0x00, 0x0E, 0x1F, (byte) 0xB4, 0x09, (byte) 0xCD, 0x21,
(byte) 0xB8, 0x01, 0x4C, (byte) 0xCD, 0x21, (byte) 0x90, (byte) 0x90 };
static final byte[] asm16_GCC_VS_Clang =
{ 0x0e, 0x1f, (byte) 0xba, 0x0e, 0x00, (byte) 0xb4, 0x09, (byte) 0xcd, 0x21,
(byte) 0xb8, 0x01, 0x4c, (byte) 0xcd, 0x21 };
static final byte[] asm16_GCC_VS_Clang = { 0x0e, 0x1f, (byte) 0xba, 0x0e, 0x00, (byte) 0xb4,
0x09, (byte) 0xcd, 0x21, (byte) 0xb8, 0x01, 0x4c, (byte) 0xcd, 0x21 };
static final byte[] THIS_BYTES = "This".getBytes();
public enum CompilerEnum {
@ -882,6 +882,7 @@ public class PeLoader extends AbstractPeDebugLoader {
BorlandCpp("borland:c++", "borlandcpp"),
BorlandUnk("borland:unknown", "borlandcpp"),
CLI("cli", "cli"),
Rustc("rustc", "rustc"),
GOLANG("golang", "golang"),
Unknown("unknown", "unknown"),
@ -926,8 +927,8 @@ public class PeLoader extends AbstractPeDebugLoader {
return (i == chararray.length);
}
public static CompilerEnum getOpinion(PortableExecutable pe, ByteProvider provider)
throws IOException {
public static CompilerEnum getOpinion(PortableExecutable pe, ByteProvider provider,
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
CompilerEnum offsetChoice = CompilerEnum.Unknown;
CompilerEnum asmChoice = CompilerEnum.Unknown;
@ -936,6 +937,19 @@ public class PeLoader extends AbstractPeDebugLoader {
DOSHeader dh = pe.getDOSHeader();
// Check for Rust. Program object is required, which may be null.
try {
if (program != null && RustUtilities.isRust(program, ".rdata")) {
int extensionCount = RustUtilities.addExtensions(program, monitor,
RustConstants.RUST_EXTENSIONS_WINDOWS);
log.appendMsg("Installed " + extensionCount + " Rust cspec extensions");
return CompilerEnum.Rustc;
}
}
catch (IOException e) {
log.appendException(e);
}
// Check for managed code (.NET)
if (pe.getNTHeader().getOptionalHeader().isCLI()) {
return CompilerEnum.CLI;
@ -1086,6 +1100,7 @@ public class PeLoader extends AbstractPeDebugLoader {
// fail
}
}
SectionHeader dataSection = pe.getNTHeader().getFileHeader().getSectionHeader(".data");
if (dataSection != null) {
try (InputStream is = dataSection.getDataStream()) {

View file

@ -0,0 +1,61 @@
/* ###
* 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.app.analyzers;
import static org.junit.Assert.*;
import org.junit.Test;
import ghidra.app.plugin.core.analysis.rust.demangler.RustDemangler;
import ghidra.app.util.demangler.DemangledException;
import ghidra.app.util.demangler.DemangledObject;
public class RustDemanglerLegacyTest {
private static String[] symbols =
{ "_ZN43_$LT$char$u20$as$u20$core..fmt..Display$GT$3fmt17h31c4c24bbd08aa24E",
"_ZN4core6option13expect_failed17h09b982639336e7eaE",
"_ZN4core3fmt9Formatter15debug_lower_hex17heb5fb064687c1b3cE",
"_ZN3std4path10Components7as_path17h3cc3e688e3107704E",
"_ZN5alloc5alloc18handle_alloc_error8rt_error17h4b79f8a717741b7cE",
"_ZN3std6thread7current17h20e47a880e55afd5E", };
private static String[] names = { "rustcall _<char_as_core..fmt..Display>::fmt(void)",
"rustcall core::option::expect_failed(void)",
"rustcall core::fmt::Formatter::debug_lower_hex(void)",
"rustcall std::path::Components::as_path(void)",
"rustcall alloc::alloc::handle_alloc_error::rt_error(void)",
"rustcall std::thread::current(void)",
"rustcall gimli::read::abbrev::Attributes::new(void)" };
@Test
public void demangle() {
RustDemangler demangler = new RustDemangler();
for (int i = 0; i < symbols.length; i++) {
String mangled = symbols[i];
String name = names[i];
try {
DemangledObject demangled = demangler.demangle(mangled);
if (!name.equals(demangled.toString())) {
fail("Demangled symbol to wrong name \n" + demangled + "\n" + name);
}
}
catch (DemangledException e) {
fail("Couldn't demangle symbol " + mangled);
}
}
}
}

View file

@ -0,0 +1,96 @@
/* ###
* 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.app.analyzers;
import static org.junit.Assert.fail;
import org.junit.Test;
import ghidra.app.plugin.core.analysis.rust.demangler.RustDemangler;
import ghidra.app.util.demangler.DemangledException;
import ghidra.app.util.demangler.DemangledObject;
public class RustDemanglerV0Test {
private static String[] symbols = {
"_RNvCsL39EUhRVRM_5tests4main",
"_RNvCsL39EUhRVRM_5tests6test_1",
"_RNvMCsL39EUhRVRM_5testsNtB2_10TestStruct8method_1",
"_RNvNtNtCsL39EUhRVRM_5tests5stuff6stuff26test_3",
"_RNCINvNtCsekVQb2M45Qb_3std2rt10lang_startuE0CsL39EUhRVRM_5tests.llvm.3070730145566890858",
"_RNvYNtNtCsheJZGYyU57U_5alloc6string6StringNtNtCscuN2HtZYDVi_4core3fmt5Write9write_fmtCsL39EUhRVRM_5tests",
"_RNSNvYNCINvNtCsekVQb2M45Qb_3std2rt10lang_startuE0INtNtNtCscuN2HtZYDVi_4core3ops8function6FnOnceuE9call_once6vtableCsL39EUhRVRM_5tests.llvm.3070730145566890858",
"_RNvXss_NtCsheJZGYyU57U_5alloc3vecINtB5_3VecAhj4_ENtNtCscuN2HtZYDVi_4core3fmt5Debug3fmtCsL39EUhRVRM_5tests",
"_RNSNvYNCINvNtCsekVQb2M45Qb_3std2rt10lang_startuE0INtNtNtCscuN2HtZYDVi_4core3ops8function6FnOnceuE9call_once6vtableCsL39EUhRVRM_5tests.llvm.6912067296627029035",
"_RNvXs_CsL39EUhRVRM_5testsNtB4_10TestStructNtB4_9TestTrait14trait_method_1",
"_RNvXs_CsL39EUhRVRM_5testsNtB4_10TestStructNtB4_9TestTrait14trait_method_2",
"_RNvXs_CsL39EUhRVRM_5testsNtB4_10TestStructNtB4_9TestTrait14trait_method_3",
"_RNvXNtCscuN2HtZYDVi_4core3fmtQNtNtCsheJZGYyU57U_5alloc6string6StringNtB2_5Write10write_charCsL39EUhRVRM_5tests",
"_RNvXNtCscuN2HtZYDVi_4core3fmtQNtNtCsheJZGYyU57U_5alloc6string6StringNtB2_5Write9write_fmtCsL39EUhRVRM_5tests",
"_RNvXNtCscuN2HtZYDVi_4core3fmtQNtNtCsheJZGYyU57U_5alloc6string6StringNtB2_5Write9write_strCsL39EUhRVRM_5tests",
"_RNvNtCsL39EUhRVRM_5tests5stuff6test_2",
"_RNvMs_NtCsheJZGYyU57U_5alloc7raw_vecINtB4_6RawVechE16reserve_for_pushCsL39EUhRVRM_5tests",
"_RNvXsX_NtCscuN2HtZYDVi_4core3fmtReNtB5_7Display3fmtCsL39EUhRVRM_5tests",
"_RNvXsV_NtCscuN2HtZYDVi_4core3fmtRAhj4_NtB5_5Debug3fmtCsL39EUhRVRM_5tests",
"_RNvXsV_NtCscuN2HtZYDVi_4core3fmtRhNtB5_5Debug3fmtCsL39EUhRVRM_5tests",
"_RNvYNtNtCsheJZGYyU57U_5alloc6string6StringNtNtCscuN2HtZYDVi_4core3fmt5Write9write_fmtCsL39EUhRVRM_5tests",
"_RNCINkXs25_NgCsbmNqQUJIY6D_4core5sliceINyB9_4IterhENuNgNoBb_4iter8iterator8Iterator9rpositionNCNgNpB9_6memchr7memrchrs_0E0Bb_",
};
private static String[] names = {
"tests::main",
"tests::test_1",
"<tests::tests::TestStruct>::method_1",
"tests::stuff::stuff2::test_3",
"std::rt::lang_start<()>::{closure#0}",
"<alloc::string::String as core::fmt::Write>::write_fmt",
"<std::rt::lang_start<()>::{closure#0} as core::ops::function::FnOnce<()>>::call_once::vtable",
"<alloc::vec::Vec<[u8; 4usize]> as core::fmt::Debug>::fmt",
"<std::rt::lang_start<()>::{closure#0} as core::ops::function::FnOnce<()>>::call_once::vtable",
"<tests::TestStruct as tests::TestTrait>::trait_method_1",
"<tests::TestStruct as tests::TestTrait>::trait_method_2",
"<tests::TestStruct as tests::TestTrait>::trait_method_3",
"<&mut alloc::string::String as core::fmt::Write>::write_char",
"<&mut alloc::string::String as core::fmt::Write>::write_fmt",
"<&mut alloc::string::String as core::fmt::Write>::write_str",
"tests::stuff::test_2",
"<alloc::raw_vec::alloc::raw_vec::RawVec<u8>>::reserve_for_push",
"<&str as core::fmt::Display>::fmt",
"<&[u8; 4usize] as core::fmt::Debug>::fmt",
"<&u8 as core::fmt::Debug>::fmt",
"<alloc::string::String as core::fmt::Write>::write_fmt",
"<core::slice::Iter<u8> as core::iter::iterator::Iterator>::rposition<core::slice::memchr::memrchr::{closure#0}>::{closure#0}",
};
@Test
public void demangle() {
RustDemangler demangler = new RustDemangler();
for (int i = 0; i < symbols.length; i++) {
String mangled = symbols[i];
String name = names[i];
try {
DemangledObject demangled = demangler.demangle(mangled);
if (name.equals(demangled.getName())) {
fail("Demangled symbol to wrong name " + mangled);
}
} catch (DemangledException e) {
fail("Couldn't demangle symbol " + mangled);
}
}
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,8 +15,11 @@
*/
package ghidra.util.constraint;
import java.util.Objects;
import generic.constraint.ConstraintData;
import ghidra.program.model.listing.Program;
import ghidra.util.xml.XmlAttributeException;
public class CompilerConstraint extends ProgramConstraint {
@ -26,15 +28,41 @@ public class CompilerConstraint extends ProgramConstraint {
}
private String compilerid;
private String compilerName;
@Override
public boolean isSatisfied(Program program) {
return compilerid.equals(program.getCompilerSpec().getCompilerSpecID().getIdAsString());
if (compilerid == null && compilerName == null) {
return false;
}
boolean satisfied = true;
if (compilerid != null) {
satisfied &=
compilerid.equals(program.getCompilerSpec().getCompilerSpecID().getIdAsString());
}
if (compilerName != null) {
satisfied &= compilerName.contains(program.getCompiler());
}
return satisfied;
}
@Override
public void loadConstraintData(ConstraintData data) {
compilerid = data.getString("id");
if (data.hasValue("id")) {
compilerid = data.getString("id");
}
if (data.hasValue("name")) {
compilerName = data.getString("name");
}
if (compilerid == null && compilerName == null) {
throw new XmlAttributeException("Missing both id and name attributes");
}
}
@Override
@ -42,12 +70,32 @@ public class CompilerConstraint extends ProgramConstraint {
if (!(obj instanceof CompilerConstraint)) {
return false;
}
return ((CompilerConstraint) obj).compilerid.equals(compilerid);
CompilerConstraint constraint = (CompilerConstraint) obj;
if (compilerid != constraint.compilerid) {
if (compilerid == null || !compilerid.equals(constraint.compilerid)) {
return false;
}
}
if (compilerName != constraint.compilerName) {
if (compilerName == null || !compilerName.equals(constraint.compilerName)) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
return Objects.hash(compilerid, compilerName);
}
@Override
public String getDescription() {
return "compiler = " + compilerid;
return "compiler = " + compilerid + " compilerName = " + compilerName;
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +15,8 @@
*/
package ghidra.util.constraint;
import java.util.Objects;
import generic.constraint.ConstraintData;
import ghidra.program.model.listing.Program;
@ -49,6 +50,11 @@ public class ExecutableFormatConstraint extends ProgramConstraint {
return ((ExecutableFormatConstraint) obj).executableFormat.equals(executableFormat);
}
@Override
public int hashCode() {
return Objects.hash(executableFormat);
}
@Override
public String getDescription() {
return "executableFormat = " + executableFormat;

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,11 +15,12 @@
*/
package ghidra.util.constraint;
import java.util.Objects;
import java.util.StringTokenizer;
import generic.constraint.ConstraintData;
import ghidra.program.model.listing.Program;
import java.util.StringTokenizer;
public class LanguageConstraint extends ProgramConstraint {
public LanguageConstraint() {
@ -63,6 +63,11 @@ public class LanguageConstraint extends ProgramConstraint {
return ((LanguageConstraint) obj).languageID.equals(languageID);
}
@Override
public int hashCode() {
return Objects.hash(languageID);
}
@Override
public String getDescription() {
return "languageID = " + languageID;

View file

@ -1,5 +1,10 @@
##VERSION: 2.0
Module.manifest||GHIDRA||||END|
data/extensions/rust/unix/cc.xml||GHIDRA||||END|
data/extensions/rust/unix/probe_fixup.xml||GHIDRA||||END|
data/extensions/rust/unix/try_fixup.xml||GHIDRA||||END|
data/extensions/rust/windows/probe_fixup.xml||GHIDRA||||END|
data/extensions/rust/windows/try_fixup.xml||GHIDRA||||END|
data/languages/adx.sinc||GHIDRA||||END|
data/languages/avx.sinc||GHIDRA||||END|
data/languages/avx2.sinc||GHIDRA||||END|

View file

@ -0,0 +1,88 @@
<prototype name="rustcall" extrapop="8" stackshift="8">
<input pointermax="8">
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM0_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM1_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM2_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM3_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM4_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM5_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM6_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM7_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RDI"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RSI"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RDX"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RCX"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R8"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R9"/>
</pentry>
<pentry minsize="1" maxsize="500" align="8">
<addr offset="8" space="stack"/>
</pentry>
<rule>
<datatype name="any" minsize="9"/>
<join align="true"/> <!-- Chunk from general purpose registers -->
</rule>
</input>
<output>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM0_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RAX"/>
</pentry>
<pentry minsize="9" maxsize="16">
<addr space="join" piece1="RDX" piece2="RAX"/>
</pentry>
</output>
<killedbycall>
<register name="RAX"/>
<register name="RDX"/>
<register name="XMM0"/>
</killedbycall>
<unaffected>
<register name="RBX"/>
<register name="RSP"/>
<register name="RBP"/>
<register name="R12"/>
<register name="R13"/>
<register name="R14"/>
<register name="R15"/>
<register name="XMM6"/>
<register name="XMM7"/>
<register name="XMM8"/>
<register name="XMM9"/>
<register name="XMM10"/>
<register name="XMM11"/>
<register name="XMM12"/>
<register name="XMM13"/>
<register name="XMM14"/>
<register name="XMM15"/>
</unaffected>
</prototype>

View file

@ -0,0 +1,8 @@
<callfixup name="__rust_probestack">
<target name="__rust_probestack"/>
<pcode>
<body><![CDATA[
temp:1 = 0;
]]></body>
</pcode>
</callfixup>

View file

@ -0,0 +1,8 @@
<callfixup name="__rust_try">
<target name="__rust_try"/>
<pcode>
<body><![CDATA[
call [RDI];
]]></body>
</pcode>
</callfixup>

View file

@ -0,0 +1,8 @@
<callfixup name="__rust_probestack">
<target name="__rust_probestack"/>
<pcode>
<body><![CDATA[
temp:1 = 0;
]]></body>
</pcode>
</callfixup>

View file

@ -0,0 +1,8 @@
<callfixup name="__rust_try">
<target name="__rust_try"/>
<pcode>
<body><![CDATA[
call [RAX];
]]></body>
</pcode>
</callfixup>

View file

@ -34,9 +34,11 @@
<constraint primary="334" processor="x86" endian="little" size="32" />
<constraint primary="34404" processor="x86" endian="little" size="64" variant="default" />
</constraint>
<constraint loader="Executable and Linking Format (ELF)" compilerSpecID="gcc">
<constraint loader="Executable and Linking Format (ELF)">
<constraint compilerSpecID="gcc">
<constraint primary="3" processor="x86" endian="little" size="32" />
<constraint primary="62" processor="x86" endian="little" size="64" variant="default" />
</constraint>
</constraint>
<constraint loader="Module Definition (DEF)" compilerSpecID="windows">
<constraint primary="0" processor="x86" endian="little" size="32" />