Merge remote-tracking branch 'origin/GP-4465_dev747368_golang_macho_applesilicon--SQUASHED'

This commit is contained in:
Ryan Kurtz 2024-04-24 13:31:26 -04:00
commit 75d5737cce
20 changed files with 623 additions and 129 deletions

View File

@ -1,4 +1,7 @@
# Golang function names which do not return
#runtime.abort and runtime.systemstack_switch intentionally have bytes that can cause undefined instruction errors in Ghidra
runtime.abort
runtime.abort.abi0
runtime.exit.abi0
runtime.dieFromSignal
@ -7,6 +10,12 @@ runtime.fatal
runtime.fatalthrow
runtime.fatalpanic
runtime.throw
runtime.systemstack_switch
runtime.newstack
runtime.newstack.abi0
runtime.mstart
runtime.mstart.abi0
runtime.mstart0
runtime.gopanic
runtime.goPanicExtendIndex

View File

@ -9,6 +9,9 @@
<functionNamesFile>ElfFunctionsThatDoNotReturn</functionNamesFile>
</executable_format>
<executable_format name="Mac OS X Mach-O">
<compiler id="golang">
<functionNamesFile>GolangFunctionsThatDoNotReturn</functionNamesFile>
</compiler>
<functionNamesFile>MachOFunctionsThatDoNotReturn</functionNamesFile>
</executable_format>
<executable_format name="DYLD Cache">

View File

@ -179,13 +179,13 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
private void markupWellknownSymbols() throws IOException {
Program program = goBinary.getProgram();
Symbol g0 = SymbolUtilities.getUniqueSymbol(program, "runtime.g0");
Symbol g0 = goBinary.getGoSymbol("runtime.g0");
Structure gStruct = goBinary.getGhidraDataType("runtime.g", Structure.class);
if (g0 != null && gStruct != null) {
markupSession.markupAddressIfUndefined(g0.getAddress(), gStruct);
}
Symbol m0 = SymbolUtilities.getUniqueSymbol(program, "runtime.m0");
Symbol m0 = goBinary.getGoSymbol("runtime.m0");
Structure mStruct = goBinary.getGhidraDataType("runtime.m", Structure.class);
if (m0 != null && mStruct != null) {
markupSession.markupAddressIfUndefined(m0.getAddress(), mStruct);
@ -416,13 +416,13 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
Program program = goBinary.getProgram();
GoRegisterInfo goRegInfo = goBinary.getRegInfo();
MemoryBlock txtMemblock = program.getMemory().getBlock(".text");
if (txtMemblock != null && goRegInfo.getZeroRegister() != null &&
!goRegInfo.isZeroRegisterIsBuiltin()) {
if (goRegInfo.getZeroRegister() != null && !goRegInfo.isZeroRegisterIsBuiltin()) {
try {
for (AddressRange textRange : goBinary.getTextAddresses().getAddressRanges()) {
program.getProgramContext()
.setValue(goRegInfo.getZeroRegister(), txtMemblock.getStart(),
txtMemblock.getEnd(), BigInteger.ZERO);
.setValue(goRegInfo.getZeroRegister(), textRange.getMinAddress(),
textRange.getMaxAddress(), BigInteger.ZERO);
}
}
catch (ContextChangeException e) {
Msg.error(this, "Unexpected Error", e);
@ -432,7 +432,7 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
int alignment = goBinary.getPtrSize();
long sizeNeeded = 0;
Symbol zerobase = SymbolUtilities.getUniqueSymbol(program, "runtime.zerobase");
Symbol zerobase = goBinary.getGoSymbol("runtime.zerobase");
long zerobaseSymbol = sizeNeeded;
sizeNeeded += zerobase == null
? NumericUtilities.getUnsignedAlignedValue(1 /* sizeof(byte) */, alignment)
@ -464,14 +464,16 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
markupSession.labelAddress(gAddr, "CURRENT_G");
Register currentGoroutineReg = goRegInfo.getCurrentGoroutineRegister();
if (currentGoroutineReg != null && txtMemblock != null) {
if (currentGoroutineReg != null) {
// currentGoroutineReg is set in a platform's arch-golang.register.info in
// the <current_goroutine> element for arch's that have a dedicated processor
// register that points at G
try {
for (AddressRange textRange : goBinary.getTextAddresses().getAddressRanges()) {
program.getProgramContext()
.setValue(currentGoroutineReg, txtMemblock.getStart(),
txtMemblock.getEnd(), gAddr.getOffsetAsBigInteger());
.setValue(currentGoroutineReg, textRange.getMinAddress(),
textRange.getMaxAddress(), gAddr.getOffsetAsBigInteger());
}
}
catch (ContextChangeException e) {
Msg.error(this, "Unexpected Error", e);
@ -582,6 +584,7 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
int callingFunctionCount;
public PropagateRttiBackgroundCommand(GoRttiMapper goBinary) {
super("Golang RTTI Propagation (deferred)", true, true, false);
this.goBinary = goBinary;
}

View File

@ -37,8 +37,9 @@ public interface ElfInfoItem {
*/
void markupProgram(Program program, Address address);
public record ItemWithAddress<T> (T item, Address address) {};
public interface ReaderFunc<T extends ElfInfoItem> {
public record ItemWithAddress<T>(T item, Address address) {}
public interface ReaderFunc<T> {
T read(BinaryReader br, Program program) throws IOException;
}
@ -50,7 +51,7 @@ public interface ElfInfoItem {
* @param sectionName name of memory section that contains the item
* @param readFunc {@link ReaderFunc} that will deserialize an instance of the item
*/
public static void markupElfInfoItemSection(Program program, String sectionName,
static void markupElfInfoItemSection(Program program, String sectionName,
ReaderFunc<ElfInfoItem> readFunc) {
ItemWithAddress<ElfInfoItem> wrappedItem =
readItemFromSection(program, sectionName, readFunc);
@ -69,9 +70,13 @@ public interface ElfInfoItem {
* @return a wrapped instance of the item, or null if the memory section does not exist
* or there was an error while reading the item from the section
*/
public static <T extends ElfInfoItem> ItemWithAddress<T> readItemFromSection(Program program,
static <T extends ElfInfoItem> ItemWithAddress<T> readItemFromSection(Program program,
String sectionName, ReaderFunc<T> readFunc) {
MemoryBlock memBlock = program.getMemory().getBlock(sectionName);
return readItemFromSection(program, program.getMemory().getBlock(sectionName), readFunc);
}
static <T extends ElfInfoItem> ItemWithAddress<T> readItemFromSection(Program program,
MemoryBlock memBlock, ReaderFunc<T> readFunc) {
if (memBlock != null) {
try (ByteProvider bp =
MemoryByteProvider.createMemoryBlockByteProvider(program.getMemory(), memBlock)) {
@ -82,10 +87,9 @@ public interface ElfInfoItem {
}
catch (IOException e) {
Msg.warn(ElfInfoItem.class,
"Unable to read Elf item in section: %s".formatted(sectionName), e);
"Unable to read Elf item in section: %s".formatted(memBlock.getName()), e);
}
}
return null;
}
}

View File

@ -20,49 +20,52 @@ import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteArrayProvider;
import ghidra.app.util.bin.format.elf.info.ElfInfoItem;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.elf.info.ElfInfoItem.ItemWithAddress;
import ghidra.app.util.bin.format.elf.info.ElfInfoItem.ReaderFunc;
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.data.DataUtilities.ClearDataMode;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.Msg;
/**
* Similar to {@link NoteGoBuildId}, but re-implemented here because of the different
* serialization used in PE binaries. (the logic about the buildid payload is trivial so
* there is no worry about duplicating code)
* This class represents a go build id string, along with a magic header.
* <p>
*
* Similar to {@link NoteGoBuildId}, but re-implemented here because of the different
* serialization used.
*/
public class PEGoBuildId implements ElfInfoItem {
public class GoBuildId {
private static final byte[] GO_BUILDID_MAGIC =
"\u00ff Go build ID: \"".getBytes(StandardCharsets.ISO_8859_1);
private static final int BUILDID_STR_LEN = 83;
public static ItemWithAddress<PEGoBuildId> findBuildId(Program program) {
ItemWithAddress<PEGoBuildId> wrappedItem = ElfInfoItem.readItemFromSection(program,
".text", PEGoBuildId::read);
public static ItemWithAddress<GoBuildId> findBuildId(Program program) {
MemoryBlock txtBlock = GoRttiMapper.getGoSection(program, "text");
ItemWithAddress<GoBuildId> wrappedItem =
readItemFromSection(program, txtBlock, GoBuildId::read);
return wrappedItem;
}
/**
* Attempts to read a PEGoBuildId from the specified stream.
* Attempts to read a GoBuildId from the specified stream.
*
* @param br BinaryReader stream (typically the beginning of the ".text" section)
* @param program_notused not used, but needed to match functional interface
* @return PEGoBuildId instance, or null if not present
* @return GoBuildId instance, or null if not present
*/
public static PEGoBuildId read(BinaryReader br, Program program_notused) {
public static GoBuildId read(BinaryReader br, Program program_notused) {
try {
byte[] magic = br.readNextByteArray(GO_BUILDID_MAGIC.length);
if (!Arrays.equals(magic, GO_BUILDID_MAGIC)) {
return null;
}
String buildIdStr = br.readNextAsciiString(BUILDID_STR_LEN);
return new PEGoBuildId(buildIdStr);
return new GoBuildId(buildIdStr);
}
catch (IOException e) {
// fall thru and return null
@ -71,13 +74,13 @@ public class PEGoBuildId implements ElfInfoItem {
}
/**
* Attempts to read a PEGoBuildId from the specified InputStream (useful for early compiler
* Attempts to read a GoBuildId from the specified InputStream (useful for early compiler
* detection before file is loaded).
*
* @param is {@link InputStream} providing access to the ".text" section of a PE binary
* @return PEGoBuildId instance, or null if not present
* @return GoBuildId instance, or null if not present
*/
public static PEGoBuildId read(InputStream is) {
public static GoBuildId read(InputStream is) {
byte[] buffer = new byte[GO_BUILDID_MAGIC.length + BUILDID_STR_LEN];
try {
int bytesRead = is.read(buffer);
@ -94,7 +97,7 @@ public class PEGoBuildId implements ElfInfoItem {
private final String buildId;
public PEGoBuildId(String buildId) {
public GoBuildId(String buildId) {
this.buildId = buildId;
}
@ -102,7 +105,6 @@ public class PEGoBuildId implements ElfInfoItem {
return buildId;
}
@Override
public void markupProgram(Program program, Address address) {
program.getOptions(Program.PROGRAM_INFO)
.setString(NoteGoBuildId.PROGRAM_INFO_KEY, getBuildId());
@ -115,7 +117,7 @@ public class PEGoBuildId implements ElfInfoItem {
}
}
catch (CodeUnitInsertionException e) {
Msg.error(this, "Failed to markup PEGoBuildId at %s: %s".formatted(address, this));
Msg.error(this, "Failed to markup GoBuildId at %s: %s".formatted(address, this));
}
}
@ -128,4 +130,24 @@ public class PEGoBuildId implements ElfInfoItem {
return result;
}
//----------------------------------------------------------------------------------------------
static <T> ItemWithAddress<T> readItemFromSection(Program program, MemoryBlock memBlock,
ReaderFunc<T> readFunc) {
if (memBlock != null) {
try (ByteProvider bp =
MemoryByteProvider.createMemoryBlockByteProvider(program.getMemory(), memBlock)) {
BinaryReader br = new BinaryReader(bp, !program.getMemory().isBigEndian());
T item = readFunc.read(br, program);
return item != null ? new ItemWithAddress<>(item, memBlock.getStart()) : null;
}
catch (IOException e) {
Msg.warn(GoBuildId.class,
"Unable to read GoBuildId in section: %s".formatted(memBlock.getName()), e);
}
}
return null;
}
}

View File

@ -24,12 +24,14 @@ import java.util.*;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.elf.info.ElfInfoItem;
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
import ghidra.framework.options.Options;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.data.DataUtilities.ClearDataMode;
import ghidra.program.model.lang.Endian;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.*;
@ -39,7 +41,9 @@ import ghidra.util.*;
*/
public class GoBuildInfo implements ElfInfoItem {
public static final String SECTION_NAME = ".go.buildinfo";
public static final String SECTION_NAME = "go.buildinfo";
public static final String ELF_SECTION_NAME = ".go.buildinfo";
public static final String MACHO_SECTION_NAME = "go_buildinfo";
// Defined in golang src/debug/buildinfo/buildinfo.go
// NOTE: ISO_8859_1 charset is required to not mangle the \u00ff when converting to bytes
@ -73,17 +77,32 @@ public class GoBuildInfo implements ElfInfoItem {
* @return new {@link GoBuildInfo} instance, if present, null if missing or error
*/
public static ItemWithAddress<GoBuildInfo> findBuildInfo(Program program) {
// try as if binary is ELF
ItemWithAddress<GoBuildInfo> wrappedItem =
ElfInfoItem.readItemFromSection(program, SECTION_NAME, GoBuildInfo::read);
ItemWithAddress<GoBuildInfo> wrappedItem = readItemFromSection(program,
GoRttiMapper.getFirstGoSection(program, SECTION_NAME, MACHO_SECTION_NAME));
if (wrappedItem == null) {
// if not present, try common PE location for buildinfo, using "ElfInfoItem" logic
// even though this might be a PE binary, cause it doesn't matter
wrappedItem = ElfInfoItem.readItemFromSection(program, ".data", GoBuildInfo::read);
// if not present, try common PE location for buildinfo
wrappedItem = readItemFromSection(program, GoRttiMapper.getGoSection(program, "data"));
}
return wrappedItem;
}
private static ItemWithAddress<GoBuildInfo> readItemFromSection(Program program,
MemoryBlock memBlock) {
if (memBlock != null) {
try (ByteProvider bp =
MemoryByteProvider.createMemoryBlockByteProvider(program.getMemory(), memBlock)) {
BinaryReader br = new BinaryReader(bp, !program.getMemory().isBigEndian());
GoBuildInfo item = read(br, program);
return new ItemWithAddress<>(item, memBlock.getStart());
}
catch (IOException e) {
// fall thru, return null
}
}
return null;
}
/**
* Reads a GoBuildInfo ".go.buildinfo" section from the specified stream.
*

View File

@ -150,7 +150,7 @@ public class GoFunctionFixup {
paramDT.getLength());
VariableStorage varStorage = new VariableStorage(program, List.of(stackVarnode));
LocalVariableImpl localVar =
new LocalVariableImpl(param.getName() + "-spill", 0, paramDT, varStorage, program);
new LocalVariableImpl(param.getName() + "_spill", 0, paramDT, varStorage, program);
// TODO: needs more thought
func.addLocalVariable(localVar, SourceType.USER_DEFINED);
@ -229,12 +229,12 @@ public class GoFunctionFixup {
returnDT = multiReturn.getStruct();
for (DataTypeComponent dtc : multiReturn.getNormalStorageComponents()) {
allocateReturnStorage(program, dtc.getFieldName() + "-return-result-alias",
allocateReturnStorage(program, dtc.getFieldName() + "_return_result_alias",
dtc.getDataType(), storageAllocator, varnodes, returnResultAliasVars,
false);
}
for (DataTypeComponent dtc : multiReturn.getStackStorageComponents()) {
allocateReturnStorage(program, dtc.getFieldName() + "-return-result-alias",
allocateReturnStorage(program, dtc.getFieldName() + "_return_result_alias",
dtc.getDataType(), storageAllocator, varnodes, returnResultAliasVars,
false);
}
@ -249,7 +249,7 @@ public class GoFunctionFixup {
varnodes.add(new Varnode(GoRttiMapper.getZerobaseAddress(program), 1));
}
else {
allocateReturnStorage(program, "return-value-alias-variable", returnDT,
allocateReturnStorage(program, "return_value_alias_variable", returnDT,
storageAllocator, varnodes, returnResultAliasVars, true);
}

View File

@ -42,7 +42,7 @@ import ghidra.util.task.TaskMonitor;
*/
public class GolangElfInfoProducer implements ElfInfoProducer {
private static final Map<String, ReaderFunc<ElfInfoItem>> GOLANGINFO_READERS = Map.of(
GoBuildInfo.SECTION_NAME, GoBuildInfo::read,
GoBuildInfo.ELF_SECTION_NAME, GoBuildInfo::read,
NoteGoBuildId.SECTION_NAME, NoteGoBuildId::read);
private ElfLoadHelper elfLoadHelper;

View File

@ -21,14 +21,12 @@ import java.util.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.golang.rtti.types.GoType;
import ghidra.app.util.bin.format.golang.structmapping.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
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.SymbolUtilities;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ -50,10 +48,25 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
@MarkupReference
private long pcHeader; // pointer to the GoPcHeader instance, useful for bootstrapping
@FieldMapping
private long data;
@FieldMapping
private long edata;
@FieldMapping
@MarkupReference
private long text;
@FieldMapping
private long etext;
@FieldMapping
private long noptrdata;
@FieldMapping
private long enoptrdata;
@FieldMapping(fieldName = "types")
private long typesOffset;
@ -63,6 +76,9 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
@FieldMapping(optional = true)
private long gofunc;
@FieldMapping
private long end;
@FieldMapping(fieldName = "typelinks")
private GoSlice typeLinks;
@ -120,6 +136,24 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
return programContext.getCodeAddress(text);
}
public AddressRange getTextRange() {
Address textstart = getText();
Address textend = programContext.getCodeAddress(etext);
return new AddressRangeImpl(textstart, textend);
}
public AddressRange getRoDataRange() {
Address roStart = programContext.getCodeAddress(etext); // TODO: rodata is avail in newer govers
Address roEnd = programContext.getCodeAddress(end);
return new AddressRangeImpl(roStart, roEnd);
}
public AddressRange getDataRange() {
Address dataStart = programContext.getCodeAddress(data);
Address dataEnd = programContext.getCodeAddress(edata);
return new AddressRangeImpl(dataStart, dataEnd);
}
/**
* Returns the starting offset of type info
*
@ -190,12 +224,12 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
* @return true if this module data structure contains sane values
*/
public boolean isValid() {
MemoryBlock txtBlock = programContext.getProgram().getMemory().getBlock(".text");
MemoryBlock txtBlock = programContext.getGoSection("text");
if (txtBlock != null && !txtBlock.contains(getText())) {
return false;
}
MemoryBlock typelinkBlock = programContext.getProgram().getMemory().getBlock(".typelink");
MemoryBlock typelinkBlock = programContext.getGoSection("typelink");
if (typelinkBlock != null &&
typelinkBlock.getStart().getOffset() != typeLinks.getArrayOffset()) {
return false;
@ -396,8 +430,7 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
/* package */ static GoModuledata getFirstModuledata(GoRttiMapper context)
throws IOException {
Program program = context.getProgram();
Symbol firstModuleDataSymbol =
SymbolUtilities.getUniqueSymbol(program, "runtime.firstmoduledata");
Symbol firstModuleDataSymbol = GoRttiMapper.getGoSymbol(program, "runtime.firstmoduledata");
if (firstModuleDataSymbol == null) {
return null;
}

View File

@ -27,7 +27,6 @@ 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.SymbolUtilities;
import ghidra.util.task.TaskMonitor;
/**
@ -39,7 +38,7 @@ import ghidra.util.task.TaskMonitor;
@StructureMapping(structureName = "runtime.pcHeader")
public class GoPcHeader {
private static final String RUNTIME_PCLNTAB_SYMBOLNAME = "runtime.pclntab";
public static final String GOPCLNTAB_SECTION_NAME = ".gopclntab";
public static final String GOPCLNTAB_SECTION_NAME = "gopclntab";
public static final int GO_1_2_MAGIC = 0xfffffffb;
public static final int GO_1_16_MAGIC = 0xfffffffa;
public static final int GO_1_18_MAGIC = 0xfffffff0;
@ -51,12 +50,12 @@ public class GoPcHeader {
* @return {@link Address} of go pclntab, or null if not present
*/
public static Address getPclntabAddress(Program program) {
MemoryBlock pclntabBlock = program.getMemory().getBlock(GOPCLNTAB_SECTION_NAME);
MemoryBlock pclntabBlock = GoRttiMapper.getGoSection(program, GOPCLNTAB_SECTION_NAME);
if (pclntabBlock != null) {
return pclntabBlock.getStart();
}
// PE binaries have a symbol instead of a named section
Symbol pclntabSymbol = SymbolUtilities.getUniqueSymbol(program, RUNTIME_PCLNTAB_SYMBOLNAME);
Symbol pclntabSymbol = GoRttiMapper.getGoSymbol(program, RUNTIME_PCLNTAB_SYMBOLNAME);
return pclntabSymbol != null
? pclntabSymbol.getAddress()
: null;

View File

@ -35,8 +35,8 @@ import ghidra.app.util.bin.format.golang.*;
import ghidra.app.util.bin.format.golang.rtti.types.*;
import ghidra.app.util.bin.format.golang.structmapping.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.ElfLoader;
import ghidra.app.util.opinion.PeLoader;
import ghidra.app.util.opinion.*;
import ghidra.framework.Platform;
import ghidra.framework.store.LockException;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
@ -44,9 +44,9 @@ import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult;
import ghidra.program.model.data.StandAloneDataTypeManager.LanguageUpdateOption;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.*;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolType;
import ghidra.util.*;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
@ -210,10 +210,14 @@ public class GoRttiMapper extends DataTypeMapper {
else if (PeLoader.PE_NAME.equals(loaderName)) {
return "win";
}
else {
return null;
else if (MachoLoader.MACH_O_NAME.equals(loaderName)) {
LanguageID languageID = program.getLanguageCompilerSpecPair().getLanguageID();
if ("AARCH64:LE:64:AppleSilicon".equals(languageID.getIdAsString())) {
return Platform.MAC_ARM_64.getDirectoryName(); // mac_arm_64
}
}
return null;
}
/**
* Searches for a golang bootstrap gdt file that matches the specified Go version/size/OS.
@ -252,6 +256,58 @@ public class GoRttiMapper extends DataTypeMapper {
program.getCompilerSpec().getCompilerSpecDescription().getCompilerSpecName());
}
public static boolean hasGolangSections(List<String> sectionNames) {
for (String sectionName : sectionNames) {
if (sectionName.contains("gopclntab") ||
sectionName.contains(GoBuildInfo.MACHO_SECTION_NAME) ||
sectionName.contains(GoBuildInfo.SECTION_NAME)) {
return true;
}
}
return false;
}
private static final List<String> SYMBOL_SEARCH_PREFIXES = List.of("", "_" /* macho symbols */);
private static final List<String> SECTION_PREFIXES =
List.of("." /* ELF */, "__" /* macho sections */);
/**
* Returns a matching symbol from the specified program, using golang specific logic.
*
* @param program {@link Program}
* @param symbolName name of golang symbol
* @return {@link Symbol}, or null if not found
*/
public static Symbol getGoSymbol(Program program, String symbolName) {
for (String prefix : SYMBOL_SEARCH_PREFIXES) {
List<Symbol> symbols = program.getSymbolTable().getSymbols(prefix + symbolName, null);
if (symbols.size() == 1) {
return symbols.get(0);
}
}
return null;
}
public static MemoryBlock getGoSection(Program program, String sectionName) {
for (String prefix : SECTION_PREFIXES) {
MemoryBlock memBlock = program.getMemory().getBlock(prefix + sectionName);
if (memBlock != null) {
return memBlock;
}
}
return null;
}
public static MemoryBlock getFirstGoSection(Program program, String... blockNames) {
for (String blockToSearch : blockNames) {
MemoryBlock memBlock = getGoSection(program, blockToSearch);
if (memBlock != null) {
return memBlock;
}
}
return null;
}
/**
* Return the address of the golang zerobase symbol, or an artificial substitute.
* <p>
@ -261,7 +317,7 @@ public class GoRttiMapper extends DataTypeMapper {
* @return {@link Address} of the runtime.zerobase, or artificial substitute
*/
public static Address getZerobaseAddress(Program prog) {
Symbol zerobaseSym = SymbolUtilities.getUniqueSymbol(prog, "runtime.zerobase");
Symbol zerobaseSym = getGoSymbol(prog, "runtime.zerobase");
Address zerobaseAddr =
zerobaseSym != null ? zerobaseSym.getAddress() : getArtificalZerobaseAddress(prog);
if (zerobaseAddr == null) {
@ -276,8 +332,7 @@ public class GoRttiMapper extends DataTypeMapper {
"ARTIFICIAL.runtime.zerobase";
private static Address getArtificalZerobaseAddress(Program program) {
Symbol zerobaseSym =
SymbolUtilities.getUniqueSymbol(program, ARTIFICIAL_RUNTIME_ZEROBASE_SYMBOLNAME);
Symbol zerobaseSym = getGoSymbol(program, ARTIFICIAL_RUNTIME_ZEROBASE_SYMBOLNAME);
return zerobaseSym != null ? zerobaseSym.getAddress() : null;
}
@ -479,7 +534,7 @@ public class GoRttiMapper extends DataTypeMapper {
* @return {@link GoModuledata}
*/
public GoModuledata getFirstModule() {
return modules.get(0);
return !modules.isEmpty() ? modules.get(0) : null;
}
/**
@ -1370,14 +1425,14 @@ public class GoRttiMapper extends DataTypeMapper {
}
private AddressRange getPclntabSearchRange() {
MemoryBlock memBlock = getFirstMemoryBlock(program, ".noptrdata", ".rdata");
MemoryBlock memBlock = getFirstGoSection(program, "noptrdata", "rdata");
return memBlock != null
? new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd())
: null;
}
private AddressRange getModuledataSearchRange() {
MemoryBlock memBlock = getFirstMemoryBlock(program, ".noptrdata", ".data");
MemoryBlock memBlock = getFirstGoSection(program, "noptrdata", "data");
return memBlock != null
? new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd())
: null;
@ -1389,15 +1444,11 @@ public class GoRttiMapper extends DataTypeMapper {
* @return {@link AddressSetView} of range that is valid to find string structs in
*/
public AddressSetView getStringStructRange() {
MemoryBlock datamb = program.getMemory().getBlock(".data");
MemoryBlock rodatamb = getFirstMemoryBlock(program, ".rodata", ".rdata");
if (datamb == null || rodatamb == null) {
return new AddressSet();
AddressSet result = new AddressSet();
for (GoModuledata moduledata : modules) {
result.add(moduledata.getDataRange());
result.add(moduledata.getRoDataRange());
}
AddressSet result = new AddressSet(datamb.getStart(), datamb.getEnd());
result.add(rodatamb.getStart(), rodatamb.getEnd());
return result;
}
@ -1407,21 +1458,29 @@ public class GoRttiMapper extends DataTypeMapper {
* @return {@link AddressSetView} of range that is valid for string char[] data
*/
public AddressSetView getStringDataRange() {
MemoryBlock rodatamb = getFirstMemoryBlock(program, ".rodata", ".rdata");
return rodatamb != null
? new AddressSet(rodatamb.getStart(), rodatamb.getEnd())
: new AddressSet();
// TODO: initialized []byte("stringchars") slices can have data in noptrdata section
AddressSet result = new AddressSet();
for (GoModuledata moduledata : modules) {
result.add(moduledata.getRoDataRange());
}
return result;
}
private static MemoryBlock getFirstMemoryBlock(Program program, String... blockNames) {
Memory memory = program.getMemory();
for (String blockToSearch : blockNames) {
MemoryBlock memBlock = memory.getBlock(blockToSearch);
if (memBlock != null) {
return memBlock;
public AddressSetView getTextAddresses() {
AddressSet result = new AddressSet();
for (GoModuledata moduledata : modules) {
result.add(moduledata.getTextRange());
}
return result;
}
return null;
public Symbol getGoSymbol(String symbolName) {
return getGoSymbol(program, symbolName);
}
public MemoryBlock getGoSection(String sectionName) {
return getGoSection(program, sectionName);
}
//--------------------------------------------------------------------------------------------

View File

@ -23,6 +23,8 @@ import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.Option;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.ByteProviderWrapper;
import ghidra.app.util.bin.format.golang.GoConstants;
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.bin.format.swift.SwiftUtils;
import ghidra.app.util.bin.format.ubi.*;
@ -61,12 +63,7 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
MachHeader machHeader = new MachHeader(provider);
String magic =
CpuTypes.getMagicString(machHeader.getCpuType(), machHeader.getCpuSubType());
List<String> sectionNames = machHeader.parseSegments()
.stream()
.flatMap(seg -> seg.getSections().stream())
.map(section -> section.getSectionName())
.toList();
String compiler = SwiftUtils.isSwift(sectionNames) ? "swift" : null;
String compiler = detectCompilerName(machHeader);
List<QueryResult> results = QueryOpinionService.query(MACH_O_NAME, magic, compiler);
for (QueryResult result : results) {
loadSpecs.add(new LoadSpec(this, machHeader.getImageBase(), result));
@ -81,6 +78,19 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
return loadSpecs;
}
private String detectCompilerName(MachHeader machHeader) throws IOException {
List<String> sectionNames = machHeader.parseSegments()
.stream()
.flatMap(seg -> seg.getSections().stream())
.map(section -> section.getSectionName())
.toList();
String compiler = SwiftUtils.isSwift(sectionNames) ? "swift" : null;
compiler = compiler == null && GoRttiMapper.hasGolangSections(sectionNames)
? GoConstants.GOLANG_CSPEC_NAME
: null;
return compiler;
}
@Override
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program, TaskMonitor monitor, MessageLog log) throws IOException {

View File

@ -24,6 +24,10 @@ import ghidra.app.plugin.core.analysis.rust.RustUtilities;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.RelocationException;
import ghidra.app.util.bin.format.elf.info.ElfInfoItem.ItemWithAddress;
import ghidra.app.util.bin.format.golang.GoBuildId;
import ghidra.app.util.bin.format.golang.GoBuildInfo;
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.bin.format.macho.commands.*;
import ghidra.app.util.bin.format.macho.commands.ExportTrie.ExportEntry;
@ -154,6 +158,9 @@ public class MachoProgramBuilder {
// Set program info
setRelocatableProperty();
setProgramDescription();
if (GoRttiMapper.isGolangProgram(program)) {
markupAndSetGolangInitialProgramProperties();
}
// Perform additional actions
renameObjMsgSendRtpSymbol();
@ -1791,6 +1798,17 @@ public class MachoProgramBuilder {
}
}
protected void markupAndSetGolangInitialProgramProperties() {
ItemWithAddress<GoBuildId> buildId = GoBuildId.findBuildId(program);
if (buildId != null) {
buildId.item().markupProgram(program, buildId.address());
}
ItemWithAddress<GoBuildInfo> buildInfo = GoBuildInfo.findBuildInfo(program);
if (buildInfo != null) {
buildInfo.item().markupProgram(program, buildInfo.address());
}
}
protected void setCompiler() {
// Check for Rust
try {

View File

@ -29,7 +29,7 @@ import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.elf.info.ElfInfoItem.ItemWithAddress;
import ghidra.app.util.bin.format.golang.GoBuildInfo;
import ghidra.app.util.bin.format.golang.PEGoBuildId;
import ghidra.app.util.bin.format.golang.GoBuildId;
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
import ghidra.app.util.bin.format.mz.DOSHeader;
import ghidra.app.util.bin.format.pe.*;
@ -298,7 +298,7 @@ public class PeLoader extends AbstractPeDebugLoader {
private void processGolangProperties(OptionalHeader optionalHeader, NTHeader ntHeader,
Program prog, TaskMonitor monitor) {
ItemWithAddress<PEGoBuildId> buildId = PEGoBuildId.findBuildId(prog);
ItemWithAddress<GoBuildId> buildId = GoBuildId.findBuildId(prog);
if (buildId != null) {
buildId.item().markupProgram(prog, buildId.address());
}
@ -1141,7 +1141,7 @@ public class PeLoader extends AbstractPeDebugLoader {
SectionHeader textSection = pe.getNTHeader().getFileHeader().getSectionHeader(".text");
if (textSection != null) {
try (InputStream is = textSection.getDataStream()) {
PEGoBuildId buildId = PEGoBuildId.read(is);
GoBuildId buildId = GoBuildId.read(is);
buildIdPresent = buildId != null;
}
catch (IOException e) {

View File

@ -11,6 +11,8 @@ data/languages/AARCH64BE.slaspec||GHIDRA||||END|
data/languages/AARCH64_AMXext.sinc||GHIDRA||||END|
data/languages/AARCH64_AppleSilicon.slaspec||GHIDRA||||END|
data/languages/AARCH64_base_PACoptions.sinc||GHIDRA||||END|
data/languages/AARCH64_golang.cspec||GHIDRA||||END|
data/languages/AARCH64_golang.register.info||GHIDRA||||END|
data/languages/AARCH64_ilp32.cspec||GHIDRA||||END|
data/languages/AARCH64_swift.cspec||GHIDRA||||END|
data/languages/AARCH64_win.cspec||GHIDRA||||END|

View File

@ -12,6 +12,7 @@
<description>Generic ARM64 v8.5-A LE instructions, LE data, missing some 8.5 vector</description>
<compiler name="default" spec="AARCH64.cspec" id="default"/>
<compiler name="Visual Studio" spec="AARCH64_win.cspec" id="windows"/>
<compiler name="golang" spec="AARCH64_golang.cspec" id="golang"/>
<external_name tool="gnu" name="aarch64"/>
<external_name tool="DWARF.register.mapping.file" name="AARCH64.dwarf"/>
<external_name tool="qemu" name="qemu-aarch64"/>
@ -28,8 +29,10 @@
id="AARCH64:BE:64:v8A">
<description>Generic ARM64 v8.5-A LE instructions, BE data, missing some 8.5 vector</description>
<compiler name="default" spec="AARCH64.cspec" id="default"/>
<compiler name="golang" spec="AARCH64_golang.cspec" id="golang"/>
<external_name tool="gnu" name="aarch64"/>
<external_name tool="DWARF.register.mapping.file" name="AARCH64.dwarf"/>
<external_name tool="Golang.register.info.file" name="AARCH64_golang.register.info"/>
<external_name tool="qemu" name="qemu-aarch64_be"/>
</language>
<language processor="AARCH64"
@ -44,6 +47,7 @@
<description>Generic ARM64 v8.5-A LE instructions, LE data, ilp32</description>
<truncate_space space="ram" size="4"/>
<compiler name="default" spec="AARCH64_ilp32.cspec" id="default"/>
<compiler name="golang" spec="AARCH64_golang.cspec" id="golang"/>
<external_name tool="gnu" name="aarch64:ilp32"/>
<external_name tool="DWARF.register.mapping.file" name="AARCH64.dwarf"/>
<external_name tool="qemu" name="qemu-aarch64"/>
@ -61,6 +65,7 @@
<description>Generic ARM64 v8.5-A LE instructions, BE data, ilp32</description>
<truncate_space space="ram" size="4"/>
<compiler name="default" spec="AARCH64_ilp32.cspec" id="default"/>
<compiler name="golang" spec="AARCH64_golang.cspec" id="golang"/>
<external_name tool="gnu" name="aarch64:ilp32"/>
<external_name tool="DWARF.register.mapping.file" name="AARCH64.dwarf"/>
<external_name tool="qemu" name="qemu-aarch64_be"/>

View File

@ -12,6 +12,9 @@
<constraint loader="Mac OS X Mach-O" compilerSpecID="swift">
<constraint primary="16777228" secondary="swift" processor="AARCH64" endian="little" size="64" variant="AppleSilicon" />
</constraint>
<constraint loader="Mac OS X Mach-O" compilerSpecID="golang">
<constraint primary="16777228" secondary="golang" processor="AARCH64" endian="little" size="64" variant="AppleSilicon" />
</constraint>
<constraint loader="DYLD Cache" compilerSpecID="default">
<constraint primary="AARCH64" processor="AARCH64" endian="little" size="64" variant="AppleSilicon" />
<constraint primary="ARM64_32" processor="AARCH64" endian="little" size="32" variant="ilp32" />

View File

@ -0,0 +1,293 @@
<?xml version="1.0" encoding="UTF-8"?>
<compiler_spec>
<data_organization>
<absolute_max_alignment value="0" />
<machine_alignment value="2" />
<default_alignment value="1" />
<default_pointer_alignment value="8" />
<pointer_size value="8" />
<wchar_size value="4" /> <!-- matches go's 'rune' -->
<short_size value="2" />
<integer_size value="8" />
<long_size value="8" />
<long_long_size value="8" />
<float_size value="4" />
<double_size value="8" />
<long_double_size value="16" />
<size_alignment_map>
<entry size="1" alignment="1" />
<entry size="2" alignment="2" />
<entry size="4" alignment="4" />
<entry size="8" alignment="8" />
</size_alignment_map>
</data_organization>
<global>
<range space="ram"/>
</global>
<context_data>
</context_data>
<stackpointer register="sp" space="ram"/>
<funcptr align="4"/> <!-- Function pointers are word aligned and leastsig bit may encode otherstuff -->
<returnaddress>
<varnode space="stack" offset="0" size="8"/>
</returnaddress>
<default_proto>
<prototype name="abi-internal" extrapop="8" stackshift="8">
<input>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q0"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q1"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q2"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q3"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q4"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q5"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q6"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q7"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q8"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q9"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q10"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q11"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q12"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q13"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q14"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q15"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x0"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x1"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x2"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x3"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x4"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x5"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x6"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x7"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x8"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x9"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x10"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x11"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x12"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x13"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x14"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x15"/>
</pentry>
<pentry minsize="1" maxsize="500" align="16">
<addr offset="8" space="stack"/>
</pentry>
</input>
<output>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="q0"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x0"/>
</pentry>
<pentry minsize="9" maxsize="16">
<addr space="join" piece2="x0" piece1="x1"/>
</pentry>
<pentry minsize="17" maxsize="24">
<addr space="join" piece3="x0" piece2="x1" piece1="x2"/>
</pentry>
<pentry minsize="25" maxsize="32">
<addr space="join" piece4="x0" piece3="x1" piece2="x2" piece1="x3"/>
</pentry>
<pentry minsize="33" maxsize="40">
<addr space="join" piece5="x0" piece4="x1" piece3="x2" piece2="x3" piece1="x4"/>
</pentry>
<pentry minsize="41" maxsize="48">
<addr space="join" piece6="x0" piece5="x1" piece4="x2" piece3="x3" piece2="x4" piece1="x5"/>
</pentry>
<pentry minsize="49" maxsize="56">
<addr space="join" piece7="x0" piece6="x1" piece5="x2" piece4="x3" piece3="x4" piece2="x5" piece1="x6"/>
</pentry>
<pentry minsize="57" maxsize="64">
<addr space="join" piece8="x0" piece7="x1" piece6="x2" piece5="x3" piece4="x4" piece3="x5" piece2="x6" piece1="x7"/>
</pentry>
<pentry minsize="65" maxsize="72">
<addr space="join" piece9="x0" piece8="x1" piece7="x2" piece6="x3" piece5="x4" piece4="x5" piece3="x6" piece2="x7" piece1="x8"/>
</pentry>
</output>
<killedbycall>
<register name="x0"/>
<register name="x1"/>
<register name="x2"/>
<register name="x3"/>
<register name="x4"/>
<register name="x5"/>
<register name="x6"/>
<register name="x7"/>
<register name="x8"/>
<register name="x9"/>
<register name="x10"/>
<register name="x11"/>
<register name="x12"/>
<register name="x13"/>
<register name="x14"/>
<register name="x15"/>
</killedbycall>
<unaffected>
<register name="x16"/>
<register name="x17"/>
</unaffected>
</prototype>
</default_proto>
<prototype name="abi0" extrapop="8" stackshift="8">
<input>
<pentry minsize="1" maxsize="500" align="8">
<addr offset="8" space="stack"/>
</pentry>
</input>
<output>
</output>
<killedbycall>
<register name="x0"/>
<register name="x1"/>
<register name="x2"/>
<register name="x3"/>
<register name="x4"/>
<register name="x5"/>
<register name="x6"/>
<register name="x7"/>
<register name="x8"/>
<register name="x9"/>
<register name="x10"/>
<register name="x11"/>
<register name="x12"/>
<register name="x13"/>
<register name="x14"/>
<register name="x15"/>
</killedbycall>
<unaffected>
<register name="x16"/>
<register name="x17"/>
</unaffected>
</prototype>
<prototype name="duffzero" extrapop="8" stackshift="8">
<input>
<pentry minsize="1" maxsize="8">
<register name="x20"/>
</pentry>
</input>
<output>
<pentry minsize="1" maxsize="8">
<register name="x20"/>
</pentry>
</output>
<killedbycall>
<register name="x20"/>
</killedbycall>
<unaffected>
<register name="x16"/>
<register name="x17"/>
</unaffected>
</prototype>
<prototype name="duffcopy" extrapop="8" stackshift="8">
<input>
<pentry minsize="1" maxsize="8">
<register name="x21"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="x20"/>
</pentry>
</input>
<output>
<pentry minsize="1" maxsize="8">
<register name="x21"/>
</pentry>
<pentry minsize="9" maxsize="16">
<addr space="join" piece2="x21" piece1="x20"/>
</pentry>
</output>
<killedbycall>
<register name="x21"/>
<register name="x20"/>
</killedbycall>
<unaffected>
<register name="x16"/>
<register name="x17"/>
</unaffected>
</prototype>
</compiler_spec>

View File

@ -0,0 +1,10 @@
<golang>
<!-- see https://github.com/golang/go/blob/master/src/internal/abi/abi_arm64.go -->
<register_info versions="V1_17,V1_18,V1_19,V1_20,V1_21">
<int_registers list="x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15"/>
<float_registers list="q0,q1,q2,q3,q4,q5,q6,q7,q8,q9,q10,q11,q12,q13,q14,q15"/>
<stack initialoffset="8" maxalign="8"/>
<current_goroutine register="x28"/>
<zero_register register="ZR"/>
</register_info>
</golang>

View File

@ -12,8 +12,10 @@
<description>AppleSilicon ARM v8.5-A LE instructions, LE data, AMX extensions</description>
<compiler name="default" spec="AARCH64.cspec" id="default"/>
<compiler name="Swift" spec="AARCH64_swift.cspec" id="swift"/>
<compiler name="golang" spec="AARCH64_golang.cspec" id="golang"/>
<external_name tool="gnu" name="aarch64"/>
<external_name tool="gnu" name="aarch64:ilp32"/>
<external_name tool="DWARF.register.mapping.file" name="AARCH64.dwarf"/>
<external_name tool="Golang.register.info.file" name="AARCH64_golang.register.info"/>
</language>
</language_definitions>