From 63512f375901c9ef93cacaf6ce6f8f0e6a4b9b54 Mon Sep 17 00:00:00 2001 From: dev747368 <48332326+dev747368@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:04:54 -0400 Subject: [PATCH] GP-4465 get Golang analysis working on AARCH64, AppleSilicon + MachO Inspired by PR #6157 (by seekbytes), adds support for Apple MachO AARCH64 binaries to existing golang analyzer. --- .../Base/data/GolangFunctionsThatDoNotReturn | 9 + .../Base/data/noReturnFunctionConstraints.xml | 51 +-- .../core/analysis/GolangSymbolAnalyzer.java | 29 +- .../util/bin/format/elf/info/ElfInfoItem.java | 18 +- .../{PEGoBuildId.java => GoBuildId.java} | 64 ++-- .../util/bin/format/golang/GoBuildInfo.java | 33 +- .../bin/format/golang/GoFunctionFixup.java | 8 +- .../format/golang/GolangElfInfoProducer.java | 2 +- .../bin/format/golang/rtti/GoModuledata.java | 47 ++- .../bin/format/golang/rtti/GoPcHeader.java | 7 +- .../bin/format/golang/rtti/GoRttiMapper.java | 123 ++++++-- .../ghidra/app/util/opinion/MachoLoader.java | 22 +- .../app/util/opinion/MachoProgramBuilder.java | 18 ++ .../ghidra/app/util/opinion/PeLoader.java | 6 +- .../Processors/AARCH64/certification.manifest | 2 + .../AARCH64/data/languages/AARCH64.ldefs | 5 + .../AARCH64/data/languages/AARCH64.opinion | 3 + .../data/languages/AARCH64_golang.cspec | 293 ++++++++++++++++++ .../languages/AARCH64_golang.register.info | 10 + .../AARCH64/data/languages/AppleSilicon.ldefs | 2 + 20 files changed, 623 insertions(+), 129 deletions(-) rename Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/{PEGoBuildId.java => GoBuildId.java} (62%) create mode 100644 Ghidra/Processors/AARCH64/data/languages/AARCH64_golang.cspec create mode 100644 Ghidra/Processors/AARCH64/data/languages/AARCH64_golang.register.info diff --git a/Ghidra/Features/Base/data/GolangFunctionsThatDoNotReturn b/Ghidra/Features/Base/data/GolangFunctionsThatDoNotReturn index a099b15bce..cd244d3fca 100644 --- a/Ghidra/Features/Base/data/GolangFunctionsThatDoNotReturn +++ b/Ghidra/Features/Base/data/GolangFunctionsThatDoNotReturn @@ -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 diff --git a/Ghidra/Features/Base/data/noReturnFunctionConstraints.xml b/Ghidra/Features/Base/data/noReturnFunctionConstraints.xml index c4e7bfc079..d5647e46c9 100644 --- a/Ghidra/Features/Base/data/noReturnFunctionConstraints.xml +++ b/Ghidra/Features/Base/data/noReturnFunctionConstraints.xml @@ -1,26 +1,29 @@ - - - GolangFunctionsThatDoNotReturn - - - RustFunctionsThatDoNotReturn - - ElfFunctionsThatDoNotReturn - - - MachOFunctionsThatDoNotReturn - - - MachOFunctionsThatDoNotReturn - - - - GolangFunctionsThatDoNotReturn - - - RustFunctionsThatDoNotReturn - - PEFunctionsThatDoNotReturn - + + + GolangFunctionsThatDoNotReturn + + + RustFunctionsThatDoNotReturn + + ElfFunctionsThatDoNotReturn + + + + GolangFunctionsThatDoNotReturn + + MachOFunctionsThatDoNotReturn + + + MachOFunctionsThatDoNotReturn + + + + GolangFunctionsThatDoNotReturn + + + RustFunctionsThatDoNotReturn + + PEFunctionsThatDoNotReturn + diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java index 0986588a6e..702c2c0f62 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java @@ -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 { - program.getProgramContext() - .setValue(goRegInfo.getZeroRegister(), txtMemblock.getStart(), - txtMemblock.getEnd(), BigInteger.ZERO); + for (AddressRange textRange : goBinary.getTextAddresses().getAddressRanges()) { + program.getProgramContext() + .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 element for arch's that have a dedicated processor // register that points at G try { - program.getProgramContext() - .setValue(currentGoroutineReg, txtMemblock.getStart(), - txtMemblock.getEnd(), gAddr.getOffsetAsBigInteger()); + for (AddressRange textRange : goBinary.getTextAddresses().getAddressRanges()) { + program.getProgramContext() + .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; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/info/ElfInfoItem.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/info/ElfInfoItem.java index 7de89c3250..f7cbf0ad8f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/info/ElfInfoItem.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/info/ElfInfoItem.java @@ -37,8 +37,9 @@ public interface ElfInfoItem { */ void markupProgram(Program program, Address address); - public record ItemWithAddress (T item, Address address) {}; - public interface ReaderFunc { + public record ItemWithAddress(T item, Address address) {} + + public interface ReaderFunc { 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 readFunc) { ItemWithAddress 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 ItemWithAddress readItemFromSection(Program program, + static ItemWithAddress readItemFromSection(Program program, String sectionName, ReaderFunc readFunc) { - MemoryBlock memBlock = program.getMemory().getBlock(sectionName); + return readItemFromSection(program, program.getMemory().getBlock(sectionName), readFunc); + } + + static ItemWithAddress readItemFromSection(Program program, + MemoryBlock memBlock, ReaderFunc 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; } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/PEGoBuildId.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoBuildId.java similarity index 62% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/PEGoBuildId.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoBuildId.java index f8056844bb..b26cdf7cb7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/PEGoBuildId.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoBuildId.java @@ -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. *

- * + * 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 findBuildId(Program program) { - ItemWithAddress wrappedItem = ElfInfoItem.readItemFromSection(program, - ".text", PEGoBuildId::read); + public static ItemWithAddress findBuildId(Program program) { + MemoryBlock txtBlock = GoRttiMapper.getGoSection(program, "text"); + ItemWithAddress 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 ItemWithAddress readItemFromSection(Program program, MemoryBlock memBlock, + ReaderFunc 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; + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoBuildInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoBuildInfo.java index 6a4cfac76e..ad978d49e0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoBuildInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoBuildInfo.java @@ -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 findBuildInfo(Program program) { - // try as if binary is ELF - ItemWithAddress wrappedItem = - ElfInfoItem.readItemFromSection(program, SECTION_NAME, GoBuildInfo::read); + ItemWithAddress 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 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. * diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoFunctionFixup.java index 4e410dd72b..8995a86f34 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoFunctionFixup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoFunctionFixup.java @@ -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); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GolangElfInfoProducer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GolangElfInfoProducer.java index cd2d2adc6e..2654b53efd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GolangElfInfoProducer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GolangElfInfoProducer.java @@ -42,7 +42,7 @@ import ghidra.util.task.TaskMonitor; */ public class GolangElfInfoProducer implements ElfInfoProducer { private static final Map> GOLANGINFO_READERS = Map.of( - GoBuildInfo.SECTION_NAME, GoBuildInfo::read, + GoBuildInfo.ELF_SECTION_NAME, GoBuildInfo::read, NoteGoBuildId.SECTION_NAME, NoteGoBuildId::read); private ElfLoadHelper elfLoadHelper; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoModuledata.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoModuledata.java index 2b8eb37cc1..b307b04965 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoModuledata.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoModuledata.java @@ -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 { @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 { @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 { 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 { * @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 { /* 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; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoPcHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoPcHeader.java index cc7c620105..a56b6d339f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoPcHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoPcHeader.java @@ -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; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoRttiMapper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoRttiMapper.java index 4a8e8853e3..34a9353539 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoRttiMapper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoRttiMapper.java @@ -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,9 +210,13 @@ 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; } /** @@ -252,6 +256,58 @@ public class GoRttiMapper extends DataTypeMapper { program.getCompilerSpec().getCompilerSpecDescription().getCompilerSpecName()); } + public static boolean hasGolangSections(List 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 SYMBOL_SEARCH_PREFIXES = List.of("", "_" /* macho symbols */); + private static final List 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 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. *

@@ -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 null; + return result; + } + + + public Symbol getGoSymbol(String symbolName) { + return getGoSymbol(program, symbolName); + } + + public MemoryBlock getGoSection(String sectionName) { + return getGoSection(program, sectionName); } //-------------------------------------------------------------------------------------------- diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java index 964a9066d0..e968c2d351 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java @@ -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 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 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 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