mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-2425 more better duffzero / duffcopy function info
This commit is contained in:
parent
176bdea28a
commit
b5422faefb
17 changed files with 642 additions and 88 deletions
|
@ -18,30 +18,30 @@ package ghidra.app.plugin.core.analysis;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.HashSet;
|
import java.util.*;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.util.MemoryBlockUtils;
|
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.elf.info.ElfInfoItem.ItemWithAddress;
|
||||||
import ghidra.app.util.bin.format.golang.*;
|
import ghidra.app.util.bin.format.golang.*;
|
||||||
import ghidra.app.util.bin.format.golang.rtti.GoModuledata;
|
import ghidra.app.util.bin.format.golang.rtti.*;
|
||||||
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
|
|
||||||
import ghidra.app.util.bin.format.golang.structmapping.MarkupSession;
|
import ghidra.app.util.bin.format.golang.structmapping.MarkupSession;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressSetView;
|
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.lang.Register;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.listing.Function.FunctionUpdateType;
|
||||||
import ghidra.program.model.mem.MemoryBlock;
|
import ghidra.program.model.mem.MemoryBlock;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.NumericUtilities;
|
import ghidra.util.NumericUtilities;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.exception.InvalidInputException;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import ghidra.util.task.UnknownProgressWrappingTaskMonitor;
|
import ghidra.util.task.UnknownProgressWrappingTaskMonitor;
|
||||||
import ghidra.xml.XmlParseException;
|
import ghidra.xml.XmlParseException;
|
||||||
|
@ -73,34 +73,35 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
monitor.setMessage("Golang symbol analyzer");
|
monitor.setMessage("Golang symbol analyzer");
|
||||||
|
|
||||||
try (GoRttiMapper programContext = GoRttiMapper.getMapperFor(program, log)) {
|
try (GoRttiMapper goBinary = GoRttiMapper.getMapperFor(program, log)) {
|
||||||
if (programContext == null) {
|
if (goBinary == null) {
|
||||||
Msg.error(this, "Golang analyzer error: unable to get GoRttiMapper");
|
Msg.error(this, "Golang analyzer error: unable to get GoRttiMapper");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
programContext.discoverGoTypes(monitor);
|
goBinary.init(monitor);
|
||||||
|
goBinary.discoverGoTypes(monitor);
|
||||||
GoModuledata firstModule = programContext.getFirstModule();
|
|
||||||
|
|
||||||
|
|
||||||
UnknownProgressWrappingTaskMonitor upwtm =
|
UnknownProgressWrappingTaskMonitor upwtm =
|
||||||
new UnknownProgressWrappingTaskMonitor(monitor, 100);
|
new UnknownProgressWrappingTaskMonitor(monitor, 100);
|
||||||
upwtm.initialize(0);
|
upwtm.initialize(0);
|
||||||
upwtm.setMessage("Marking up Golang RTTI structures");
|
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.labelStructure(firstModule, "firstmoduledata");
|
||||||
markupSession.markup(firstModule, false);
|
markupSession.markup(firstModule, false);
|
||||||
|
}
|
||||||
|
|
||||||
markupMiscInfoStructs(program);
|
markupWellknownSymbols(goBinary, markupSession);
|
||||||
markupWellknownSymbols(programContext, markupSession);
|
setupProgramContext(goBinary, markupSession);
|
||||||
|
goBinary.recoverDataTypes(monitor);
|
||||||
|
markupGoFunctions(goBinary, markupSession);
|
||||||
fixupNoReturnFuncs(program);
|
fixupNoReturnFuncs(program);
|
||||||
setupProgramContext(programContext, markupSession);
|
markupMiscInfoStructs(program);
|
||||||
programContext.recoverDataTypes(monitor);
|
|
||||||
|
|
||||||
if (analyzerOptions.createBootstrapDatatypeArchive) {
|
if (analyzerOptions.createBootstrapDatatypeArchive) {
|
||||||
createBootstrapGDT(programContext, program, monitor);
|
createBootstrapGDT(goBinary, program, monitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
|
@ -124,23 +125,103 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||||
analyzerOptions.createBootstrapDatatypeArchive);
|
analyzerOptions.createBootstrapDatatypeArchive);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void markupWellknownSymbols(GoRttiMapper programContext, MarkupSession session)
|
private void markupWellknownSymbols(GoRttiMapper goBinary, MarkupSession session)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Program program = programContext.getProgram();
|
Program program = goBinary.getProgram();
|
||||||
|
|
||||||
Symbol g0 = SymbolUtilities.getUniqueSymbol(program, "runtime.g0");
|
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) {
|
if (g0 != null && gStruct != null) {
|
||||||
session.markupAddressIfUndefined(g0.getAddress(), gStruct);
|
session.markupAddressIfUndefined(g0.getAddress(), gStruct);
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol m0 = SymbolUtilities.getUniqueSymbol(program, "runtime.m0");
|
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) {
|
if (m0 != null && mStruct != null) {
|
||||||
session.markupAddressIfUndefined(m0.getAddress(), mStruct);
|
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) {
|
private void markupMiscInfoStructs(Program program) {
|
||||||
// this also adds "golang" info to program properties
|
// this also adds "golang" info to program properties
|
||||||
|
|
||||||
|
@ -229,15 +310,14 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||||
return newMB.getStart();
|
return newMB.getStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupProgramContext(GoRttiMapper programContext, MarkupSession session)
|
private void setupProgramContext(GoRttiMapper goBinary, MarkupSession session)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Program program = programContext.getProgram();
|
Program program = goBinary.getProgram();
|
||||||
GoRegisterInfo goRegInfo = GoRegisterInfoManager.getInstance()
|
GoRegisterInfo goRegInfo = goBinary.getRegInfo();
|
||||||
.getRegisterInfoForLang(program.getLanguage(),
|
|
||||||
programContext.getGolangVersion());
|
|
||||||
|
|
||||||
MemoryBlock txtMemblock = program.getMemory().getBlock(".text");
|
MemoryBlock txtMemblock = program.getMemory().getBlock(".text");
|
||||||
if (txtMemblock != null && goRegInfo.getZeroRegister() != null) {
|
if (txtMemblock != null && goRegInfo.getZeroRegister() != null &&
|
||||||
|
!goRegInfo.isZeroRegisterIsBuiltin()) {
|
||||||
try {
|
try {
|
||||||
program.getProgramContext()
|
program.getProgramContext()
|
||||||
.setValue(goRegInfo.getZeroRegister(), txtMemblock.getStart(),
|
.setValue(goRegInfo.getZeroRegister(), txtMemblock.getStart(),
|
||||||
|
@ -248,7 +328,7 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int alignment = programContext.getPtrSize();
|
int alignment = goBinary.getPtrSize();
|
||||||
long sizeNeeded = 0;
|
long sizeNeeded = 0;
|
||||||
|
|
||||||
Symbol zerobase = SymbolUtilities.getUniqueSymbol(program, "runtime.zerobase");
|
Symbol zerobase = SymbolUtilities.getUniqueSymbol(program, "runtime.zerobase");
|
||||||
|
@ -258,13 +338,13 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
long gStructOffset = sizeNeeded;
|
long gStructOffset = sizeNeeded;
|
||||||
Structure gStruct = programContext.getGhidraDataType("runtime.g", Structure.class);
|
Structure gStruct = goBinary.getGhidraDataType("runtime.g", Structure.class);
|
||||||
sizeNeeded += gStruct != null
|
sizeNeeded += gStruct != null
|
||||||
? NumericUtilities.getUnsignedAlignedValue(gStruct.getLength(), alignment)
|
? NumericUtilities.getUnsignedAlignedValue(gStruct.getLength(), alignment)
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
long mStructOffset = sizeNeeded;
|
long mStructOffset = sizeNeeded;
|
||||||
Structure mStruct = programContext.getGhidraDataType("runtime.m", Structure.class);
|
Structure mStruct = goBinary.getGhidraDataType("runtime.m", Structure.class);
|
||||||
sizeNeeded += mStruct != null
|
sizeNeeded += mStruct != null
|
||||||
? NumericUtilities.getUnsignedAlignedValue(mStruct.getLength(), alignment)
|
? NumericUtilities.getUnsignedAlignedValue(mStruct.getLength(), alignment)
|
||||||
: 0;
|
: 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 {
|
TaskMonitor monitor) throws IOException {
|
||||||
GoVer goVer = programContext.getGolangVersion();
|
GoVer goVer = goBinary.getGolangVersion();
|
||||||
String osName = GoRttiMapper.getGolangOSString(program);
|
String osName = GoRttiMapper.getGolangOSString(program);
|
||||||
String gdtFilename =
|
String gdtFilename =
|
||||||
GoRttiMapper.getGDTFilename(goVer, programContext.getPtrSize(), osName);
|
GoRttiMapper.getGDTFilename(goVer, goBinary.getPtrSize(), osName);
|
||||||
gdtFilename =
|
gdtFilename =
|
||||||
gdtFilename.replace(".gdt", "_%d.gdt".formatted(System.currentTimeMillis()));
|
gdtFilename.replace(".gdt", "_%d.gdt".formatted(System.currentTimeMillis()));
|
||||||
File gdt = new File(System.getProperty("user.home"), gdtFilename);
|
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);
|
Msg.info(this, "Golang bootstrap GDT created: " + gdt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +403,7 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canAnalyze(Program program) {
|
public boolean canAnalyze(Program program) {
|
||||||
return "golang".equals(
|
return GoConstants.GOLANG_CSPEC_NAME.equals(
|
||||||
program.getCompilerSpec().getCompilerSpecDescription().getCompilerSpecName());
|
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.";
|
"a newly discovered starting symbol, provided the user hasn't manually moved.";
|
||||||
|
|
||||||
private static final String DEFAULT_STARTING_SYMBOLS =
|
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 {
|
public static enum StartLocationType {
|
||||||
LOWEST_ADDRESS("Lowest Address"),
|
LOWEST_ADDRESS("Lowest Address"),
|
||||||
|
|
|
@ -21,8 +21,16 @@ import ghidra.program.model.data.CategoryPath;
|
||||||
* Misc constant values for golang
|
* Misc constant values for golang
|
||||||
*/
|
*/
|
||||||
public class GoConstants {
|
public class GoConstants {
|
||||||
|
public static final String GOLANG_CSPEC_NAME = "golang";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Category path to place golang types in
|
* Category path to place golang types in
|
||||||
*/
|
*/
|
||||||
public static final CategoryPath GOLANG_CATEGORYPATH = new CategoryPath("/golang");
|
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 {
|
public class GoRegisterInfo {
|
||||||
|
|
||||||
private List<Register> intRegisters;
|
private final List<Register> intRegisters;
|
||||||
private List<Register> floatRegisters;
|
private final List<Register> floatRegisters;
|
||||||
private int stackInitialOffset;
|
private final int stackInitialOffset;
|
||||||
private int maxAlign; // 4 or 8
|
private final int maxAlign; // 4 or 8
|
||||||
private Register currentGoroutineRegister; // always points to g
|
private final Register currentGoroutineRegister; // always points to g
|
||||||
private Register zeroRegister; // always contains a zero value
|
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,
|
GoRegisterInfo(List<Register> intRegisters, List<Register> floatRegisters,
|
||||||
int stackInitialOffset, int maxAlign, Register currentGoroutineRegister,
|
int stackInitialOffset, int maxAlign, Register currentGoroutineRegister,
|
||||||
Register zeroRegister) {
|
Register zeroRegister, boolean zeroRegisterIsBuiltin) {
|
||||||
this.intRegisters = intRegisters;
|
this.intRegisters = intRegisters;
|
||||||
this.floatRegisters = floatRegisters;
|
this.floatRegisters = floatRegisters;
|
||||||
this.stackInitialOffset = stackInitialOffset;
|
this.stackInitialOffset = stackInitialOffset;
|
||||||
this.maxAlign = maxAlign;
|
this.maxAlign = maxAlign;
|
||||||
this.currentGoroutineRegister = currentGoroutineRegister;
|
this.currentGoroutineRegister = currentGoroutineRegister;
|
||||||
this.zeroRegister = zeroRegister;
|
this.zeroRegister = zeroRegister;
|
||||||
|
this.zeroRegisterIsBuiltin = zeroRegisterIsBuiltin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIntRegisterSize() {
|
public int getIntRegisterSize() {
|
||||||
|
@ -62,6 +64,10 @@ public class GoRegisterInfo {
|
||||||
return zeroRegister;
|
return zeroRegister;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isZeroRegisterIsBuiltin() {
|
||||||
|
return zeroRegisterIsBuiltin;
|
||||||
|
}
|
||||||
|
|
||||||
public List<Register> getIntRegisters() {
|
public List<Register> getIntRegisters() {
|
||||||
return intRegisters;
|
return intRegisters;
|
||||||
}
|
}
|
||||||
|
@ -76,11 +82,11 @@ public class GoRegisterInfo {
|
||||||
|
|
||||||
public int getAlignmentForType(DataType dt) {
|
public int getAlignmentForType(DataType dt) {
|
||||||
while (dt instanceof TypeDef || dt instanceof Array) {
|
while (dt instanceof TypeDef || dt instanceof Array) {
|
||||||
if (dt instanceof TypeDef) {
|
if (dt instanceof TypeDef td) {
|
||||||
dt = ((TypeDef) dt).getBaseDataType();
|
dt = td.getBaseDataType();
|
||||||
}
|
}
|
||||||
if (dt instanceof Array) {
|
if (dt instanceof Array a) {
|
||||||
dt = ((Array) dt).getDataType();
|
dt = a.getDataType();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isIntType(dt) && isIntrinsicSize(dt.getLength())) {
|
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"/>
|
* <float_registers list="XMM0,XMM1,XMM2,XMM3,XMM4,XMM5,XMM6,XMM7,XMM8,XMM9,XMM10,XMM11,XMM12,XMM13,XMM14"/>
|
||||||
* <stack initialoffset="8" maxalign="8"/>
|
* <stack initialoffset="8" maxalign="8"/>
|
||||||
* <current_goroutine register="R14"/>
|
* <current_goroutine register="R14"/>
|
||||||
* <zero_register register="XMM15"/>
|
* <zero_register register="XMM15" builtin="true|false"/>
|
||||||
* </register_info>
|
* </register_info>
|
||||||
* <register_info versions="V1_2">
|
* <register_info versions="V1_2">
|
||||||
* ...
|
* ...
|
||||||
|
@ -115,7 +115,7 @@ public class GoRegisterInfoManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Map<GoVer, GoRegisterInfo> readFrom(Element rootElem, Language lang)
|
private Map<GoVer, GoRegisterInfo> readFrom(Element rootElem, Language lang)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
Map<GoVer, GoRegisterInfo> result = new HashMap<>();
|
Map<GoVer, GoRegisterInfo> result = new HashMap<>();
|
||||||
|
@ -152,10 +152,12 @@ public class GoRegisterInfoManager {
|
||||||
Register currentGoRoutineReg =
|
Register currentGoRoutineReg =
|
||||||
parseRegStr(goRoutineElem.getAttributeValue("register"), lang);
|
parseRegStr(goRoutineElem.getAttributeValue("register"), lang);
|
||||||
Register zeroReg = parseRegStr(zeroRegElem.getAttributeValue("register"), lang);
|
Register zeroReg = parseRegStr(zeroRegElem.getAttributeValue("register"), lang);
|
||||||
|
boolean zeroRegIsBuiltin =
|
||||||
|
XmlUtilities.parseOptionalBooleanAttr(zeroRegElem, "builtin", false);
|
||||||
|
|
||||||
GoRegisterInfo registerInfo =
|
GoRegisterInfo registerInfo =
|
||||||
new GoRegisterInfo(intRegs, floatRegs, stackInitialOffset, maxAlign,
|
new GoRegisterInfo(intRegs, floatRegs, stackInitialOffset, maxAlign,
|
||||||
currentGoRoutineReg, zeroReg);
|
currentGoRoutineReg, zeroReg, zeroRegIsBuiltin);
|
||||||
Map<GoVer, GoRegisterInfo> result = new HashMap<>();
|
Map<GoVer, GoRegisterInfo> result = new HashMap<>();
|
||||||
for (GoVer goVer : validGoVersions) {
|
for (GoVer goVer : validGoVersions) {
|
||||||
result.put(goVer, registerInfo);
|
result.put(goVer, registerInfo);
|
||||||
|
@ -165,7 +167,7 @@ public class GoRegisterInfoManager {
|
||||||
|
|
||||||
private GoRegisterInfo getDefault(Language lang) {
|
private GoRegisterInfo getDefault(Language lang) {
|
||||||
int goSize = lang.getInstructionAlignment();
|
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 {
|
private List<Register> parseRegListStr(String s, Language lang) throws IOException {
|
||||||
|
|
|
@ -66,6 +66,10 @@ public class GoFunctabEntry {
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getFuncoff() {
|
||||||
|
return funcoff;
|
||||||
|
}
|
||||||
|
|
||||||
private GoModuledata getModuledata() {
|
private GoModuledata getModuledata() {
|
||||||
return programContext.findContainingModuleByFuncData(context.getStructureStart());
|
return programContext.findContainingModuleByFuncData(context.getStructureStart());
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,21 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||||
return pclntable.isOffsetWithinData(offset, 1);
|
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() {
|
public boolean isValid() {
|
||||||
MemoryBlock txtBlock = programContext.getProgram().getMemory().getBlock(".text");
|
MemoryBlock txtBlock = programContext.getProgram().getMemory().getBlock(".text");
|
||||||
if (txtBlock != null && txtBlock.getStart().getOffset() != text) {
|
if (txtBlock != null && txtBlock.getStart().getOffset() != text) {
|
||||||
|
@ -153,6 +168,16 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||||
return funcnametab;
|
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
|
@Override
|
||||||
public StructureContext<GoModuledata> getStructureContext() {
|
public StructureContext<GoModuledata> getStructureContext() {
|
||||||
return structureContext;
|
return structureContext;
|
||||||
|
@ -168,15 +193,9 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||||
markupStringTable(funcnametab.getArrayAddress(), funcnametab.getLen(), session);
|
markupStringTable(funcnametab.getArrayAddress(), funcnametab.getLen(), session);
|
||||||
markupStringTable(filetab.getArrayAddress(), filetab.getLen(), session);
|
markupStringTable(filetab.getArrayAddress(), filetab.getLen(), session);
|
||||||
|
|
||||||
if (ftab.getLen() > 0) {
|
GoSlice subSlice = 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
|
|
||||||
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.markupArray("moduledata.ftab", GoFunctabEntry.class, false, session);
|
||||||
subSlice.markupArrayElements(GoFunctabEntry.class, session);
|
subSlice.markupArrayElements(GoFunctabEntry.class, session);
|
||||||
}
|
|
||||||
|
|
||||||
Structure textsectDT =
|
Structure textsectDT =
|
||||||
programContext.getGhidraDataType("runtime.textsect", Structure.class);
|
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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
|
@ -34,9 +35,13 @@ import ghidra.app.util.opinion.PeLoader;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.lang.Endian;
|
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.listing.Program;
|
||||||
import ghidra.program.model.mem.Memory;
|
import ghidra.program.model.mem.Memory;
|
||||||
import ghidra.program.model.mem.MemoryBlock;
|
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.Msg;
|
||||||
import ghidra.util.NumericUtilities;
|
import ghidra.util.NumericUtilities;
|
||||||
import ghidra.util.exception.CancelledException;
|
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<String, GoType> typeNameIndex = new HashMap<>();
|
||||||
private final Map<Long, DataType> cachedRecoveredDataTypes = new HashMap<>();
|
private final Map<Long, DataType> cachedRecoveredDataTypes = new HashMap<>();
|
||||||
private final List<GoModuledata> modules = new ArrayList<>();
|
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 mapGoType;
|
||||||
private GoType chanGoType;
|
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.
|
* Creates a GoRttiMapper using the specified bootstrap information.
|
||||||
|
@ -259,6 +271,41 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||||
return goVersion;
|
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
|
* Returns the first module data instance
|
||||||
*
|
*
|
||||||
|
@ -277,6 +324,40 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||||
modules.add(module);
|
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.
|
* Finds the {@link GoModuledata} that contains the specified offset.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -415,6 +496,13 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||||
return getGoType(addr.getOffset());
|
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
|
* Finds a go type by its go-type name, from the list of
|
||||||
* {@link #discoverGoTypes(TaskMonitor) discovered} go types.
|
* {@link #discoverGoTypes(TaskMonitor) discovered} go types.
|
||||||
|
@ -662,33 +750,28 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||||
* @throws CancelledException if cancelled
|
* @throws CancelledException if cancelled
|
||||||
*/
|
*/
|
||||||
public void discoverGoTypes(TaskMonitor monitor) throws IOException, CancelledException {
|
public void discoverGoTypes(TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
GoModuledata firstModule = findFirstModuledata(monitor);
|
|
||||||
if (firstModule == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addModule(firstModule);
|
|
||||||
|
|
||||||
UnknownProgressWrappingTaskMonitor upwtm =
|
UnknownProgressWrappingTaskMonitor upwtm =
|
||||||
new UnknownProgressWrappingTaskMonitor(monitor, 50);
|
new UnknownProgressWrappingTaskMonitor(monitor, 50);
|
||||||
upwtm.setMessage("Iterating Golang RTTI types");
|
upwtm.setMessage("Iterating Golang RTTI types");
|
||||||
upwtm.initialize(0);
|
upwtm.initialize(0);
|
||||||
|
|
||||||
goTypes.clear();
|
goTypes.clear();
|
||||||
|
typeNameIndex.clear();
|
||||||
Set<Long> discoveredTypes = new HashSet<>();
|
Set<Long> discoveredTypes = new HashSet<>();
|
||||||
for (Iterator<GoType> it = firstModule.iterateTypes(); it.hasNext();) {
|
for (GoModuledata module : modules) {
|
||||||
|
for (Iterator<GoType> it = module.iterateTypes(); it.hasNext();) {
|
||||||
upwtm.checkCancelled();
|
upwtm.checkCancelled();
|
||||||
upwtm.setProgress(discoveredTypes.size());
|
upwtm.setProgress(discoveredTypes.size());
|
||||||
|
|
||||||
GoType type = it.next();
|
GoType type = it.next();
|
||||||
type.discoverGoTypes(discoveredTypes);
|
type.discoverGoTypes(discoveredTypes);
|
||||||
}
|
}
|
||||||
typeNameIndex.clear();
|
}
|
||||||
for (GoType goType : goTypes.values()) {
|
for (GoType goType : goTypes.values()) {
|
||||||
String typeName = goType.getNameString();
|
String typeName = goType.getNameString();
|
||||||
typeNameIndex.put(typeName, goType);
|
typeNameIndex.put(typeName, goType);
|
||||||
}
|
}
|
||||||
Msg.info(this, "Found %d golang types".formatted(goTypes.size()));
|
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;
|
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() {
|
private void initHiddenCompilerTypes() {
|
||||||
|
|
|
@ -94,6 +94,13 @@ public class GoSlice {
|
||||||
return programContext.getDataAddress(array);
|
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() {
|
public long getLen() {
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.golang.rtti.types;
|
package ghidra.app.util.bin.format.golang.rtti.types;
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import ghidra.app.util.bin.format.golang.rtti.GoString;
|
import ghidra.app.util.bin.format.golang.rtti.GoString;
|
||||||
import ghidra.app.util.bin.format.golang.structmapping.StructureMapping;
|
import ghidra.app.util.bin.format.golang.structmapping.StructureMapping;
|
||||||
|
|
|
@ -56,6 +56,11 @@ public class GoStructType extends GoType {
|
||||||
return fields.readList(GoStructField.class);
|
return fields.readList(GoStructField.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEndOfTypeInfo() throws IOException {
|
||||||
|
return fields.getArrayEnd(GoStructField.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void additionalMarkup(MarkupSession session) throws IOException {
|
public void additionalMarkup(MarkupSession session) throws IOException {
|
||||||
super.additionalMarkup(session);
|
super.additionalMarkup(session);
|
||||||
|
|
|
@ -92,6 +92,19 @@ public abstract class GoType implements StructureMarkup<GoType> {
|
||||||
: 0);
|
: 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
|
@Markup
|
||||||
public GoUncommonType getUncommonType() throws IOException {
|
public GoUncommonType getUncommonType() throws IOException {
|
||||||
return typ.hasUncommonType()
|
return typ.hasUncommonType()
|
||||||
|
|
|
@ -15,9 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.golang.rtti.types;
|
package ghidra.app.util.bin.format.golang.rtti.types;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.app.util.bin.format.golang.rtti.*;
|
import ghidra.app.util.bin.format.golang.rtti.*;
|
||||||
import ghidra.app.util.bin.format.golang.structmapping.*;
|
import ghidra.app.util.bin.format.golang.structmapping.*;
|
||||||
|
@ -70,4 +69,18 @@ public class GoUncommonType {
|
||||||
return slice.readList(GoMethod.class);
|
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
|
* @param function the function
|
||||||
* @return true if the function has a default name.
|
* @return true if the function has a default name.
|
||||||
*/
|
*/
|
||||||
static boolean isDefaultFunctionName(Function function) {
|
public static boolean isDefaultFunctionName(Function function) {
|
||||||
String defaultFunctionName =
|
String defaultFunctionName =
|
||||||
SymbolUtilities.getDefaultFunctionName(function.getEntryPoint());
|
SymbolUtilities.getDefaultFunctionName(function.getEntryPoint());
|
||||||
return defaultFunctionName.equals(function.getName());
|
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>
|
</unaffected>
|
||||||
</prototype>
|
</prototype>
|
||||||
</default_proto>
|
</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">
|
<prototype name="__cdeclf" extrapop="4" stackshift="4">
|
||||||
<input>
|
<input>
|
||||||
<pentry minsize="1" maxsize="500" align="4">
|
<pentry minsize="1" maxsize="500" align="4">
|
||||||
|
|
|
@ -178,6 +178,68 @@
|
||||||
</unaffected>
|
</unaffected>
|
||||||
</prototype>
|
</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">
|
<prototype name="__stdcall" extrapop="8" stackshift="8">
|
||||||
<!-- Derived from "System V Application Binary Interface AMD64 Architecture Processor Supplement" April 2016 -->
|
<!-- Derived from "System V Application Binary Interface AMD64 Architecture Processor Supplement" April 2016 -->
|
||||||
<input>
|
<input>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue