mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-09-18 01:31:53 +00:00
GP-2412: Improved support for Rust binaries
This commit is contained in:
parent
d686733b35
commit
921247f640
|
@ -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|
|
||||
|
|
20
Ghidra/Features/Base/data/RustFunctionsThatDoNotReturn
Normal file
20
Ghidra/Features/Base/data/RustFunctionsThatDoNotReturn
Normal 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
|
|
@ -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>
|
||||
|
|
BIN
Ghidra/Features/Base/data/typeinfo/rust/rust-common.gdt
Normal file
BIN
Ghidra/Features/Base/data/typeinfo/rust/rust-common.gdt
Normal file
Binary file not shown.
|
@ -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 {
|
||||
|
|
|
@ -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";
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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|
|
||||
|
|
88
Ghidra/Processors/x86/data/extensions/rust/unix/cc.xml
Normal file
88
Ghidra/Processors/x86/data/extensions/rust/unix/cc.xml
Normal 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>
|
|
@ -0,0 +1,8 @@
|
|||
<callfixup name="__rust_probestack">
|
||||
<target name="__rust_probestack"/>
|
||||
<pcode>
|
||||
<body><![CDATA[
|
||||
temp:1 = 0;
|
||||
]]></body>
|
||||
</pcode>
|
||||
</callfixup>
|
|
@ -0,0 +1,8 @@
|
|||
<callfixup name="__rust_try">
|
||||
<target name="__rust_try"/>
|
||||
<pcode>
|
||||
<body><![CDATA[
|
||||
call [RDI];
|
||||
]]></body>
|
||||
</pcode>
|
||||
</callfixup>
|
|
@ -0,0 +1,8 @@
|
|||
<callfixup name="__rust_probestack">
|
||||
<target name="__rust_probestack"/>
|
||||
<pcode>
|
||||
<body><![CDATA[
|
||||
temp:1 = 0;
|
||||
]]></body>
|
||||
</pcode>
|
||||
</callfixup>
|
|
@ -0,0 +1,8 @@
|
|||
<callfixup name="__rust_try">
|
||||
<target name="__rust_try"/>
|
||||
<pcode>
|
||||
<body><![CDATA[
|
||||
call [RAX];
|
||||
]]></body>
|
||||
</pcode>
|
||||
</callfixup>
|
|
@ -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" />
|
||||
|
|
Loading…
Reference in a new issue