mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-02-16 15:40:14 +00:00
GP-2425 more better duffzero / duffcopy function info
This commit is contained in:
parent
176bdea28a
commit
b5422faefb
@ -18,30 +18,30 @@ package ghidra.app.plugin.core.analysis;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.bin.format.dwarf4.DWARFUtil;
|
||||
import ghidra.app.util.bin.format.elf.info.ElfInfoItem.ItemWithAddress;
|
||||
import ghidra.app.util.bin.format.golang.*;
|
||||
import ghidra.app.util.bin.format.golang.rtti.GoModuledata;
|
||||
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
|
||||
import ghidra.app.util.bin.format.golang.rtti.*;
|
||||
import ghidra.app.util.bin.format.golang.structmapping.MarkupSession;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.PrototypeModel;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.listing.Function.FunctionUpdateType;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.task.UnknownProgressWrappingTaskMonitor;
|
||||
import ghidra.xml.XmlParseException;
|
||||
@ -73,34 +73,35 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||
throws CancelledException {
|
||||
monitor.setMessage("Golang symbol analyzer");
|
||||
|
||||
try (GoRttiMapper programContext = GoRttiMapper.getMapperFor(program, log)) {
|
||||
if (programContext == null) {
|
||||
try (GoRttiMapper goBinary = GoRttiMapper.getMapperFor(program, log)) {
|
||||
if (goBinary == null) {
|
||||
Msg.error(this, "Golang analyzer error: unable to get GoRttiMapper");
|
||||
return false;
|
||||
}
|
||||
programContext.discoverGoTypes(monitor);
|
||||
|
||||
GoModuledata firstModule = programContext.getFirstModule();
|
||||
|
||||
goBinary.init(monitor);
|
||||
goBinary.discoverGoTypes(monitor);
|
||||
|
||||
UnknownProgressWrappingTaskMonitor upwtm =
|
||||
new UnknownProgressWrappingTaskMonitor(monitor, 100);
|
||||
upwtm.initialize(0);
|
||||
upwtm.setMessage("Marking up Golang RTTI structures");
|
||||
|
||||
MarkupSession markupSession = programContext.createMarkupSession(upwtm);
|
||||
MarkupSession markupSession = goBinary.createMarkupSession(upwtm);
|
||||
GoModuledata firstModule = goBinary.getFirstModule();
|
||||
if (firstModule != null) {
|
||||
markupSession.labelStructure(firstModule, "firstmoduledata");
|
||||
markupSession.markup(firstModule, false);
|
||||
}
|
||||
|
||||
markupSession.labelStructure(firstModule, "firstmoduledata");
|
||||
markupSession.markup(firstModule, false);
|
||||
|
||||
markupMiscInfoStructs(program);
|
||||
markupWellknownSymbols(programContext, markupSession);
|
||||
markupWellknownSymbols(goBinary, markupSession);
|
||||
setupProgramContext(goBinary, markupSession);
|
||||
goBinary.recoverDataTypes(monitor);
|
||||
markupGoFunctions(goBinary, markupSession);
|
||||
fixupNoReturnFuncs(program);
|
||||
setupProgramContext(programContext, markupSession);
|
||||
programContext.recoverDataTypes(monitor);
|
||||
markupMiscInfoStructs(program);
|
||||
|
||||
if (analyzerOptions.createBootstrapDatatypeArchive) {
|
||||
createBootstrapGDT(programContext, program, monitor);
|
||||
createBootstrapGDT(goBinary, program, monitor);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -124,23 +125,103 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||
analyzerOptions.createBootstrapDatatypeArchive);
|
||||
}
|
||||
|
||||
private void markupWellknownSymbols(GoRttiMapper programContext, MarkupSession session)
|
||||
private void markupWellknownSymbols(GoRttiMapper goBinary, MarkupSession session)
|
||||
throws IOException {
|
||||
Program program = programContext.getProgram();
|
||||
|
||||
Program program = goBinary.getProgram();
|
||||
|
||||
Symbol g0 = SymbolUtilities.getUniqueSymbol(program, "runtime.g0");
|
||||
Structure gStruct = programContext.getGhidraDataType("runtime.g", Structure.class);
|
||||
Structure gStruct = goBinary.getGhidraDataType("runtime.g", Structure.class);
|
||||
if (g0 != null && gStruct != null) {
|
||||
session.markupAddressIfUndefined(g0.getAddress(), gStruct);
|
||||
}
|
||||
|
||||
|
||||
Symbol m0 = SymbolUtilities.getUniqueSymbol(program, "runtime.m0");
|
||||
Structure mStruct = programContext.getGhidraDataType("runtime.m", Structure.class);
|
||||
Structure mStruct = goBinary.getGhidraDataType("runtime.m", Structure.class);
|
||||
if (m0 != null && mStruct != null) {
|
||||
session.markupAddressIfUndefined(m0.getAddress(), mStruct);
|
||||
}
|
||||
}
|
||||
|
||||
private void markupGoFunctions(GoRttiMapper goBinary, MarkupSession markupSession)
|
||||
throws IOException {
|
||||
for (GoFuncData funcdata : goBinary.getAllFunctions()) {
|
||||
String funcname = SymbolUtilities.replaceInvalidChars(funcdata.getName(), true);
|
||||
markupSession.createFunctionIfMissing(funcname, funcdata.getFuncAddress());
|
||||
}
|
||||
try {
|
||||
fixDuffFunctions(goBinary, markupSession);
|
||||
}
|
||||
catch (InvalidInputException | DuplicateNameException e) {
|
||||
Msg.error(this, "Error configuring duff functions", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes the function signature of the runtime.duffzero and runtime.duffcopy functions.
|
||||
* <p>
|
||||
* The alternate duff-ified entry points haven't been discovered yet, so the information
|
||||
* set to the main function entry point will be propagated at a later time to the alternate
|
||||
* entry points by the GolangDuffFixupAnalyzer.
|
||||
*
|
||||
* @param goBinary the golang binary
|
||||
* @param session {@link MarkupSession}
|
||||
* @throws InvalidInputException if error assigning the function signature
|
||||
* @throws DuplicateNameException if error assigning the function signature
|
||||
*/
|
||||
private void fixDuffFunctions(GoRttiMapper goBinary, MarkupSession session)
|
||||
throws InvalidInputException, DuplicateNameException {
|
||||
Program program = goBinary.getProgram();
|
||||
GoRegisterInfo regInfo = goBinary.getRegInfo();
|
||||
DataType voidPtr = program.getDataTypeManager().getPointer(VoidDataType.dataType);
|
||||
DataType uintDT = goBinary.getTypeOrDefault("uint", DataType.class,
|
||||
AbstractUnsignedIntegerDataType.getUnsignedDataType(goBinary.getPtrSize(), null));
|
||||
|
||||
GoFuncData duffzeroFuncdata = goBinary.getFunctionByName("runtime.duffzero");
|
||||
Function duffzeroFunc = duffzeroFuncdata != null
|
||||
? program.getFunctionManager().getFunctionAt(duffzeroFuncdata.getFuncAddress())
|
||||
: null;
|
||||
PrototypeModel duffzeroCC = goBinary.getDuffzeroCallingConvention();
|
||||
if (duffzeroFunc != null && duffzeroCC != null) {
|
||||
// NOTE: some duffzero funcs need a zero value supplied to them via a register set
|
||||
// by the caller. (depending on the arch) The duffzero calling convention defined
|
||||
// by the callspec should take care of this by defining that register as the second
|
||||
// storage location. Otherwise, the callspec will only have a single storage
|
||||
// location defined.
|
||||
boolean needZeroValueParam = regInfo.getZeroRegister() == null;
|
||||
List<Variable> params = new ArrayList<>();
|
||||
params.add(new ParameterImpl("dest", voidPtr, program));
|
||||
if (needZeroValueParam) {
|
||||
params.add(new ParameterImpl("zeroValue", uintDT, program));
|
||||
}
|
||||
|
||||
duffzeroFunc.updateFunction(duffzeroCC.getName(),
|
||||
new ReturnParameterImpl(VoidDataType.dataType, program), params,
|
||||
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true,
|
||||
SourceType.ANALYSIS);
|
||||
|
||||
DWARFUtil.appendComment(program, duffzeroFunc.getEntryPoint(), CodeUnit.PLATE_COMMENT,
|
||||
"Golang special function: ", "duffzero", "\n");
|
||||
}
|
||||
|
||||
GoFuncData duffcopyFuncdata = goBinary.getFunctionByName("runtime.duffcopy");
|
||||
Function duffcopyFunc = duffcopyFuncdata != null
|
||||
? program.getFunctionManager().getFunctionAt(duffcopyFuncdata.getFuncAddress())
|
||||
: null;
|
||||
PrototypeModel duffcopyCC = goBinary.getDuffcopyCallingConvention();
|
||||
if (duffcopyFuncdata != null && duffcopyCC != null) {
|
||||
List<Variable> params = List.of(
|
||||
new ParameterImpl("dest", voidPtr, program),
|
||||
new ParameterImpl("src", voidPtr, program));
|
||||
duffcopyFunc.updateFunction(duffcopyCC.getName(),
|
||||
new ReturnParameterImpl(VoidDataType.dataType, program), params,
|
||||
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.ANALYSIS);
|
||||
|
||||
DWARFUtil.appendComment(program, duffcopyFunc.getEntryPoint(), CodeUnit.PLATE_COMMENT,
|
||||
"Golang special function: ", "duffcopy", "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void markupMiscInfoStructs(Program program) {
|
||||
// this also adds "golang" info to program properties
|
||||
|
||||
@ -229,15 +310,14 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||
return newMB.getStart();
|
||||
}
|
||||
|
||||
private void setupProgramContext(GoRttiMapper programContext, MarkupSession session)
|
||||
private void setupProgramContext(GoRttiMapper goBinary, MarkupSession session)
|
||||
throws IOException {
|
||||
Program program = programContext.getProgram();
|
||||
GoRegisterInfo goRegInfo = GoRegisterInfoManager.getInstance()
|
||||
.getRegisterInfoForLang(program.getLanguage(),
|
||||
programContext.getGolangVersion());
|
||||
Program program = goBinary.getProgram();
|
||||
GoRegisterInfo goRegInfo = goBinary.getRegInfo();
|
||||
|
||||
MemoryBlock txtMemblock = program.getMemory().getBlock(".text");
|
||||
if (txtMemblock != null && goRegInfo.getZeroRegister() != null) {
|
||||
if (txtMemblock != null && goRegInfo.getZeroRegister() != null &&
|
||||
!goRegInfo.isZeroRegisterIsBuiltin()) {
|
||||
try {
|
||||
program.getProgramContext()
|
||||
.setValue(goRegInfo.getZeroRegister(), txtMemblock.getStart(),
|
||||
@ -248,9 +328,9 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||
}
|
||||
}
|
||||
|
||||
int alignment = programContext.getPtrSize();
|
||||
int alignment = goBinary.getPtrSize();
|
||||
long sizeNeeded = 0;
|
||||
|
||||
|
||||
Symbol zerobase = SymbolUtilities.getUniqueSymbol(program, "runtime.zerobase");
|
||||
long zerobaseSymbol = sizeNeeded;
|
||||
sizeNeeded += zerobase == null
|
||||
@ -258,13 +338,13 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||
: 0;
|
||||
|
||||
long gStructOffset = sizeNeeded;
|
||||
Structure gStruct = programContext.getGhidraDataType("runtime.g", Structure.class);
|
||||
Structure gStruct = goBinary.getGhidraDataType("runtime.g", Structure.class);
|
||||
sizeNeeded += gStruct != null
|
||||
? NumericUtilities.getUnsignedAlignedValue(gStruct.getLength(), alignment)
|
||||
: 0;
|
||||
|
||||
long mStructOffset = sizeNeeded;
|
||||
Structure mStruct = programContext.getGhidraDataType("runtime.m", Structure.class);
|
||||
Structure mStruct = goBinary.getGhidraDataType("runtime.m", Structure.class);
|
||||
sizeNeeded += mStruct != null
|
||||
? NumericUtilities.getUnsignedAlignedValue(mStruct.getLength(), alignment)
|
||||
: 0;
|
||||
@ -304,16 +384,16 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||
}
|
||||
}
|
||||
|
||||
private void createBootstrapGDT(GoRttiMapper programContext, Program program,
|
||||
private void createBootstrapGDT(GoRttiMapper goBinary, Program program,
|
||||
TaskMonitor monitor) throws IOException {
|
||||
GoVer goVer = programContext.getGolangVersion();
|
||||
GoVer goVer = goBinary.getGolangVersion();
|
||||
String osName = GoRttiMapper.getGolangOSString(program);
|
||||
String gdtFilename =
|
||||
GoRttiMapper.getGDTFilename(goVer, programContext.getPtrSize(), osName);
|
||||
GoRttiMapper.getGDTFilename(goVer, goBinary.getPtrSize(), osName);
|
||||
gdtFilename =
|
||||
gdtFilename.replace(".gdt", "_%d.gdt".formatted(System.currentTimeMillis()));
|
||||
File gdt = new File(System.getProperty("user.home"), gdtFilename);
|
||||
programContext.exportTypesToGDT(gdt, monitor);
|
||||
goBinary.exportTypesToGDT(gdt, monitor);
|
||||
Msg.info(this, "Golang bootstrap GDT created: " + gdt);
|
||||
}
|
||||
|
||||
@ -323,7 +403,7 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||
|
||||
@Override
|
||||
public boolean canAnalyze(Program program) {
|
||||
return "golang".equals(
|
||||
return GoConstants.GOLANG_CSPEC_NAME.equals(
|
||||
program.getCompilerSpec().getCompilerSpecDescription().getCompilerSpecName());
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
||||
"a newly discovered starting symbol, provided the user hasn't manually moved.";
|
||||
|
||||
private static final String DEFAULT_STARTING_SYMBOLS =
|
||||
"main, WinMain, libc_start_main, WinMainStartup, start, entry, main.main";
|
||||
"main, WinMain, libc_start_main, WinMainStartup, main.main, start, entry";
|
||||
|
||||
public static enum StartLocationType {
|
||||
LOWEST_ADDRESS("Lowest Address"),
|
||||
|
@ -21,8 +21,16 @@ import ghidra.program.model.data.CategoryPath;
|
||||
* Misc constant values for golang
|
||||
*/
|
||||
public class GoConstants {
|
||||
public static final String GOLANG_CSPEC_NAME = "golang";
|
||||
|
||||
/**
|
||||
* Category path to place golang types in
|
||||
*/
|
||||
public static final CategoryPath GOLANG_CATEGORYPATH = new CategoryPath("/golang");
|
||||
|
||||
public static final String GOLANG_ABI_INTERNAL_CALLINGCONVENTION_NAME = "abi-internal";
|
||||
public static final String GOLANG_ABI0_CALLINGCONVENTION_NAME = "abi0";
|
||||
public static final String GOLANG_DUFFZERO_CALLINGCONVENTION_NAME = "duffzero";
|
||||
public static final String GOLANG_DUFFCOPY_CALLINGCONVENTION_NAME = "duffcopy";
|
||||
}
|
||||
|
||||
|
@ -28,22 +28,24 @@ import ghidra.program.model.lang.Register;
|
||||
*/
|
||||
public class GoRegisterInfo {
|
||||
|
||||
private List<Register> intRegisters;
|
||||
private List<Register> floatRegisters;
|
||||
private int stackInitialOffset;
|
||||
private int maxAlign; // 4 or 8
|
||||
private Register currentGoroutineRegister; // always points to g
|
||||
private Register zeroRegister; // always contains a zero value
|
||||
private final List<Register> intRegisters;
|
||||
private final List<Register> floatRegisters;
|
||||
private final int stackInitialOffset;
|
||||
private final int maxAlign; // 4 or 8
|
||||
private final Register currentGoroutineRegister; // always points to g
|
||||
private final Register zeroRegister; // always contains a zero value
|
||||
private final boolean zeroRegisterIsBuiltin; // zero register is provided by cpu, or is manually set
|
||||
|
||||
GoRegisterInfo(List<Register> intRegisters, List<Register> floatRegisters,
|
||||
int stackInitialOffset, int maxAlign, Register currentGoroutineRegister,
|
||||
Register zeroRegister) {
|
||||
Register zeroRegister, boolean zeroRegisterIsBuiltin) {
|
||||
this.intRegisters = intRegisters;
|
||||
this.floatRegisters = floatRegisters;
|
||||
this.stackInitialOffset = stackInitialOffset;
|
||||
this.maxAlign = maxAlign;
|
||||
this.currentGoroutineRegister = currentGoroutineRegister;
|
||||
this.zeroRegister = zeroRegister;
|
||||
this.zeroRegisterIsBuiltin = zeroRegisterIsBuiltin;
|
||||
}
|
||||
|
||||
public int getIntRegisterSize() {
|
||||
@ -62,6 +64,10 @@ public class GoRegisterInfo {
|
||||
return zeroRegister;
|
||||
}
|
||||
|
||||
public boolean isZeroRegisterIsBuiltin() {
|
||||
return zeroRegisterIsBuiltin;
|
||||
}
|
||||
|
||||
public List<Register> getIntRegisters() {
|
||||
return intRegisters;
|
||||
}
|
||||
@ -76,11 +82,11 @@ public class GoRegisterInfo {
|
||||
|
||||
public int getAlignmentForType(DataType dt) {
|
||||
while (dt instanceof TypeDef || dt instanceof Array) {
|
||||
if (dt instanceof TypeDef) {
|
||||
dt = ((TypeDef) dt).getBaseDataType();
|
||||
if (dt instanceof TypeDef td) {
|
||||
dt = td.getBaseDataType();
|
||||
}
|
||||
if (dt instanceof Array) {
|
||||
dt = ((Array) dt).getDataType();
|
||||
if (dt instanceof Array a) {
|
||||
dt = a.getDataType();
|
||||
}
|
||||
}
|
||||
if (isIntType(dt) && isIntrinsicSize(dt.getLength())) {
|
||||
|
@ -37,7 +37,7 @@ import ghidra.util.xml.XmlUtilities;
|
||||
* <float_registers list="XMM0,XMM1,XMM2,XMM3,XMM4,XMM5,XMM6,XMM7,XMM8,XMM9,XMM10,XMM11,XMM12,XMM13,XMM14"/>
|
||||
* <stack initialoffset="8" maxalign="8"/>
|
||||
* <current_goroutine register="R14"/>
|
||||
* <zero_register register="XMM15"/>
|
||||
* <zero_register register="XMM15" builtin="true|false"/>
|
||||
* </register_info>
|
||||
* <register_info versions="V1_2">
|
||||
* ...
|
||||
@ -115,7 +115,7 @@ public class GoRegisterInfoManager {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<GoVer, GoRegisterInfo> readFrom(Element rootElem, Language lang)
|
||||
private Map<GoVer, GoRegisterInfo> readFrom(Element rootElem, Language lang)
|
||||
throws IOException {
|
||||
|
||||
Map<GoVer, GoRegisterInfo> result = new HashMap<>();
|
||||
@ -152,10 +152,12 @@ public class GoRegisterInfoManager {
|
||||
Register currentGoRoutineReg =
|
||||
parseRegStr(goRoutineElem.getAttributeValue("register"), lang);
|
||||
Register zeroReg = parseRegStr(zeroRegElem.getAttributeValue("register"), lang);
|
||||
boolean zeroRegIsBuiltin =
|
||||
XmlUtilities.parseOptionalBooleanAttr(zeroRegElem, "builtin", false);
|
||||
|
||||
GoRegisterInfo registerInfo =
|
||||
new GoRegisterInfo(intRegs, floatRegs, stackInitialOffset, maxAlign,
|
||||
currentGoRoutineReg, zeroReg);
|
||||
currentGoRoutineReg, zeroReg, zeroRegIsBuiltin);
|
||||
Map<GoVer, GoRegisterInfo> result = new HashMap<>();
|
||||
for (GoVer goVer : validGoVersions) {
|
||||
result.put(goVer, registerInfo);
|
||||
@ -165,7 +167,7 @@ public class GoRegisterInfoManager {
|
||||
|
||||
private GoRegisterInfo getDefault(Language lang) {
|
||||
int goSize = lang.getInstructionAlignment();
|
||||
return new GoRegisterInfo(List.of(), List.of(), goSize, goSize, null, null);
|
||||
return new GoRegisterInfo(List.of(), List.of(), goSize, goSize, null, null, false);
|
||||
}
|
||||
|
||||
private List<Register> parseRegListStr(String s, Language lang) throws IOException {
|
||||
|
@ -66,6 +66,10 @@ public class GoFunctabEntry {
|
||||
: null;
|
||||
}
|
||||
|
||||
public long getFuncoff() {
|
||||
return funcoff;
|
||||
}
|
||||
|
||||
private GoModuledata getModuledata() {
|
||||
return programContext.findContainingModuleByFuncData(context.getStructureStart());
|
||||
}
|
||||
|
@ -128,6 +128,21 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||
return pclntable.isOffsetWithinData(offset, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an artificial slice of the functab entries that are valid.
|
||||
*
|
||||
* @return artificial slice of the functab entries that are valid
|
||||
*/
|
||||
public GoSlice getFunctabEntriesSlice() {
|
||||
// chop off the last entry as it is not a full entry (it just points to the address
|
||||
// at the end of the text segment) and can conflict with markup of the following structs
|
||||
long sliceElementCount = ftab.getLen() > 0 ? ftab.getLen() - 1 : 0;
|
||||
int entryLen =
|
||||
programContext.getStructureMappingInfo(GoFunctabEntry.class).getStructureLength();
|
||||
GoSlice subSlice = ftab.getSubSlice(0, sliceElementCount, entryLen);
|
||||
return subSlice;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
MemoryBlock txtBlock = programContext.getProgram().getMemory().getBlock(".text");
|
||||
if (txtBlock != null && txtBlock.getStart().getOffset() != text) {
|
||||
@ -153,6 +168,16 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||
return funcnametab;
|
||||
}
|
||||
|
||||
public List<GoFuncData> getAllFunctionData() throws IOException {
|
||||
List<GoFunctabEntry> functabentries =
|
||||
getFunctabEntriesSlice().readList(GoFunctabEntry.class);
|
||||
List<GoFuncData> result = new ArrayList<>();
|
||||
for (GoFunctabEntry functabEntry : functabentries) {
|
||||
result.add(functabEntry.getFuncData());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureContext<GoModuledata> getStructureContext() {
|
||||
return structureContext;
|
||||
@ -168,15 +193,9 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||
markupStringTable(funcnametab.getArrayAddress(), funcnametab.getLen(), session);
|
||||
markupStringTable(filetab.getArrayAddress(), filetab.getLen(), session);
|
||||
|
||||
if (ftab.getLen() > 0) {
|
||||
// chop off the last entry as it is not a full entry (it just points to the address
|
||||
// at the end of the text segment) and can conflict with markup of the following structs
|
||||
int entryLen =
|
||||
programContext.getStructureMappingInfo(GoFunctabEntry.class).getStructureLength();
|
||||
GoSlice subSlice = ftab.getSubSlice(0, ftab.getLen() - 1, entryLen);
|
||||
subSlice.markupArray("moduledata.ftab", GoFunctabEntry.class, false, session);
|
||||
subSlice.markupArrayElements(GoFunctabEntry.class, session);
|
||||
}
|
||||
GoSlice subSlice = getFunctabEntriesSlice();
|
||||
subSlice.markupArray("moduledata.ftab", GoFunctabEntry.class, false, session);
|
||||
subSlice.markupArrayElements(GoFunctabEntry.class, session);
|
||||
|
||||
Structure textsectDT =
|
||||
programContext.getGhidraDataType("runtime.textsect", Structure.class);
|
||||
|
@ -18,6 +18,7 @@ package ghidra.app.util.bin.format.golang.rtti;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
@ -34,9 +35,13 @@ import ghidra.app.util.opinion.PeLoader;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.lang.PrototypeModel;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolType;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
@ -197,8 +202,15 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
private final Map<String, GoType> typeNameIndex = new HashMap<>();
|
||||
private final Map<Long, DataType> cachedRecoveredDataTypes = new HashMap<>();
|
||||
private final List<GoModuledata> modules = new ArrayList<>();
|
||||
private Map<Address, GoFuncData> funcdataByAddr = new HashMap<>();
|
||||
private Map<String, GoFuncData> funcdataByName = new HashMap<>();
|
||||
private GoType mapGoType;
|
||||
private GoType chanGoType;
|
||||
private GoRegisterInfo regInfo;
|
||||
private PrototypeModel abiInternalCallingConvention;
|
||||
private PrototypeModel abi0CallingConvention;
|
||||
private PrototypeModel duffzeroCallingConvention;
|
||||
private PrototypeModel duffcopyCallingConvention;
|
||||
|
||||
/**
|
||||
* Creates a GoRttiMapper using the specified bootstrap information.
|
||||
@ -259,6 +271,41 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
return goVersion;
|
||||
}
|
||||
|
||||
public GoRegisterInfo getRegInfo() {
|
||||
return regInfo;
|
||||
}
|
||||
|
||||
public void init(TaskMonitor monitor) throws IOException {
|
||||
initHiddenCompilerTypes();
|
||||
|
||||
this.regInfo = GoRegisterInfoManager.getInstance()
|
||||
.getRegisterInfoForLang(program.getLanguage(), goVersion);
|
||||
|
||||
this.abiInternalCallingConvention = program.getFunctionManager()
|
||||
.getCallingConvention(GoConstants.GOLANG_ABI_INTERNAL_CALLINGCONVENTION_NAME);
|
||||
this.abi0CallingConvention = program.getFunctionManager()
|
||||
.getCallingConvention(GoConstants.GOLANG_ABI0_CALLINGCONVENTION_NAME);
|
||||
this.duffzeroCallingConvention = program.getFunctionManager()
|
||||
.getCallingConvention(GoConstants.GOLANG_DUFFZERO_CALLINGCONVENTION_NAME);
|
||||
this.duffcopyCallingConvention = program.getFunctionManager()
|
||||
.getCallingConvention(GoConstants.GOLANG_DUFFCOPY_CALLINGCONVENTION_NAME);
|
||||
|
||||
GoModuledata firstModule = findFirstModuledata(monitor);
|
||||
if (firstModule != null) {
|
||||
addModule(firstModule);
|
||||
}
|
||||
initFuncdata();
|
||||
}
|
||||
|
||||
private void initFuncdata() throws IOException {
|
||||
for (GoModuledata module : modules) {
|
||||
for (GoFuncData funcdata : module.getAllFunctionData()) {
|
||||
funcdataByAddr.put(funcdata.getFuncAddress(), funcdata);
|
||||
funcdataByName.put(funcdata.getName(), funcdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first module data instance
|
||||
*
|
||||
@ -277,6 +324,40 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
modules.add(module);
|
||||
}
|
||||
|
||||
public GoParamStorageAllocator getStorageAllocator() {
|
||||
GoParamStorageAllocator storageAllocator = new GoParamStorageAllocator(program, goVersion);
|
||||
return storageAllocator;
|
||||
}
|
||||
|
||||
public boolean isGolangAbi0Func(Function func) {
|
||||
Address funcAddr = func.getEntryPoint();
|
||||
for (Symbol symbol : func.getProgram().getSymbolTable().getSymbolsAsIterator(funcAddr)) {
|
||||
if (symbol.getSymbolType() == SymbolType.LABEL) {
|
||||
String labelName = symbol.getName();
|
||||
if (labelName.endsWith("abi0")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public PrototypeModel getAbi0CallingConvention() {
|
||||
return abi0CallingConvention;
|
||||
}
|
||||
|
||||
public PrototypeModel getAbiInternalCallingConvention() {
|
||||
return abiInternalCallingConvention;
|
||||
}
|
||||
|
||||
public PrototypeModel getDuffzeroCallingConvention() {
|
||||
return duffzeroCallingConvention;
|
||||
}
|
||||
|
||||
public PrototypeModel getDuffcopyCallingConvention() {
|
||||
return duffcopyCallingConvention;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the {@link GoModuledata} that contains the specified offset.
|
||||
* <p>
|
||||
@ -415,6 +496,13 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
return getGoType(addr.getOffset());
|
||||
}
|
||||
|
||||
public GoType getLastGoType() {
|
||||
Optional<Entry<Long, GoType>> max = goTypes.entrySet()
|
||||
.stream()
|
||||
.max((o1, o2) -> o1.getKey().compareTo(o2.getKey()));
|
||||
return max.isPresent() ? max.get().getValue() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a go type by its go-type name, from the list of
|
||||
* {@link #discoverGoTypes(TaskMonitor) discovered} go types.
|
||||
@ -662,33 +750,28 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
public void discoverGoTypes(TaskMonitor monitor) throws IOException, CancelledException {
|
||||
GoModuledata firstModule = findFirstModuledata(monitor);
|
||||
if (firstModule == null) {
|
||||
return;
|
||||
}
|
||||
addModule(firstModule);
|
||||
|
||||
UnknownProgressWrappingTaskMonitor upwtm =
|
||||
new UnknownProgressWrappingTaskMonitor(monitor, 50);
|
||||
upwtm.setMessage("Iterating Golang RTTI types");
|
||||
upwtm.initialize(0);
|
||||
|
||||
goTypes.clear();
|
||||
Set<Long> discoveredTypes = new HashSet<>();
|
||||
for (Iterator<GoType> it = firstModule.iterateTypes(); it.hasNext();) {
|
||||
upwtm.checkCancelled();
|
||||
upwtm.setProgress(discoveredTypes.size());
|
||||
|
||||
GoType type = it.next();
|
||||
type.discoverGoTypes(discoveredTypes);
|
||||
}
|
||||
typeNameIndex.clear();
|
||||
Set<Long> discoveredTypes = new HashSet<>();
|
||||
for (GoModuledata module : modules) {
|
||||
for (Iterator<GoType> it = module.iterateTypes(); it.hasNext();) {
|
||||
upwtm.checkCancelled();
|
||||
upwtm.setProgress(discoveredTypes.size());
|
||||
|
||||
GoType type = it.next();
|
||||
type.discoverGoTypes(discoveredTypes);
|
||||
}
|
||||
}
|
||||
for (GoType goType : goTypes.values()) {
|
||||
String typeName = goType.getNameString();
|
||||
typeNameIndex.put(typeName, goType);
|
||||
}
|
||||
Msg.info(this, "Found %d golang types".formatted(goTypes.size()));
|
||||
initHiddenCompilerTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -764,6 +847,18 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
return offset != 0 ? readStructure(GoName.class, offset) : null;
|
||||
}
|
||||
|
||||
public GoFuncData getFunctionData(Address funcAddr) throws IOException {
|
||||
return funcdataByAddr.get(funcAddr);
|
||||
}
|
||||
|
||||
public GoFuncData getFunctionByName(String funcName) {
|
||||
return funcdataByName.get(funcName);
|
||||
}
|
||||
|
||||
public List<GoFuncData> getAllFunctions() throws IOException {
|
||||
return new ArrayList<>(funcdataByAddr.values());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
|
||||
private void initHiddenCompilerTypes() {
|
||||
|
@ -94,6 +94,13 @@ public class GoSlice {
|
||||
return programContext.getDataAddress(array);
|
||||
}
|
||||
|
||||
public long getArrayEnd(Class<?> elementClass) {
|
||||
StructureMappingInfo<?> elementSMI =
|
||||
context.getDataTypeMapper().getStructureMappingInfo(elementClass);
|
||||
int elementLength = elementSMI.getStructureLength();
|
||||
return array + len * elementLength;
|
||||
}
|
||||
|
||||
public long getLen() {
|
||||
return len;
|
||||
}
|
||||
|
@ -15,9 +15,8 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.rtti.types;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.util.bin.format.golang.rtti.GoString;
|
||||
import ghidra.app.util.bin.format.golang.structmapping.StructureMapping;
|
||||
|
@ -56,6 +56,11 @@ public class GoStructType extends GoType {
|
||||
return fields.readList(GoStructField.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEndOfTypeInfo() throws IOException {
|
||||
return fields.getArrayEnd(GoStructField.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void additionalMarkup(MarkupSession session) throws IOException {
|
||||
super.additionalMarkup(session);
|
||||
|
@ -92,6 +92,19 @@ public abstract class GoType implements StructureMarkup<GoType> {
|
||||
: 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location of where this type object, and any known associated optional
|
||||
* structures ends.
|
||||
*
|
||||
* @return index location of end of this type object
|
||||
* @throws IOException if error reading
|
||||
*/
|
||||
public long getEndOfTypeInfo() throws IOException {
|
||||
return typ.hasUncommonType()
|
||||
? getUncommonType().getEndOfTypeInfo()
|
||||
: context.getStructureEnd();
|
||||
}
|
||||
|
||||
@Markup
|
||||
public GoUncommonType getUncommonType() throws IOException {
|
||||
return typ.hasUncommonType()
|
||||
|
@ -15,9 +15,8 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.rtti.types;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.format.golang.rtti.*;
|
||||
import ghidra.app.util.bin.format.golang.structmapping.*;
|
||||
@ -70,4 +69,18 @@ public class GoUncommonType {
|
||||
return slice.readList(GoMethod.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location of where this object, and any known associated optional
|
||||
* structures ends.
|
||||
*
|
||||
* @return index location of end of this type object
|
||||
*/
|
||||
public long getEndOfTypeInfo() {
|
||||
if (mcount == 0) {
|
||||
return context.getStructureEnd();
|
||||
}
|
||||
GoSlice slice = getMethodsSlice();
|
||||
return slice.getArrayEnd(GoMethod.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -342,7 +342,7 @@ public class FunctionUtility {
|
||||
* @param function the function
|
||||
* @return true if the function has a default name.
|
||||
*/
|
||||
static boolean isDefaultFunctionName(Function function) {
|
||||
public static boolean isDefaultFunctionName(Function function) {
|
||||
String defaultFunctionName =
|
||||
SymbolUtilities.getDefaultFunctionName(function.getEntryPoint());
|
||||
return defaultFunctionName.equals(function.getName());
|
||||
|
@ -0,0 +1,191 @@
|
||||
/* ###
|
||||
* 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;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.app.cmd.comments.SetCommentCmd;
|
||||
import ghidra.app.decompiler.DecompInterface;
|
||||
import ghidra.app.decompiler.DecompileResults;
|
||||
import ghidra.app.decompiler.parallel.DecompilerCallback;
|
||||
import ghidra.app.decompiler.parallel.ParallelDecompiler;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.bin.format.golang.GoConstants;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.listing.Function.FunctionUpdateType;
|
||||
import ghidra.program.model.pcode.HighFunction;
|
||||
import ghidra.program.model.pcode.PcodeBlockBasic;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.FunctionUtility;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class GolangDuffFixupAnalyzer extends AbstractAnalyzer {
|
||||
private final static String NAME = "Golang Duff Function Fixup";
|
||||
private final static String DESCRIPTION = """
|
||||
Propagates function signature information from the base runtime.duffcopy \
|
||||
and runtime.duffzero functions to the other entry points that were discovered \
|
||||
during analysis.""";
|
||||
|
||||
private Program program;
|
||||
private TaskMonitor monitor;
|
||||
private MessageLog log;
|
||||
|
||||
public GolangDuffFixupAnalyzer() {
|
||||
super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
|
||||
setPriority(AnalysisPriority.FUNCTION_ANALYSIS.after());
|
||||
setDefaultEnablement(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAnalyze(Program program) {
|
||||
return GoConstants.GOLANG_CSPEC_NAME.equals(
|
||||
program.getCompilerSpec().getCompilerSpecDescription().getCompilerSpecName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
||||
throws CancelledException {
|
||||
this.program = program;
|
||||
this.monitor = monitor;
|
||||
this.log = log;
|
||||
|
||||
Symbol duffzeroSym = SymbolUtilities.getUniqueSymbol(program, "runtime.duffzero");
|
||||
Function duffzeroFunc = duffzeroSym != null ? (Function) duffzeroSym.getObject() : null;
|
||||
Symbol duffcopySym = SymbolUtilities.getUniqueSymbol(program, "runtime.duffcopy");
|
||||
Function duffcopyFunc = duffcopySym != null ? (Function) duffcopySym.getObject() : null;
|
||||
|
||||
List<Function> funcs = new ArrayList<>();
|
||||
if (duffzeroFunc != null && duffzeroFunc.getCallingConvention() != null) {
|
||||
funcs.add(duffzeroFunc);
|
||||
}
|
||||
if (duffcopyFunc != null && duffcopyFunc.getCallingConvention() != null) {
|
||||
funcs.add(duffcopyFunc);
|
||||
}
|
||||
|
||||
if (funcs.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Map<Address, AddressSetView> map = getFunctionActualRanges(funcs);
|
||||
|
||||
if (duffzeroFunc != null) {
|
||||
updateDuffFuncs(duffzeroFunc, map.get(duffzeroFunc.getEntryPoint()));
|
||||
}
|
||||
if (duffcopyFunc != null) {
|
||||
updateDuffFuncs(duffcopyFunc, map.get(duffcopyFunc.getEntryPoint()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy details from the base duff function to any other unnamed functions that start within
|
||||
* the base duff function's range.
|
||||
*
|
||||
* @param duffFunc base duff function
|
||||
* @param duffFuncBody the addresses the base function occupies
|
||||
*/
|
||||
private void updateDuffFuncs(Function duffFunc, AddressSetView duffFuncBody) {
|
||||
if (duffFunc == null || duffFuncBody == null) {
|
||||
return;
|
||||
}
|
||||
String duffComment = program.getListing()
|
||||
.getCodeUnitAt(duffFunc.getEntryPoint())
|
||||
.getComment(CodeUnit.PLATE_COMMENT);
|
||||
for (FunctionIterator funcIt =
|
||||
program.getFunctionManager().getFunctions(duffFuncBody, true); funcIt.hasNext();) {
|
||||
Function func = funcIt.next();
|
||||
if (!FunctionUtility.isDefaultFunctionName(func)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
func.setName(duffFunc.getName() + "_" + func.getEntryPoint(), SourceType.ANALYSIS);
|
||||
func.updateFunction(duffFunc.getCallingConventionName(), duffFunc.getReturn(),
|
||||
Arrays.asList(duffFunc.getParameters()),
|
||||
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.ANALYSIS);
|
||||
if (duffComment != null && !duffComment.isBlank()) {
|
||||
new SetCommentCmd(func.getEntryPoint(), CodeUnit.PLATE_COMMENT, duffComment)
|
||||
.applyTo(program);
|
||||
}
|
||||
}
|
||||
catch (DuplicateNameException | InvalidInputException e) {
|
||||
log.appendMsg("Error updating duff functions");
|
||||
log.appendException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void configureDecompiler(DecompInterface decompiler) {
|
||||
decompiler.toggleCCode(false); //only need syntax tree
|
||||
decompiler.toggleSyntaxTree(true); // Produce syntax tree
|
||||
decompiler.setSimplificationStyle("normalize");
|
||||
}
|
||||
|
||||
record HighFunctionAddresses(Address functionEntry, AddressSetView functionAddresses) {}
|
||||
|
||||
/**
|
||||
* Returns the addresses that a function occupies (as determined by the decompiler instead of
|
||||
* the disassembler).
|
||||
*
|
||||
* @param funcs list of functions
|
||||
* @return map of function entry point and addresses for that function
|
||||
*/
|
||||
private Map<Address, AddressSetView> getFunctionActualRanges(List<Function> funcs) {
|
||||
DecompilerCallback<HighFunctionAddresses> callback =
|
||||
new DecompilerCallback<>(program, this::configureDecompiler) {
|
||||
@Override
|
||||
public HighFunctionAddresses process(DecompileResults results, TaskMonitor tMonitor)
|
||||
throws Exception {
|
||||
tMonitor.checkCancelled();
|
||||
if (results == null) {
|
||||
return null;
|
||||
}
|
||||
Function func = results.getFunction();
|
||||
HighFunction highFunc = results.getHighFunction();
|
||||
if (func == null || highFunc == null) {
|
||||
return null;
|
||||
}
|
||||
AddressSet funcAddrs = new AddressSet();
|
||||
for (PcodeBlockBasic bb : highFunc.getBasicBlocks()) {
|
||||
funcAddrs.add(bb.getStart(), bb.getStop());
|
||||
}
|
||||
return new HighFunctionAddresses(func.getEntryPoint(), funcAddrs);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
List<HighFunctionAddresses> funcAddresses =
|
||||
ParallelDecompiler.decompileFunctions(callback, funcs, monitor);
|
||||
Map<Address, AddressSetView> results = funcAddresses.stream()
|
||||
.collect(
|
||||
Collectors.toMap(hfa -> hfa.functionEntry, hfa -> hfa.functionAddresses));
|
||||
return results;
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this, "Error: could not decompile functions with ParallelDecompiler", e);
|
||||
return Map.of();
|
||||
}
|
||||
finally {
|
||||
callback.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -51,6 +51,56 @@
|
||||
</unaffected>
|
||||
</prototype>
|
||||
</default_proto>
|
||||
|
||||
<prototype name="duffzero" extrapop="4" stackshift="4">
|
||||
<input>
|
||||
<pentry minsize="1" maxsize="4">
|
||||
<register name="EDI"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="4">
|
||||
<register name="EAX"/>
|
||||
</pentry>
|
||||
</input>
|
||||
|
||||
<output>
|
||||
<pentry minsize="1" maxsize="4">
|
||||
<register name="EDI"/>
|
||||
</pentry>
|
||||
</output>
|
||||
|
||||
<killedbycall>
|
||||
<register name="EDI"/>
|
||||
</killedbycall>
|
||||
<unaffected>
|
||||
<register name="ESP"/>
|
||||
<register name="EBP"/>
|
||||
</unaffected>
|
||||
</prototype>
|
||||
|
||||
<prototype name="duffcopy" extrapop="4" stackshift="4">
|
||||
<input>
|
||||
<pentry minsize="1" maxsize="4">
|
||||
<register name="EDI"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="4">
|
||||
<register name="ESI"/>
|
||||
</pentry>
|
||||
</input>
|
||||
|
||||
<output>
|
||||
</output>
|
||||
|
||||
<killedbycall>
|
||||
<register name="EDI"/>
|
||||
<register name="ESI"/>
|
||||
<register name="ECX"/>
|
||||
</killedbycall>
|
||||
<unaffected>
|
||||
<register name="ESP"/>
|
||||
<register name="EBP"/>
|
||||
</unaffected>
|
||||
</prototype>
|
||||
|
||||
<prototype name="__cdeclf" extrapop="4" stackshift="4">
|
||||
<input>
|
||||
<pentry minsize="1" maxsize="500" align="4">
|
||||
|
@ -178,6 +178,68 @@
|
||||
</unaffected>
|
||||
</prototype>
|
||||
|
||||
<prototype name="duffzero" extrapop="8" stackshift="8">
|
||||
<input>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<register name="RDI"/>
|
||||
</pentry>
|
||||
</input>
|
||||
|
||||
<output>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<register name="RDI"/>
|
||||
</pentry>
|
||||
</output>
|
||||
|
||||
<killedbycall>
|
||||
<register name="RDI"/>
|
||||
</killedbycall>
|
||||
<unaffected>
|
||||
<register name="RSP"/>
|
||||
<register name="RBP"/>
|
||||
<register name="R14"/>
|
||||
</unaffected>
|
||||
</prototype>
|
||||
|
||||
<prototype name="duffcopy" extrapop="8" stackshift="8">
|
||||
<input>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<register name="RDI"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<register name="RSI"/>
|
||||
</pentry>
|
||||
</input>
|
||||
|
||||
<output>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<register name="RDI"/>
|
||||
</pentry>
|
||||
<pentry minsize="9" maxsize="16">
|
||||
<addr space="join" piece2="RDI" piece1="RSI"/>
|
||||
</pentry>
|
||||
</output>
|
||||
|
||||
<killedbycall>
|
||||
<register name="RDI"/>
|
||||
<register name="RSI"/>
|
||||
</killedbycall>
|
||||
<unaffected>
|
||||
<register name="RAX"/>
|
||||
<register name="RBX"/>
|
||||
<register name="RCX"/>
|
||||
<register name="RDI"/>
|
||||
<register name="RSI"/>
|
||||
<register name="R8"/>
|
||||
<register name="R9"/>
|
||||
<register name="R10"/>
|
||||
<register name="R11"/>
|
||||
<register name="RSP"/>
|
||||
<register name="RBP"/>
|
||||
<register name="R14"/>
|
||||
</unaffected>
|
||||
</prototype>
|
||||
|
||||
<prototype name="__stdcall" extrapop="8" stackshift="8">
|
||||
<!-- Derived from "System V Application Binary Interface AMD64 Architecture Processor Supplement" April 2016 -->
|
||||
<input>
|
||||
|
Loading…
Reference in New Issue
Block a user