diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest index 35177a917d..8f6c07bb61 100644 --- a/Ghidra/Features/Base/certification.manifest +++ b/Ghidra/Features/Base/certification.manifest @@ -87,6 +87,8 @@ data/symbols/win64/mfc90u.exports||GHIDRA||||END| data/symbols/win64/msvcrt.hints||GHIDRA||||END| data/typeinfo/generic/generic_clib.gdt||GHIDRA||||END| data/typeinfo/generic/generic_clib_64.gdt||GHIDRA||||END| +data/typeinfo/golang/golang_1.15_anybit_any.gdt||GHIDRA||||END| +data/typeinfo/golang/golang_1.16_anybit_any.gdt||GHIDRA||||END| data/typeinfo/golang/golang_1.17_anybit_any.gdt||GHIDRA||||END| data/typeinfo/golang/golang_1.18_anybit_any.gdt||GHIDRA||||END| data/typeinfo/golang/golang_1.19_anybit_any.gdt||GHIDRA||||END| diff --git a/Ghidra/Features/Base/data/typeinfo/golang/golang_1.15_anybit_any.gdt b/Ghidra/Features/Base/data/typeinfo/golang/golang_1.15_anybit_any.gdt new file mode 100644 index 0000000000..a7de3bdab2 Binary files /dev/null and b/Ghidra/Features/Base/data/typeinfo/golang/golang_1.15_anybit_any.gdt differ diff --git a/Ghidra/Features/Base/data/typeinfo/golang/golang_1.16_anybit_any.gdt b/Ghidra/Features/Base/data/typeinfo/golang/golang_1.16_anybit_any.gdt new file mode 100644 index 0000000000..045d020eb6 Binary files /dev/null and b/Ghidra/Features/Base/data/typeinfo/golang/golang_1.16_anybit_any.gdt differ diff --git a/Ghidra/Features/Base/ghidra_scripts/FixupGolangFuncParamStorageScript.java b/Ghidra/Features/Base/ghidra_scripts/FixupGolangFuncParamStorageScript.java index 98768f34c7..5d24e21592 100644 --- a/Ghidra/Features/Base/ghidra_scripts/FixupGolangFuncParamStorageScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/FixupGolangFuncParamStorageScript.java @@ -18,11 +18,10 @@ // specified for the arch. //@category Functions //@menupath Tools.Fix Golang Function Param Storage -import java.util.List; - import ghidra.app.script.GhidraScript; import ghidra.app.util.bin.format.golang.GoFunctionFixup; import ghidra.app.util.bin.format.golang.GoVer; +import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper; import ghidra.program.model.listing.Function; public class FixupGolangFuncParamStorageScript extends GhidraScript { @@ -34,10 +33,9 @@ public class FixupGolangFuncParamStorageScript extends GhidraScript { return; } GoVer goVersion = GoVer.fromProgramProperties(currentProgram); - if ( goVersion == GoVer.UNKNOWN ) { - List versions = List.of(GoVer.values()); - goVersion = - askChoice("Golang Version", "What is the golang version?", versions, GoVer.UNKNOWN); + if (goVersion == GoVer.INVALID) { + goVersion = askChoice("Golang Version", "What is the golang version?", + GoRttiMapper.getAllSupportedVersions(), GoVer.INVALID); } println("Fixing param storage for function %s@%s".formatted(func.getName(), func.getEntryPoint())); 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 702c2c0f62..ace8e52179 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 @@ -177,8 +177,6 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer { } private void markupWellknownSymbols() throws IOException { - Program program = goBinary.getProgram(); - Symbol g0 = goBinary.getGoSymbol("runtime.g0"); Structure gStruct = goBinary.getGhidraDataType("runtime.g", Structure.class); if (g0 != null && gStruct != null) { @@ -317,31 +315,25 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer { Program program = goBinary.getProgram(); GoRegisterInfo regInfo = goBinary.getRegInfo(); DataType voidPtr = program.getDataTypeManager().getPointer(VoidDataType.dataType); - DataType uintDT = goBinary.getTypeOrDefault("uint", DataType.class, - AbstractIntegerDataType.getUnsignedDataType(goBinary.getPtrSize(), null)); GoFuncData duffzeroFuncdata = goBinary.getFunctionByName("runtime.duffzero"); Function duffzeroFunc = duffzeroFuncdata != null ? program.getFunctionManager().getFunctionAt(duffzeroFuncdata.getFuncAddress()) : null; - if (duffzeroFunc != null && - goBinary.hasCallingConvention(GOLANG_DUFFZERO_CALLINGCONVENTION_NAME)) { + List duffzeroParams = regInfo.getDuffzeroParams(program); + if (duffzeroFunc != null && !duffzeroParams.isEmpty()) { + // NOTE: some go archs don't create duffzero functions. See + // cmd/compile/internal/ssa/config.go and look for flag noDuffDevice in each arch. try { - // 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 params = new ArrayList<>(); - params.add(new ParameterImpl("dest", voidPtr, program)); - if (needZeroValueParam) { - params.add(new ParameterImpl("zeroValue", uintDT, program)); - } - duffzeroFunc.updateFunction(GOLANG_DUFFZERO_CALLINGCONVENTION_NAME, - new ReturnParameterImpl(VoidDataType.dataType, program), params, - FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.ANALYSIS); + // NOTE: even though we are specifying custom storage for the arguments, the + // calling convention name is still important as it tells the decompiler which + // registers are unaffected vs killed-by-call + + ReturnParameterImpl voidRet = new ReturnParameterImpl(VoidDataType.dataType, + VariableStorage.VOID_STORAGE, program); + duffzeroFunc.updateFunction(GOLANG_DUFFZERO_CALLINGCONVENTION_NAME, voidRet, + duffzeroParams, FunctionUpdateType.CUSTOM_STORAGE, true, SourceType.ANALYSIS); markupSession.appendComment(duffzeroFunc, null, "Golang special function: duffzero"); @@ -527,6 +519,7 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer { String duffComment = program.getListing() .getCodeUnitAt(duffFunc.getEntryPoint()) .getComment(CodeUnit.PLATE_COMMENT); + monitor.setMessage("Fixing alternate duffzero/duffcopy entry points"); for (FunctionIterator funcIt = program.getFunctionManager().getFunctions(funcBody, true); funcIt.hasNext();) { @@ -538,9 +531,11 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer { func.setName(duffFunc.getName() + "_" + func.getEntryPoint(), SourceType.ANALYSIS); func.setParentNamespace(funcNS); + FunctionUpdateType fut = duffFunc.hasCustomVariableStorage() + ? FunctionUpdateType.CUSTOM_STORAGE + : FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS; func.updateFunction(ccName, duffFunc.getReturn(), - Arrays.asList(duffFunc.getParameters()), - FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.ANALYSIS); + Arrays.asList(duffFunc.getParameters()), fut, true, SourceType.ANALYSIS); if (duffComment != null && !duffComment.isBlank()) { new SetCommentCmd(func.getEntryPoint(), CodeUnit.PLATE_COMMENT, duffComment) .applyTo(program); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java index 3bf7b5ce62..66e3983ff4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java @@ -415,7 +415,19 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public short readNextShort() throws IOException { - short s = readShort(currentIndex); + return readNextShort(converter); + } + + /** + * Reads the short at the current index and then increments the current + * index by SIZEOF_SHORT. + * + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @return the short at the current index + * @exception IOException if an I/O error occurs + */ + public short readNextShort(DataConverter dc) throws IOException { + short s = readShort(dc, currentIndex); currentIndex += SIZEOF_SHORT; return s; } @@ -427,7 +439,19 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public int readNextUnsignedShort() throws IOException { - return Short.toUnsignedInt(readNextShort()); + return Short.toUnsignedInt(readNextShort(converter)); + } + + /** + * Reads the unsigned short at the current index and then increments the current + * index by SIZEOF_SHORT. + * + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @return the unsigned short at the current index, as an int + * @exception IOException if an I/O error occurs + */ + public int readNextUnsignedShort(DataConverter dc) throws IOException { + return Short.toUnsignedInt(readNextShort(dc)); } /** @@ -437,7 +461,19 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public int readNextInt() throws IOException { - int i = readInt(currentIndex); + return readNextInt(converter); + } + + /** + * Reads the integer at the current index and then increments the current + * index by SIZEOF_INT. + * + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @return the integer at the current index + * @exception IOException if an I/O error occurs + */ + public int readNextInt(DataConverter dc) throws IOException { + int i = readInt(dc, currentIndex); currentIndex += SIZEOF_INT; return i; } @@ -449,7 +485,19 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public long readNextUnsignedInt() throws IOException { - return Integer.toUnsignedLong(readNextInt()); + return Integer.toUnsignedLong(readNextInt(converter)); + } + + /** + * Reads the unsigned integer at the current index and then increments the current + * index by SIZEOF_INT. + * + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @return the unsigned integer at the current index, as a long + * @exception IOException if an I/O error occurs + */ + public long readNextUnsignedInt(DataConverter dc) throws IOException { + return Integer.toUnsignedLong(readNextInt(dc)); } /** @@ -459,7 +507,19 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public long readNextLong() throws IOException { - long l = readLong(currentIndex); + return readNextLong(converter); + } + + /** + * Reads the long at the current index and then increments the current + * index by SIZEOF_LONG. + * + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @return the long at the current index + * @exception IOException if an I/O error occurs + */ + public long readNextLong(DataConverter dc) throws IOException { + long l = readLong(dc, currentIndex); currentIndex += SIZEOF_LONG; return l; } @@ -469,10 +529,22 @@ public class BinaryReader { * * @param len the number of bytes that the integer occupies, 1 to 8 * @return value of requested length, with sign bit extended, in a long - * @throws IOException + * @exception IOException if an I/O error occurs */ public long readNextValue(int len) throws IOException { - long result = readValue(currentIndex, len); + return readNextValue(converter, len); + } + + /** + * Returns the signed value of the integer (of the specified length) at the current index. + * + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @param len the number of bytes that the integer occupies, 1 to 8 + * @return value of requested length, with sign bit extended, in a long + * @exception IOException if an I/O error occurs + */ + public long readNextValue(DataConverter dc, int len) throws IOException { + long result = readValue(dc, currentIndex, len); currentIndex += len; return result; } @@ -482,10 +554,22 @@ public class BinaryReader { * * @param len the number of bytes that the integer occupies, 1 to 8 * @return unsigned value of requested length, in a long - * @throws IOException + * @exception IOException if an I/O error occurs */ public long readNextUnsignedValue(int len) throws IOException { - long result = readUnsignedValue(currentIndex, len); + return readNextUnsignedValue(converter, len); + } + + /** + * Returns the unsigned value of the integer (of the specified length) at the current index. + * + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @param len the number of bytes that the integer occupies, 1 to 8 + * @return unsigned value of requested length, in a long + * @exception IOException if an I/O error occurs + */ + public long readNextUnsignedValue(DataConverter dc, int len) throws IOException { + long result = readUnsignedValue(dc, currentIndex, len); currentIndex += len; return result; } @@ -684,7 +768,25 @@ public class BinaryReader { * @throws InvalidDataException if value can not be held in a java integer */ public int readNextUnsignedIntExact() throws IOException, InvalidDataException { - long i = readNextUnsignedInt(); + return readNextUnsignedIntExact(converter); + } + + /** + * Reads an unsigned int32 value, and returns it as a java int (instead of a java long). + *

+ * If the value is outside the range of 0..Integer.MAX_VALUE, an InvalidDataException is thrown. + *

+ * Useful for reading uint32 values that are going to be used in java to allocate arrays or + * other similar cases where the value must be a java integer. + * + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @return the uint32 value read from the stream, if it fits into the range [0..MAX_VALUE] + * of a java integer + * @throws IOException if there was an error reading + * @throws InvalidDataException if value can not be held in a java integer + */ + public int readNextUnsignedIntExact(DataConverter dc) throws IOException, InvalidDataException { + long i = readNextUnsignedInt(dc); ensureInt32u(i); return (int) i; } @@ -908,8 +1010,19 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public short readShort(long index) throws IOException { + return readShort(converter, index); + } + + /** + * Returns the signed SHORT at index. + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @param index the index where the SHORT begins + * @return the signed SHORT + * @exception IOException if an I/O error occurs + */ + public short readShort(DataConverter dc, long index) throws IOException { byte[] bytes = provider.readBytes(index, SIZEOF_SHORT); - return converter.getShort(bytes); + return dc.getShort(bytes); } /** @@ -919,7 +1032,18 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public int readUnsignedShort(long index) throws IOException { - return Short.toUnsignedInt(readShort(index)); + return Short.toUnsignedInt(readShort(converter, index)); + } + + /** + * Returns the unsigned SHORT at index. + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @param index the index where the SHORT begins + * @return the unsigned SHORT as an int + * @exception IOException if an I/O error occurs + */ + public int readUnsignedShort(DataConverter dc, long index) throws IOException { + return Short.toUnsignedInt(readShort(dc, index)); } /** @@ -929,8 +1053,19 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public int readInt(long index) throws IOException { + return readInt(converter, index); + } + + /** + * Returns the signed INTEGER at index. + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @param index the index where the INTEGER begins + * @return the signed INTEGER + * @exception IOException if an I/O error occurs + */ + public int readInt(DataConverter dc, long index) throws IOException { byte[] bytes = provider.readBytes(index, SIZEOF_INT); - return converter.getInt(bytes); + return dc.getInt(bytes); } /** @@ -940,7 +1075,18 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public long readUnsignedInt(long index) throws IOException { - return Integer.toUnsignedLong(readInt(index)); + return Integer.toUnsignedLong(readInt(converter, index)); + } + + /** + * Returns the unsigned INTEGER at index. + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @param index the index where the INTEGER begins + * @return the unsigned INTEGER as a long + * @exception IOException if an I/O error occurs + */ + public long readUnsignedInt(DataConverter dc, long index) throws IOException { + return Integer.toUnsignedLong(readInt(dc, index)); } /** @@ -950,8 +1096,19 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public long readLong(long index) throws IOException { + return readLong(converter, index); + } + + /** + * Returns the signed LONG at index. + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @param index the index where the LONG begins + * @return the LONG + * @exception IOException if an I/O error occurs + */ + public long readLong(DataConverter dc, long index) throws IOException { byte[] bytes = provider.readBytes(index, SIZEOF_LONG); - return converter.getLong(bytes); + return dc.getLong(bytes); } /** @@ -960,11 +1117,24 @@ public class BinaryReader { * @param index where the value begins * @param len the number of bytes that the integer occupies, 1 to 8 * @return value of requested length, with sign bit extended, in a long - * @throws IOException + * @exception IOException if an I/O error occurs */ public long readValue(long index, int len) throws IOException { + return readValue(converter, index, len); + } + + /** + * Returns the signed value of the integer (of the specified length) at the specified offset. + * + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @param index where the value begins + * @param len the number of bytes that the integer occupies, 1 to 8 + * @return value of requested length, with sign bit extended, in a long + * @exception IOException if an I/O error occurs + */ + public long readValue(DataConverter dc, long index, int len) throws IOException { byte[] bytes = provider.readBytes(index, len); - return converter.getSignedValue(bytes, len); + return dc.getSignedValue(bytes, len); } /** @@ -973,11 +1143,24 @@ public class BinaryReader { * @param index where the value begins * @param len the number of bytes that the integer occupies, 1 to 8 * @return unsigned value of requested length, in a long - * @throws IOException + * @exception IOException if an I/O error occurs */ public long readUnsignedValue(long index, int len) throws IOException { + return readUnsignedValue(converter, index, len); + } + + /** + * Returns the unsigned value of the integer (of the specified length) at the specified offset. + * + * @param dc {@link BigEndianDataConverter BE} or {@link LittleEndianDataConverter LE} + * @param index where the value begins + * @param len the number of bytes that the integer occupies, 1 to 8 + * @return unsigned value of requested length, in a long + * @exception IOException if an I/O error occurs + */ + public long readUnsignedValue(DataConverter dc, long index, int len) throws IOException { byte[] bytes = provider.readBytes(index, len); - return converter.getValue(bytes, len); + return dc.getValue(bytes, len); // NOTE: getValue() is unsigned so this is all good } /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFProgram.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFProgram.java index 4f7abc7b84..4d7ef32aea 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFProgram.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFProgram.java @@ -1201,25 +1201,32 @@ public class DWARFProgram implements Closeable { private DWARFLocationList readLocationList(DWARFNumericAttribute loclistAttr, DWARFCompilationUnit cu) throws IOException { - switch (loclistAttr.getAttributeForm()) { - case DW_FORM_sec_offset: - int dwarfVer = cu.getDWARFVersion(); - if (dwarfVer < 5) { - debugLocation.setPointerIndex(loclistAttr.getUnsignedValue()); - return DWARFLocationList.readV4(debugLocation, cu); - } - else if (dwarfVer == 5) { - debugLocLists.setPointerIndex(loclistAttr.getUnsignedValue()); + try { + switch (loclistAttr.getAttributeForm()) { + case DW_FORM_sec_offset: + int dwarfVer = cu.getDWARFVersion(); + if (dwarfVer < 5) { + debugLocation.setPointerIndex(loclistAttr.getUnsignedValue()); + return DWARFLocationList.readV4(debugLocation, cu); + } + else if (dwarfVer == 5) { + debugLocLists.setPointerIndex(loclistAttr.getUnsignedValue()); + return DWARFLocationList.readV5(debugLocLists, cu); + } + break; + case DW_FORM_loclistx: + int index = loclistAttr.getUnsignedIntExact(); + long locOffset = locationListTable.getOffset(index, cu); + debugLocLists.setPointerIndex(locOffset); return DWARFLocationList.readV5(debugLocLists, cu); - } - break; - case DW_FORM_loclistx: - int index = loclistAttr.getUnsignedIntExact(); - long locOffset = locationListTable.getOffset(index, cu); - debugLocLists.setPointerIndex(locOffset); - return DWARFLocationList.readV5(debugLocLists, cu); - default: - break; // fallthru to throw + default: + break; // fallthru to throw + } + } + catch (IOException | IllegalArgumentException e) { + throw new IOException( + "Failed to read location list specified by %s".formatted(loclistAttr.toString()), + e); } throw new IOException( "Unsupported loclist form %s".formatted(loclistAttr.getAttributeForm())); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRange.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRange.java index d9a7754f2f..d7ab365e02 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRange.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRange.java @@ -43,7 +43,7 @@ public class DWARFRange implements Comparable { public DWARFRange(long start, long end) { if (Long.compareUnsigned(end, start) < 0) { throw new IllegalArgumentException( - "Range max (%d) cannot be less than min (%d).".formatted(end, start)); + "Range max (%x) cannot be less than min (%x).".formatted(end, start)); } this.start = start; this.end = end; 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 ad978d49e0..f347e77d6a 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 @@ -189,7 +189,7 @@ public class GoBuildInfo implements ElfInfoItem { return version; } - public GoVer getVerEnum() { + public GoVer getGoVer() { return GoVer.parse(version); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoRegisterInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoRegisterInfo.java index 631fc1471c..19d496746c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoRegisterInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoRegisterInfo.java @@ -15,11 +15,17 @@ */ package ghidra.app.util.bin.format.golang; +import java.util.ArrayList; import java.util.List; +import ghidra.app.util.bin.format.dwarf.DWARFUtil; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; import ghidra.program.model.lang.Register; +import ghidra.program.model.listing.*; +import ghidra.program.model.pcode.Varnode; +import ghidra.program.model.symbol.SourceType; +import ghidra.util.exception.InvalidInputException; /** * Immutable information about registers, alignment sizes, etc needed to allocate storage @@ -28,6 +34,8 @@ import ghidra.program.model.lang.Register; */ public class GoRegisterInfo { + public enum RegType { INT, FLOAT } + private final List intRegisters; private final List floatRegisters; private final int stackInitialOffset; @@ -36,9 +44,14 @@ public class GoRegisterInfo { private final Register zeroRegister; // always contains a zero value private final boolean zeroRegisterIsBuiltin; // zero register is provided by cpu, or is manually set + private final Register duffzeroDestParam; + private final Register duffzeroZeroParam; // if duffzero has 2nd param + private final RegType duffzeroZeroParamType; + GoRegisterInfo(List intRegisters, List floatRegisters, int stackInitialOffset, int maxAlign, Register currentGoroutineRegister, - Register zeroRegister, boolean zeroRegisterIsBuiltin) { + Register zeroRegister, boolean zeroRegisterIsBuiltin, Register duffzeroDestParam, + Register duffzeroZeroParam, RegType duffzeroZeroParamType) { this.intRegisters = intRegisters; this.floatRegisters = floatRegisters; this.stackInitialOffset = stackInitialOffset; @@ -46,6 +59,10 @@ public class GoRegisterInfo { this.currentGoroutineRegister = currentGoroutineRegister; this.zeroRegister = zeroRegister; this.zeroRegisterIsBuiltin = zeroRegisterIsBuiltin; + + this.duffzeroDestParam = duffzeroDestParam; + this.duffzeroZeroParam = duffzeroZeroParam; + this.duffzeroZeroParamType = duffzeroZeroParamType; } public int getIntRegisterSize() { @@ -80,6 +97,45 @@ public class GoRegisterInfo { return stackInitialOffset; } + public List getDuffzeroParams(Program program) { + if (duffzeroDestParam == null) { + return List.of(); + } + try { + ProgramBasedDataTypeManager dtm = program.getDataTypeManager(); + DataType voidPtr = dtm.getPointer(VoidDataType.dataType); + + List params = new ArrayList<>(); + + params.add(new ParameterImpl("dest", Parameter.UNASSIGNED_ORDINAL, voidPtr, + getStorageForReg(program, duffzeroDestParam, voidPtr.getLength()), true, program, + SourceType.ANALYSIS)); + if (duffzeroZeroParam != null && duffzeroZeroParamType != null) { + int regSize = duffzeroZeroParam.getMinimumByteSize(); + DataType dt = switch (duffzeroZeroParamType) { + case FLOAT -> AbstractFloatDataType.getFloatDataType(regSize, dtm); + case INT -> AbstractIntegerDataType.getUnsignedDataType(regSize, dtm); + }; + params.add(new ParameterImpl("zeroValue", Parameter.UNASSIGNED_ORDINAL, dt, + getStorageForReg(program, duffzeroZeroParam, regSize), true, program, + SourceType.ANALYSIS)); + } + + return params; + } + catch (InvalidInputException e) { + return List.of(); + } + + } + + private VariableStorage getStorageForReg(Program program, Register reg, int len) + throws InvalidInputException { + return new VariableStorage(program, + DWARFUtil.convertRegisterListToVarnodeStorage(List.of(reg), len) + .toArray(Varnode[]::new)); + } + public int getAlignmentForType(DataType dt) { while (dt instanceof TypeDef || dt instanceof Array) { if (dt instanceof TypeDef td) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoRegisterInfoManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoRegisterInfoManager.java index c01d261806..cbe16358c6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoRegisterInfoManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoRegisterInfoManager.java @@ -24,6 +24,7 @@ import org.jdom.input.SAXBuilder; import generic.jar.ResourceFile; import ghidra.app.util.bin.format.dwarf.DWARFUtil; +import ghidra.app.util.bin.format.golang.GoRegisterInfo.RegType; import ghidra.program.model.lang.*; import ghidra.util.Msg; import ghidra.util.xml.XmlUtilities; @@ -32,12 +33,13 @@ import ghidra.util.xml.XmlUtilities; * XML config file format: *

  * 	<golang>
- * 		<register_info versions="V1_17,V1_18">
+ * 		<register_info versions="V1_17,V1_18,1.20,1.21"> // or "all"
  * 			<int_registers list="RAX,RBX,RCX,RDI,RSI,R8,R9,R10,R11"/>
  * 			<float_registers list="XMM0,XMM1,XMM2,XMM3,XMM4,XMM5,XMM6,XMM7,XMM8,XMM9,XMM10,XMM11,XMM12,XMM13,XMM14"/>
  * 			<stack initialoffset="8" maxalign="8"/>
  * 			<current_goroutine register="R14"/>
  * 			<zero_register register="XMM15" builtin="true|false"/>
+ * 			<duffzero dest="RDI" zero_arg="XMM0" zero_type="float|int"/>
  * 		</register_info>
  * 		<register_info versions="V1_2">
  * 			...
@@ -65,23 +67,37 @@ public class GoRegisterInfoManager {
 	 * returned that forces all parameters to be stack allocated.
 	 * 
 	 * @param lang {@link Language}
-	 * @param goVersion {@link GoVer} enum
+	 * @param goVer {@link GoVer}
 	 * @return {@link GoRegisterInfo}, never null
 	 */
-	public synchronized GoRegisterInfo getRegisterInfoForLang(Language lang, GoVer goVersion) {
+	public synchronized GoRegisterInfo getRegisterInfoForLang(Language lang, GoVer goVer) {
 		Map perVersionRegInfos =
 			cache.computeIfAbsent(lang.getLanguageID(), (key) -> loadRegisterInfo(lang));
-		GoRegisterInfo registerInfo = perVersionRegInfos.get(goVersion);
+
+		GoRegisterInfo registerInfo = getMatchingRegisterInfo(perVersionRegInfos, goVer);
 		if (registerInfo == null) {
 			registerInfo = getDefault(lang);
-			perVersionRegInfos.put(goVersion, registerInfo);
+			perVersionRegInfos.put(goVer, registerInfo);
 			int goSize = lang.getInstructionAlignment();
-			Msg.warn(this, "Missing Golang register info for: " + lang.getLanguageID() +
-				", defaulting to abi0, size=" + goSize);
+			Msg.warn(this, "Missing Golang register info for: %s, defaulting to abi0, size=%d"
+					.formatted(lang.getLanguageID(), goSize));
 		}
 		return registerInfo;
 	}
 
+	private GoRegisterInfo getMatchingRegisterInfo(Map mappedRegInfo, GoVer goVer) {
+		GoRegisterInfo result = mappedRegInfo.get(goVer);
+		if ( result == null ) {
+			result = mappedRegInfo.entrySet()
+					.stream()
+					.filter(e -> e.getKey().isWildcard())
+					.map(Map.Entry::getValue)
+					.findFirst()
+					.orElse(null);
+		}
+		return result;
+	}
+
 	private Map loadRegisterInfo(Language lang) {
 		try {
 			ResourceFile f = DWARFUtil.getLanguageExternalFile(lang, REGISTER_INFO_EXTERNAL_NAME);
@@ -136,8 +152,9 @@ public class GoRegisterInfoManager {
 		Element stackElem = regInfoElem.getChild("stack");
 		Element goRoutineElem = regInfoElem.getChild("current_goroutine");
 		Element zeroRegElem = regInfoElem.getChild("zero_register");
+		Element duffZeroElem = regInfoElem.getChild("duffzero");
 		if (intRegsElem == null || floatRegsElem == null || stackElem == null ||
-			goRoutineElem == null || zeroRegElem == null) {
+			goRoutineElem == null || zeroRegElem == null || duffZeroElem == null) {
 			throw new IOException("Bad format");
 		}
 
@@ -154,10 +171,14 @@ public class GoRegisterInfoManager {
 		Register zeroReg = parseRegStr(zeroRegElem.getAttributeValue("register"), lang);
 		boolean zeroRegIsBuiltin =
 			XmlUtilities.parseOptionalBooleanAttr(zeroRegElem, "builtin", false);
+		
+		Register duffzeroDest = parseRegStr(duffZeroElem.getAttributeValue("dest"), lang);
+		Register duffzeroZero = parseRegStr(duffZeroElem.getAttributeValue("zero_arg"), lang);
+		RegType duffzeroZeroType = parseRegTypeStr(duffZeroElem.getAttributeValue("zero_type"));
 
-		GoRegisterInfo registerInfo =
-			new GoRegisterInfo(intRegs, floatRegs, stackInitialOffset, maxAlign,
-				currentGoRoutineReg, zeroReg, zeroRegIsBuiltin);
+		GoRegisterInfo registerInfo = new GoRegisterInfo(intRegs, floatRegs, stackInitialOffset,
+			maxAlign, currentGoRoutineReg, zeroReg, zeroRegIsBuiltin, duffzeroDest, duffzeroZero,
+			duffzeroZeroType);
 		Map result = new HashMap<>();
 		for (GoVer goVer : validGoVersions) {
 			result.put(goVer, registerInfo);
@@ -167,7 +188,8 @@ public class GoRegisterInfoManager {
 
 	private GoRegisterInfo getDefault(Language lang) {
 		int goSize = lang.getInstructionAlignment();
-		return new GoRegisterInfo(List.of(), List.of(), goSize, goSize, null, null, false);
+		return new GoRegisterInfo(List.of(), List.of(), goSize, goSize, null, null, false, null,
+			null, RegType.INT);
 	}
 
 	private List parseRegListStr(String s, Language lang) throws IOException {
@@ -196,26 +218,33 @@ public class GoRegisterInfoManager {
 		return register;
 	}
 
+	private RegType parseRegTypeStr(String s) {
+		return switch (Objects.requireNonNullElse(s, "int").toLowerCase()) {
+			default -> RegType.INT;
+			case "float" -> RegType.FLOAT;
+		};
+
+	}
+
 	private Set parseValidGoVersionsStr(String s) throws IOException {
 		if (s.trim().equalsIgnoreCase("all")) {
-			EnumSet allVers = EnumSet.allOf(GoVer.class);
-			allVers.remove(GoVer.UNKNOWN);
-			return allVers;
+			return Set.of(GoVer.ANY);
 		}
 
-		EnumSet result = EnumSet.noneOf(GoVer.class);
+		Set result = new HashSet<>();
 		for (String verStr : s.split(",")) {
 			verStr = verStr.trim();
 			if (verStr.isEmpty()) {
 				continue;
 			}
-			try {
-				GoVer ver = GoVer.valueOf(verStr);
-				result.add(ver);
+			if (verStr.startsWith("V")) {
+				verStr = verStr.substring(1).replace('_', '.'); // convert "V1_1" -> "1.1"
 			}
-			catch (IllegalArgumentException e) {
+			GoVer ver = GoVer.parse(verStr);
+			if (ver.isInvalid()) {
 				throw new IOException("Unknown go version: " + verStr);
 			}
+			result.add(ver);
 		}
 		return result;
 	}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoVer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoVer.java
index b9f6bca38f..14e208e97c 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoVer.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoVer.java
@@ -15,31 +15,40 @@
  */
 package ghidra.app.util.bin.format.golang;
 
+import java.util.Objects;
+
 import ghidra.framework.options.Options;
 import ghidra.program.model.listing.Program;
 
 /**
  * Golang version numbers
  */
-public enum GoVer {
-	UNKNOWN(0, 0),
-	V1_2(1, 2),
-	V1_16(1, 16),
-	V1_17(1, 17),
-	V1_18(1, 18),
-	V1_19(1, 19),
-	V1_20(1, 20),
-	V1_21(1, 21),
-	V1_22(1, 22);
+public class GoVer implements Comparable {
+	public static final GoVer INVALID = new GoVer(0, 0);
+	public static final GoVer ANY = new GoVer(-1, -1);
+
+	// a couple of well-known versions that are re-used in a few places
+	public static final GoVer V1_2 = new GoVer(1, 2);
+	public static final GoVer V1_16 = new GoVer(1, 16);
+	public static final GoVer V1_17 = new GoVer(1, 17);
+	public static final GoVer V1_18 = new GoVer(1, 18);
 
 	private final int major;
 	private final int minor;
 
-	GoVer(int major, int minor) {
+	public GoVer(int major, int minor) {
 		this.major = major;
 		this.minor = minor;
 	}
 
+	public boolean isInvalid() {
+		return major == 0 && minor == 0;
+	}
+
+	public boolean isWildcard() {
+		return major == -1 && minor == -1;
+	}
+
 	/**
 	 * Major value
 	 * 
@@ -58,6 +67,15 @@ public enum GoVer {
 		return minor;
 	}
 
+	@Override
+	public int compareTo(GoVer o) {
+		int result = Integer.compare(major, o.major);
+		if (result == 0) {
+			result = Integer.compare(minor, o.minor);
+		}
+		return result;
+	}
+
 	/**
 	 * Compares this version to the specified other version and returns true if this version
 	 * is greater than or equal to the other version.
@@ -66,45 +84,75 @@ public enum GoVer {
 	 * @return true if this version is gte other version
 	 */
 	public boolean isAtLeast(GoVer otherVersion) {
-		return this.ordinal() >= otherVersion.ordinal();
+		return compareTo(otherVersion) >= 0;
 	}
 
 	/**
-	 * Parses a version string ("1.2") and returns the matching GoVer enum instance, or
-	 * UNKNOWN if no matching version or bad data.
+	 * Returns true if this version is between the specified min and max versions (inclusive).
+	 * 
+	 * @param min minimum version to allow (inclusive)
+	 * @param max maximum version to allow (inclusive)
+	 * @return boolean true if this version is between the specified min and max versions
+	 */
+	public boolean inRange(GoVer min, GoVer max) {
+		return min.compareTo(this) <= 0 && this.compareTo(max) <= 0;
+	}
+
+	/**
+	 * Parses a version string ("1.2") and returns a GoVer instance, or
+	 * INVALID if no matching version or bad data.
 	 *  
 	 * @param s string to parse
-	 * @return GoVer enum instance, or UNKNOWN
+	 * @return GoVer instance, or INVALID
 	 */
 	public static GoVer parse(String s) {
-		String[] parts = s.split("\\.");
+		String[] parts = Objects.requireNonNullElse(s, "").split("\\.");
 		if (parts.length < 2) {
-			return UNKNOWN;
+			return INVALID;
 		}
 		try {
 			int major = Integer.parseInt(parts[0]);
 			int minor = Integer.parseInt(parts[1]);
-			for (GoVer ver : values()) {
-				if (ver.major == major && ver.minor == minor) {
-					return ver;
-				}
-			}
+			//don't care about patch level right now
+			return new GoVer(major, minor);
 		}
 		catch (NumberFormatException e) {
 			// fall thru, return unknown
 		}
-		return UNKNOWN;
+		return INVALID;
 	}
 
 	public static final String GOLANG_VERSION_PROPERTY_NAME = "Golang go version";
 	public static GoVer fromProgramProperties(Program program) {
 		Options props = program.getOptions(Program.PROGRAM_INFO);
 		String verStr = props.getString(GOLANG_VERSION_PROPERTY_NAME, null);
-		return verStr != null ? parse(verStr) : UNKNOWN;
+		return parse(verStr);
 	}
 
 	public static void setProgramPropertiesWithOriginalVersionString(Options props, String s) {
 		props.setString(GOLANG_VERSION_PROPERTY_NAME, s);
 	}
 
+	@Override
+	public int hashCode() {
+		return Objects.hash(major, minor);
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj) {
+			return true;
+		}
+		if (!(obj instanceof GoVer)) {
+			return false;
+		}
+		GoVer other = (GoVer) obj;
+		return major == other.major && minor == other.minor;
+	}
+
+	@Override
+	public String toString() {
+		return "%d.%d".formatted(major, minor);
+	}
+
 }
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoFuncData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoFuncData.java
index 0733f9c580..7ba8f8d25c 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoFuncData.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoFuncData.java
@@ -22,7 +22,8 @@ import ghidra.app.util.bin.BinaryReader;
 import ghidra.app.util.bin.format.golang.rtti.types.GoMethod.GoMethodInfo;
 import ghidra.app.util.bin.format.golang.structmapping.*;
 import ghidra.program.model.address.*;
-import ghidra.program.model.data.*;
+import ghidra.program.model.data.ArrayDataType;
+import ghidra.program.model.data.DataType;
 import ghidra.program.model.listing.Function;
 import ghidra.util.NumericUtilities;
 import ghidra.util.exception.CancelledException;
@@ -39,19 +40,19 @@ public class GoFuncData implements StructureMarkup {
 	@ContextField
 	private StructureContext context;
 
-	@FieldMapping(optional = true, fieldName = { "entryoff", "entryOff" })
+	@FieldMapping(presentWhen = "1.18+", fieldName = { "entryoff", "entryOff" })
 	@EOLComment("getDescription")
 	@MarkupReference("getFuncAddress")
-	private long entryoff;	// valid in >=1.18, relative offset of function
+	private long entryoff;	// relative offset of function
 
-	@FieldMapping(optional = true)
+	@FieldMapping(presentWhen = "-1.17")
 	@EOLComment("getDescription")
 	@MarkupReference("getFuncAddress")
-	private long entry;	// valid in <=1.17, location of function
+	private long entry;	// absolute location of function
 
 	@FieldMapping(fieldName = { "nameoff", "nameOff" })
 	@MarkupReference("getNameAddress")
-	private long nameoff;
+	private long nameoff;	// uint32
 
 	//private long args; // size of arguments
 
@@ -64,14 +65,14 @@ public class GoFuncData implements StructureMarkup {
 	@FieldMapping
 	private int npcdata; // number of elements in varlen pcdata array
 
-	@FieldMapping
-	private long cuOffset;
+	@FieldMapping(presentWhen = "1.16+")
+	private long cuOffset = -1;
 
 	@FieldMapping
 	@EOLComment("getFuncIDEnum")
 	private byte funcID;	// see GoFuncID enum
 
-	@FieldMapping
+	@FieldMapping(presentWhen = "1.17+")
 	@EOLComment("flags")
 	private byte flag;	// runtime.funcFlag, see GoFuncFlag enum
 
@@ -130,13 +131,14 @@ public class GoFuncData implements StructureMarkup {
 		// using the max pc value
 		try {
 			long max = new GoPcValueEvaluator(this, pcfile).getMaxPC() - 1;
-			return max > entry
-					? new AddressRangeImpl(funcAddress, funcAddress.getNewAddress(max))
-					: null;
+			if (max > entry) {
+				return new AddressRangeImpl(funcAddress, funcAddress.getNewAddress(max));
+			}
 		}
 		catch (IOException e) {
-			return new AddressRangeImpl(getFuncAddress(), getFuncAddress());
+			// fall thru, return 1-byte range
 		}
+		return new AddressRangeImpl(funcAddress, funcAddress);
 	}
 
 	/**
@@ -228,7 +230,7 @@ public class GoFuncData implements StructureMarkup {
 	 * 
 	 * Return value information is unknown and always represented as an "undefined" data type.
 	 * 
-	 * @return pseduo-function signature string, such as "undefined foo( 8, 8 )" which would
+	 * @return pseudo-function signature string, such as "undefined foo( 8, 8 )" which would
 	 * indicate the function had 2 8-byte arguments 
 	 * @throws IOException if error reading lookup data
 	 */
@@ -237,18 +239,6 @@ public class GoFuncData implements StructureMarkup {
 		return sig.toString();
 	}
 
-	/**
-	 * Attempts to return a {@link FunctionDefinition} for this function, based on this
-	 * function's inclusion in a golang interface as a method.
-	 * 
-	 * @return {@link FunctionDefinition}
-	 * @throws IOException if error
-	 */
-	public FunctionDefinition findMethodSignature() throws IOException {
-		MethodInfo methodInfo = findMethodInfo();
-		return methodInfo != null ? methodInfo.getSignature() : null;
-	}
-
 	/**
 	 * Attempts to return a {@link GoMethodInfo} for this function, based on this
 	 * function's inclusion in a golang interface as a method.
@@ -275,9 +265,14 @@ public class GoFuncData implements StructureMarkup {
 	 */
 	public Address getNameAddress() {
 		GoModuledata moduledata = getModuledata();
-		return moduledata != null
-				? moduledata.getFuncnametab().getArrayAddress().add(nameoff)
-				: null;
+		if (moduledata != null) {
+			GoSlice slice = moduledata.getFuncnametab();
+			if (slice == null) {
+				slice = moduledata.getPclntable();
+			}
+			return slice.getArrayAddress().add(nameoff);
+		}
+		return null;
 	}
 
 	/**
@@ -287,15 +282,17 @@ public class GoFuncData implements StructureMarkup {
 	 */
 	public String getName() {
 		GoModuledata moduledata = getModuledata();
-		try {
-			if (moduledata != null) {
-				return programContext
-						.getReader(moduledata.getFuncnametab().getArrayOffset() + nameoff)
-						.readNextUtf8String();
+		if (moduledata != null) {
+			try {
+				GoSlice slice = moduledata.getFuncnametab();
+				if (slice == null) {
+					slice = moduledata.getPclntable();
+				}
+				return slice.getElementReader(1, (int) nameoff).readNextUtf8String();
+			}
+			catch (IOException e) {
+				// fall thru
 			}
-		}
-		catch (IOException e) {
-			// fall thru
 		}
 		return "unknown_func_%x_%s".formatted(context.getStructureStart(),
 			funcAddress != null ? funcAddress : "missing_addr");
@@ -369,11 +366,25 @@ public class GoFuncData implements StructureMarkup {
 		int fileno = new GoPcValueEvaluator(this, pcfile).eval(entry);
 		int lineNum = new GoPcValueEvaluator(this, pcln).eval(entry);
 
-		long fileoff = fileno >= 0
-				? moduledata.getCutab()
-						.readUIntElement(4 /*sizeof(uint32)*/, (int) cuOffset + fileno)
-				: -1;
-		String fileName = fileoff != -1 ? moduledata.getFilename(fileoff) : null;
+		if (fileno < 0) {
+			return null;
+		}
+
+		long fileoff;
+		GoSlice cutab = moduledata.getCutab();
+		GoSlice filetab = moduledata.getFiletab();
+		GoSlice nameSlice;
+		if (cutab == null) { // when <= 1.15
+			fileoff = filetab.readUIntElement(4 /*sizeof(uint32*/, fileno);
+			nameSlice = moduledata.getPclntable();
+		}
+		else { // when >= 1.16
+			fileoff = cutab.readUIntElement(4 /*sizeof(uint32)*/, (int) cuOffset + fileno);
+			nameSlice = filetab;
+		}
+		String fileName = fileoff >= 0 // -1 == no value 
+				? nameSlice.getElementReader(1, (int) fileoff).readNextUtf8String()
+				: null;
 		return fileName != null ? new GoSourceFileInfo(fileName, lineNum) : null;
 	}
 
@@ -579,25 +590,3 @@ public class GoFuncData implements StructureMarkup {
 	}
 
 }
-/*
-struct runtime._func  
-Length: 40  Alignment: 4
-{ 
-  uint32                    entryoff         
-  int32                      nameoff         
-  int32                      args              
-  uint32                    deferreturn   
-  uint32                    pcsp              
-  uint32                    pcfile             
-  uint32                    pcln               
-  uint32                    npcdata        
-  uint32                    cuOffset        
-  runtime.funcID      funcID            
-  runtime.funcFlag  flag                
-  uint8[1]                _                    
-  uint8                      nfuncdata     
-} pack()
-
-int32[] pcdata
-int32[] funcdata
-*/
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoFunctabEntry.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoFunctabEntry.java
index 7c7d5933bb..b85cdc3865 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoFunctabEntry.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoFunctabEntry.java
@@ -32,13 +32,13 @@ public class GoFunctabEntry {
 	@ContextField
 	private StructureContext context;
 
-	@FieldMapping(optional = true)
+	@FieldMapping(presentWhen = "1.18+")
 	@MarkupReference("getFuncAddress")
-	private long entryoff;	// valid in >=1.18, relative offset of function
+	private long entryoff;	// relative offset of function
 
-	@FieldMapping(optional = true)
+	@FieldMapping(presentWhen = "-1.17")
 	@MarkupReference("getFuncAddress")
-	private long entry;	// valid in <=1.17, location of function
+	private long entry;	// absolute location of function
 
 	@FieldMapping
 	@MarkupReference("getFuncData")
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 b307b04965..bbbc90a74f 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
@@ -44,9 +44,27 @@ public class GoModuledata implements StructureMarkup {
 	@ContextField
 	private StructureContext structureContext;
 
-	@FieldMapping
+	@FieldMapping(presentWhen = "1.16+")
 	@MarkupReference
-	private long pcHeader;	// pointer to the GoPcHeader instance, useful for bootstrapping
+	private long pcHeader;	// pointer to the GoPcHeader instance, useful for bootstrapping.  when ver >= 1.16, this is first field
+
+	@FieldMapping(presentWhen = "1.16+")
+	private GoSlice funcnametab;	// []uint8 blob of null term strings
+
+	@FieldMapping(presentWhen = "1.16+")
+	private GoSlice cutab;	// []uint32
+
+	@FieldMapping
+	private GoSlice filetab; // []uint32 when ver <=1.15, []uint8 blob of null term strings when ver >= 1.16
+
+	@FieldMapping(presentWhen = "1.16+")
+	private GoSlice pctab;	// []uint8
+
+	@FieldMapping
+	private GoSlice pclntable;	// []uint8, shares footprint with ftab.  when ver <= 1.15, this is first field and happens to have a GoPcHeader
+
+	@FieldMapping
+	private GoSlice ftab;	// []runtime.functab, shares footprint with pclntable
 
 	@FieldMapping
 	private long data;
@@ -82,24 +100,6 @@ public class GoModuledata implements StructureMarkup {
 	@FieldMapping(fieldName = "typelinks")
 	private GoSlice typeLinks;
 
-	@FieldMapping
-	private GoSlice funcnametab;	// []uint8 blob of null term strings
-
-	@FieldMapping
-	private GoSlice cutab;	// []uint32
-
-	@FieldMapping
-	private GoSlice filetab; // []uint8 blob of null term strings
-
-	@FieldMapping
-	private GoSlice pctab;	// []uint8
-
-	@FieldMapping
-	private GoSlice pclntable;	// []uint8, shares footprint with ftab
-
-	@FieldMapping
-	private GoSlice ftab;	// []runtime.functab, shares footprint with pclntable
-
 	@FieldMapping
 	private GoSlice itablinks; // []*runtime.itab (array of pointers to runtime.tab)
 
@@ -114,17 +114,25 @@ public class GoModuledata implements StructureMarkup {
 	 * Compares the data in this structure to fields in a GoPcHeader and returns true if they
 	 * match.
 	 * 
-	 * @param pclntab GoPcHeader instance
+	 * @param otherPcHeader GoPcHeader instance
 	 * @return boolean true if match, false if no match
 	 */
-	public boolean matchesPclntab(GoPcHeader pclntab) {
-		return (!pclntab.hasTextStart() || pclntab.getTextStart().equals(getText())) &&
-			pclntab.getFuncnameAddress().equals(funcnametab.getArrayAddress());
+	public boolean matchesPcHeader(GoPcHeader otherPcHeader) {
+		return (!otherPcHeader.hasTextStart() || otherPcHeader.getTextStart().equals(getText())) &&
+			otherPcHeader.getFuncnameAddress().equals(funcnametab.getArrayAddress());
 	}
 
 	@Markup
 	public GoPcHeader getPcHeader() throws IOException {
-		return programContext.readStructure(GoPcHeader.class, pcHeader);
+		return pcHeader != 0 // when ver >= 1.16 
+				? programContext.readStructure(GoPcHeader.class, pcHeader)
+				: programContext.readStructure(GoPcHeader.class, pclntable.getArrayAddress());
+	}
+
+	public Address getPcHeaderAddress() {
+		return pcHeader != 0
+				? programContext.getDataAddress(pcHeader)
+				: pclntable.getArrayAddress();
 	}
 
 	/**
@@ -236,8 +244,8 @@ public class GoModuledata implements StructureMarkup {
 		}
 
 		// all these static slices should be allocated with len == cap.  If not true, fail.
-		if (!typeLinks.isFull() || !filetab.isFull() || !pctab.isFull() || !pclntable.isFull() ||
-			!ftab.isFull()) {
+		if (!typeLinks.isFull() || !filetab.isFull() || (pctab != null && !pctab.isFull()) ||
+			!pclntable.isFull() || !ftab.isFull()) {
 			return false;
 		}
 
@@ -287,15 +295,8 @@ public class GoModuledata implements StructureMarkup {
 		return filetab;
 	}
 
-	/**
-	 * Returns the filename at the specified offset.
-	 * 
-	 * @param fileoff offset in the filetab of the filename
-	 * @return filename
-	 * @throws IOException if error reading
-	 */
-	public String getFilename(long fileoff) throws IOException {
-		return programContext.getReader(filetab.getElementOffset(1, fileoff)).readNextUtf8String();
+	public GoSlice getPclntable() {
+		return pclntable;
 	}
 
 	/**
@@ -307,6 +308,10 @@ public class GoModuledata implements StructureMarkup {
 		return pctab;
 	}
 
+	public GoSlice getPcValueTable() {
+		return pctab != null ? pctab : pclntable;
+	}
+
 	/**
 	 * Returns a reference to the controlling {@link GoRttiMapper go binary} context.
 	 * 
@@ -329,7 +334,9 @@ public class GoModuledata implements StructureMarkup {
 
 		itablinks.markupArray("moduledata.itablinks", null, GoItab.class, true, session);
 
-		markupStringTable(funcnametab.getArrayAddress(), funcnametab.getLen(), session);
+		if (funcnametab != null) {
+			markupStringTable(funcnametab.getArrayAddress(), funcnametab.getLen(), session);
+		}
 		markupStringTable(filetab.getArrayAddress(), filetab.getLen(), session);
 
 		GoSlice subSlice = getFunctabEntriesSlice();
@@ -441,15 +448,15 @@ public class GoModuledata implements StructureMarkup {
 	 * Searches memory for a likely GoModuledata structure.
 	 * 
 	 * @param context already initialized {@link GoRttiMapper}
-	 * @param pclntabAddress address of an already found {@link GoPcHeader}
-	 * @param pclntab the {@link GoPcHeader}
+	 * @param pcHeaderAddress address of an already found {@link GoPcHeader}
+	 * @param pcHeader the {@link GoPcHeader}
 	 * @param range memory range to search.  Will be different for different types of binaries
 	 * @param monitor {@link TaskMonitor} 
 	 * @return new GoModuledata instance, or null if not found
 	 * @throws IOException if error reading found structure
 	 */
 	/* package */ static GoModuledata findFirstModule(GoRttiMapper context,
-			Address pclntabAddress, GoPcHeader pclntab, AddressRange range, TaskMonitor monitor)
+			Address pcHeaderAddress, GoPcHeader pcHeader, AddressRange range, TaskMonitor monitor)
 			throws IOException {
 		if (range == null) {
 			return null;
@@ -462,7 +469,7 @@ public class GoModuledata implements StructureMarkup {
 		// field of the GoModuledata structure.
 		int ptrSize = context.getPtrSize();
 		byte[] searchBytes = new byte[ptrSize];
-		context.getDataConverter().putValue(pclntabAddress.getOffset(), ptrSize, searchBytes, 0);
+		context.getDataConverter().putValue(pcHeaderAddress.getOffset(), ptrSize, searchBytes, 0);
 		Address moduleAddr = memory.findBytes(range.getMinAddress(), range.getMaxAddress(),
 			searchBytes, null, true, monitor);
 		if (moduleAddr == null) {
@@ -473,50 +480,6 @@ public class GoModuledata implements StructureMarkup {
 
 		// Verify that we read a good GoModuledata struct by comparing some of its values to
 		// the pclntab structure.
-		return moduleData.matchesPclntab(pclntab) ? moduleData : null;
+		return moduleData.matchesPcHeader(pcHeader) ? moduleData : null;
 	}
 }
-
-/*
-struct runtime.moduledata Length:276 Alignment:4{
-  runtime.pcHeader*pcHeader
-  []uint8                                                  funcnametab      
-  []uint32                                                cutab                 
-  []uint8                                                  filetab                 
-  []uint8                                                  pctab                 
-  []uint8                                                  pclntable            
-  []runtime.functab                                ftab
-uintptr                                  findfunctab
-uintptr                                  minpc
-uintptr                                  maxpc
-uintptr                                  text
-uintptr                                  etext
-uintptr                                  noptrdata
-uintptr                                  enoptrdata
-uintptr                                  data
-uintptr                                  edata
-uintptr                                  bss
-uintptr                                  ebss
-uintptr                                  noptrbss
-uintptr                                  enoptrbss
-uintptr                                  end
-uintptr                                  gcdata
-uintptr                                  gcbss
-uintptr                                  types
-uintptr                                  etypes
-uintptr                                  rodata
-uintptr                                  gofunc                
-  []runtime.textsect                              textsectmap       
-  []int32                                                  typelinks             
-  []*runtime.itab                                    itablinks             
-  []
-runtime.ptabEntry ptab
-string                                                   pluginpath          
-  []runtime.modulehash                           pkghashes
-string                                                   modulename      
-  []runtime.modulehash                           modulehashes
-uint8                                     hasmain
-runtime.bitvector                                gcdatamask
-runtime.bitvector                                gcbssmask map[runtime.typeOff]*runtime._type  typemap
-bool                                                       bad runtime.moduledata*next
-}pack()*/
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 a56b6d339f..62a6daf388 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
@@ -22,21 +22,26 @@ import ghidra.app.util.bin.format.golang.GoVer;
 import ghidra.app.util.bin.format.golang.structmapping.*;
 import ghidra.program.model.address.Address;
 import ghidra.program.model.address.AddressRange;
+import ghidra.program.model.data.*;
 import ghidra.program.model.lang.Endian;
 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.util.BigEndianDataConverter;
+import ghidra.util.LittleEndianDataConverter;
 import ghidra.util.task.TaskMonitor;
 
 /**
  * A low-level structure embedded in golang binaries that contains useful bootstrapping
  * information.
  * 

+ * Introduced in golang 1.16 * */ -@StructureMapping(structureName = "runtime.pcHeader") +@StructureMapping(structureName = GoPcHeader.GO_STRUCTURE_NAME) public class GoPcHeader { + public static final String GO_STRUCTURE_NAME = "runtime.pcHeader"; private static final String RUNTIME_PCLNTAB_SYMBOLNAME = "runtime.pclntab"; public static final String GOPCLNTAB_SECTION_NAME = "gopclntab"; public static final int GO_1_2_MAGIC = 0xfffffffb; @@ -49,29 +54,27 @@ public class GoPcHeader { * @param program {@link Program} * @return {@link Address} of go pclntab, or null if not present */ - public static Address getPclntabAddress(Program program) { + public static Address getPcHeaderAddress(Program program) { 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 = GoRttiMapper.getGoSymbol(program, RUNTIME_PCLNTAB_SYMBOLNAME); - return pclntabSymbol != null - ? pclntabSymbol.getAddress() - : null; + return pclntabSymbol != null ? pclntabSymbol.getAddress() : null; } /** - * Returns true if the specified program has an easily found pclntab + * Returns true if the specified program has an easily found pclntab w/pcHeader * * @param program {@link Program} * @return boolean true if program has a pclntab, false otherwise */ - public static boolean hasPclntab(Program program) { - Address addr = getPclntabAddress(program); + public static boolean hasPcHeader(Program program) { + Address addr = getPcHeaderAddress(program); if (addr != null) { try (ByteProvider provider = new MemoryByteProvider(program.getMemory(), addr)) { - return isPclntab(provider); + return isPcHeader(provider); } catch (IOException e) { // fall thru @@ -81,43 +84,43 @@ public class GoPcHeader { } /** - * Searches (possibly slowly) for a pclntab structure in the specified memory range, which - * is typically necessary in stripped PE binaries. + * Searches (possibly slowly) for a pclntab/pcHeader structure in the specified memory range, + * which is typically necessary in stripped PE binaries. * * @param programContext {@link GoRttiMapper} * @param range memory range to search (typically .rdata or .noptrdata sections) * @param monitor {@link TaskMonitor} that will let the user cancel - * @return {@link Address} of the found pclntab structure, or null if not found + * @return {@link Address} of the found pcHeader structure, or null if not found * @throws IOException if error reading */ - public static Address findPclntabAddress(GoRttiMapper programContext, AddressRange range, + public static Address findPcHeaderAddress(GoRttiMapper programContext, AddressRange range, TaskMonitor monitor) throws IOException { if (range == null) { return null; } // search for magic signature + padding + wildcard_minLc + ptrSize - byte[] searchBytes = new byte[/*4 + 2 + 1 + 1*/] { + byte[] searchBytes = new byte[] { // 4 + 2 + 1 + 1 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, // magic signature 0, 0, // padding 0, // unknown minLc, masked (byte) programContext.getPtrSize() // ptrSize }; - byte[] searchMask = new byte[] { + byte[] searchMask = new byte[] { // also 4 + 2 + 1 + 1 (byte) 0xf0, (byte) 0xff, (byte) 0xff, (byte) 0xf0, // magic, first byte nibble and last byte nibble is wildcard to handle either endian matching (byte) 0xff, (byte) 0xff, // padding 0, // unknown minLc - wildcarded (byte) 0xff // ptrSize }; Memory memory = programContext.getProgram().getMemory(); - Address pclntabAddr = - memory.findBytes(range.getMinAddress(), range.getMaxAddress(), searchBytes, searchMask, - true, monitor); - if (pclntabAddr == null) { + Address pcHeaderAddr = memory.findBytes(range.getMinAddress(), range.getMaxAddress(), + searchBytes, searchMask, true, monitor); + if (pcHeaderAddr == null) { return null; } - MemoryByteProvider bp = - new MemoryByteProvider(memory, pclntabAddr, range.getMaxAddress()); - return isPclntab(bp) ? pclntabAddr : null; + try (MemoryByteProvider bp = + new MemoryByteProvider(memory, pcHeaderAddr, range.getMaxAddress())) { + return isPcHeader(bp) ? pcHeaderAddr : null; + } } /** @@ -127,11 +130,10 @@ public class GoPcHeader { * @return boolean true if the byte provider has the magic signature of a pclntab * @throws IOException if error reading */ - public static boolean isPclntab(ByteProvider provider) throws IOException { + public static boolean isPcHeader(ByteProvider provider) throws IOException { byte[] header = provider.readBytes(0, 8); // logic from pclntab.go parsePclnTab() - if (provider.length() < 16 || - header[4] != 0 || header[5] != 0 || // pad bytes == 0 + if (provider.length() < 16 || header[4] != 0 || header[5] != 0 || // pad bytes == 0 (header[6] != 1 && header[6] != 2 && header[6] != 4) || // minLc == 1,2,4 (header[7] != 4 && header[7] != 8) // ptrSize == 4,8 ) { @@ -140,6 +142,23 @@ public class GoPcHeader { return readMagic(provider) != null; } + public static Structure createArtificialGoPcHeaderStructure(CategoryPath cp, + DataTypeManager dtm) { + // this manually creates a minimal struct that matches the header of a <=1.15 pclntab + // section + StructureDataType struct = new StructureDataType(cp, GO_STRUCTURE_NAME, 0, dtm); + struct.setDescription( + "Artificial structure created by Ghidra to represent the header of the pclntable"); + struct.setPackingEnabled(true); + struct.add(AbstractIntegerDataType.getUnsignedDataType(4, null), "magic", null); + struct.add(AbstractIntegerDataType.getUnsignedDataType(1, null), "pad1", null); + struct.add(AbstractIntegerDataType.getUnsignedDataType(1, null), "pad2", null); + struct.add(AbstractIntegerDataType.getUnsignedDataType(1, null), "minLC", null); + struct.add(AbstractIntegerDataType.getUnsignedDataType(1, null), "ptrSize", null); + + return struct; + } + @ContextField private GoRttiMapper programContext; @@ -156,27 +175,27 @@ public class GoPcHeader { @FieldMapping private byte ptrSize; - @FieldMapping(optional = true) // present >= 1.18 + @FieldMapping(presentWhen = "1.18+") @MarkupReference private long textStart; // should be same as offset of ".text" - @FieldMapping + @FieldMapping(presentWhen = "1.16+") @MarkupReference("getFuncnameAddress") private long funcnameOffset; - @FieldMapping + @FieldMapping(presentWhen = "1.16+") @MarkupReference("getCuAddress") private long cuOffset; - @FieldMapping + @FieldMapping(presentWhen = "1.16+") @MarkupReference("getFiletabAddress") private long filetabOffset; - @FieldMapping + @FieldMapping(presentWhen = "1.16+") @MarkupReference("getPctabAddress") private long pctabOffset; - @FieldMapping + @FieldMapping(presentWhen = "1.16+") @MarkupReference("getPclnAddress") private long pclnOffset; @@ -187,7 +206,7 @@ public class GoPcHeader { case GO_1_2_MAGIC -> GoVer.V1_2; case GO_1_16_MAGIC -> GoVer.V1_16; case GO_1_18_MAGIC -> GoVer.V1_18; - default -> GoVer.UNKNOWN; + default -> GoVer.INVALID; }; return ver; } @@ -214,7 +233,9 @@ public class GoPcHeader { * @return address of func name slice */ public Address getFuncnameAddress() { - return programContext.getDataAddress(context.getStructureStart() + funcnameOffset); + return funcnameOffset != 0 + ? programContext.getDataAddress(context.getStructureStart() + funcnameOffset) + : null; } /** @@ -222,7 +243,9 @@ public class GoPcHeader { * @return address of the cu tab slice */ public Address getCuAddress() { - return programContext.getDataAddress(context.getStructureStart() + cuOffset); + return cuOffset != 0 + ? programContext.getDataAddress(context.getStructureStart() + cuOffset) + : null; } /** @@ -230,7 +253,9 @@ public class GoPcHeader { * @return address of the filetab slice */ public Address getFiletabAddress() { - return programContext.getDataAddress(context.getStructureStart() + filetabOffset); + return filetabOffset != 0 + ? programContext.getDataAddress(context.getStructureStart() + filetabOffset) + : null; } /** @@ -238,7 +263,9 @@ public class GoPcHeader { * @return address of the pctab slice */ public Address getPctabAddress() { - return programContext.getDataAddress(context.getStructureStart() + pctabOffset); + return pctabOffset != 0 + ? programContext.getDataAddress(context.getStructureStart() + pctabOffset) + : null; } /** @@ -246,7 +273,9 @@ public class GoPcHeader { * @return address of the pcln slice */ public Address getPclnAddress() { - return programContext.getDataAddress(context.getStructureStart() + pclnOffset); + return pclnOffset != 0 + ? programContext.getDataAddress(context.getStructureStart() + pclnOffset) + : null; } /** @@ -273,8 +302,9 @@ public class GoPcHeader { } private static GoVerEndian readMagic(ByteProvider provider) throws IOException { - int leMagic = new BinaryReader(provider, true /* little endian */).readInt(0); - int beMagic = new BinaryReader(provider, false /* big endian */).readInt(0); + BinaryReader reader = new BinaryReader(provider, true); + int leMagic = reader.readInt(LittleEndianDataConverter.INSTANCE, 0); + int beMagic = reader.readInt(BigEndianDataConverter.INSTANCE, 0); if (leMagic == GO_1_2_MAGIC || beMagic == GO_1_2_MAGIC) { return new GoVerEndian(GoVer.V1_2, leMagic == GO_1_2_MAGIC); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoPcValueEvaluator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoPcValueEvaluator.java index 5bf66867b5..57160d1140 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoPcValueEvaluator.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoPcValueEvaluator.java @@ -46,7 +46,7 @@ public class GoPcValueEvaluator { GoModuledata moduledata = func.getModuledata(); this.pcquantum = moduledata.getGoBinary().getMinLC(); - this.reader = moduledata.getPctab().getElementReader(1, (int) offset); + this.reader = moduledata.getPcValueTable().getElementReader(1, (int) offset); this.funcEntry = func.getFuncAddress().getOffset(); this.pc = funcEntry; 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 34a9353539..f006453c9f 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 @@ -78,7 +78,13 @@ import ghidra.util.task.UnknownProgressWrappingTaskMonitor; * * */ -public class GoRttiMapper extends DataTypeMapper { +public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContext { + public static final GoVer SUPPORTED_MIN_VER = new GoVer(1, 15); + public static final GoVer SUPPORTED_MAX_VER = new GoVer(1, 22); + + private static final List SYMBOL_SEARCH_PREFIXES = List.of("", "_" /* macho symbols */); + private static final List SECTION_PREFIXES = + List.of("." /* ELF */, "__" /* macho sections */); private static final String FAILED_FLAG = "FAILED TO FIND GOLANG BINARY"; @@ -163,20 +169,24 @@ public class GoRttiMapper extends DataTypeMapper { return null; } - GoVer goVer = buildInfo.getVerEnum(); - if (goVer == GoVer.UNKNOWN) { + GoVer goVer = buildInfo.getGoVer(); + if (goVer.isInvalid()) { throw new BootstrapInfoException( - "Unsupported Golang version, version info: '%s'".formatted(buildInfo.getVersion())); + "Invalid Golang version string [%s]".formatted(buildInfo.getVersion())); + } + if (!goVer.inRange(SUPPORTED_MIN_VER, SUPPORTED_MAX_VER)) { + Msg.error(GoRttiMapper.class, "Untested golang version [%s]".formatted(goVer)); } ResourceFile gdtFile = findGolangBootstrapGDT(goVer, buildInfo.getPointerSize(), getGolangOSString(program)); if (gdtFile == null) { - Msg.error(GoRttiMapper.class, "Missing golang gdt archive for " + goVer); + Msg.error(GoRttiMapper.class, + "Missing golang gdt archive for golang version [%s]".formatted(goVer)); } - return new GoRttiMapper(program, buildInfo.getPointerSize(), buildInfo.getEndian(), - buildInfo.getVerEnum(), gdtFile); + return new GoRttiMapper(program, buildInfo.getPointerSize(), buildInfo.getEndian(), goVer, + gdtFile); } /** @@ -267,10 +277,6 @@ public class GoRttiMapper extends DataTypeMapper { 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. * @@ -328,6 +334,16 @@ public class GoRttiMapper extends DataTypeMapper { return zerobaseAddr; } + public static List getAllSupportedVersions() { + List result = new ArrayList<>(); + for (int minor = SUPPORTED_MIN_VER.getMinor(); minor <= SUPPORTED_MAX_VER + .getMinor(); minor++) { + // TODO: a bit of a hack only supporting 1.x version number instances + result.add(new GoVer(1, minor)); + } + return result; + } + public final static String ARTIFICIAL_RUNTIME_ZEROBASE_SYMBOLNAME = "ARTIFICIAL.runtime.zerobase"; @@ -415,7 +431,7 @@ public class GoRttiMapper extends DataTypeMapper { this.stringDT = getTypeOrDefault("string", Structure.class, null); try { - registerStructures(GOLANG_STRUCTMAPPED_CLASSES); + registerStructures(GOLANG_STRUCTMAPPED_CLASSES, this); } catch (IOException e) { if (archiveGDT == null) { @@ -433,6 +449,20 @@ public class GoRttiMapper extends DataTypeMapper { } } + @Override + public T getType(String name, Class clazz) { + T result = super.getType(name, clazz); + if (result == null && GoPcHeader.GO_STRUCTURE_NAME.equals(name) && + Structure.class.isAssignableFrom(clazz)) { + // create an artificial runtime.pcHeader structure for <=1.15 to enable GoModuledata + // to have references to a GoPcHeader + result = clazz.cast(GoPcHeader.createArtificialGoPcHeaderStructure(GOLANG_CP, + program.getDataTypeManager())); + } + return result; + } + + /** * Returns the golang version * @return {@link GoVer} @@ -462,10 +492,12 @@ public class GoRttiMapper extends DataTypeMapper { GoModuledata firstModule = findFirstModuledata(monitor); if (firstModule != null) { GoPcHeader pcHeader = firstModule.getPcHeader(); - this.minLC = pcHeader.getMinLC(); - if (pcHeader.getPtrSize() != ptrSize) { - throw new IOException( - "Mismatched ptrSize: %d vs %d".formatted(pcHeader.getPtrSize(), ptrSize)); + if (pcHeader != null) { + this.minLC = pcHeader.getMinLC(); + if (pcHeader.getPtrSize() != ptrSize) { + throw new IOException( + "Mismatched ptrSize: %d vs %d".formatted(pcHeader.getPtrSize(), ptrSize)); + } } addModule(firstModule); } @@ -1401,22 +1433,22 @@ public class GoRttiMapper extends DataTypeMapper { private GoModuledata findFirstModuledata(TaskMonitor monitor) throws IOException { GoModuledata result = GoModuledata.getFirstModuledata(this); - if (result == null) { - monitor.setMessage("Searching for Golang pclntab"); - monitor.initialize(0); - Address pclntabAddress = GoPcHeader.getPclntabAddress(program); - if (pclntabAddress == null) { - pclntabAddress = - GoPcHeader.findPclntabAddress(this, getPclntabSearchRange(), monitor); - } - if (pclntabAddress != null) { - monitor.setMessage("Searching for Golang firstmoduledata"); - monitor.initialize(0); - GoPcHeader pclntab = readStructure(GoPcHeader.class, pclntabAddress); - result = GoModuledata.findFirstModule(this, pclntabAddress, pclntab, - getModuledataSearchRange(), monitor); - } + Address pcHeaderAddress = + result != null ? result.getPcHeaderAddress() : GoPcHeader.getPcHeaderAddress(program); + if (pcHeaderAddress == null) { + monitor.initialize(0, "Searching for Golang pclntab"); + pcHeaderAddress = + GoPcHeader.findPcHeaderAddress(this, getPclntabSearchRange(), monitor); } + if (result == null && pcHeaderAddress != null) { + // find the moduledata struct by searching for a pointer to the pclntab/pcHeader, + // which should be the first field in the moduledata struct. + monitor.initialize(0, "Searching for Golang firstmoduledata"); + GoPcHeader pcHeader = readStructure(GoPcHeader.class, pcHeaderAddress); + result = GoModuledata.findFirstModule(this, pcHeaderAddress, pcHeader, + getModuledataSearchRange(), monitor); + } + if (result != null && !result.isValid()) { throw new IOException("Invalid Golang moduledata at %s" .formatted(result.getStructureContext().getStructureAddress())); @@ -1554,4 +1586,25 @@ public class GoRttiMapper extends DataTypeMapper { } } + + @Override + public boolean isFieldPresent(String presentWhen) { + presentWhen = presentWhen.strip(); + if (presentWhen.isEmpty()) { + return true; + } + String[] verNums = presentWhen.split("[+-]", -1); // "1.2-1.5" or "1.2+" or "-1.2" + if (verNums.length != 2) { + throw new IllegalArgumentException( + "Invalid 'presentWhen' value [%s]".formatted(presentWhen)); + } + GoVer startVer = verNums[0].isBlank() ? new GoVer(1, 0) : GoVer.parse(verNums[0]); + GoVer endVer = verNums[1].isBlank() ? new GoVer(99, 99) : GoVer.parse(verNums[1]); + if (startVer.isInvalid() || endVer.isInvalid()) { + throw new IllegalArgumentException( + "Invalid 'presentWhen' value [%s]".formatted(presentWhen)); + } + return startVer.compareTo(goVersion) <= 0 && goVersion.compareTo(endVer) <= 0; + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoVarlenString.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoVarlenString.java index 6e5685203d..1513f0a5bc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoVarlenString.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoVarlenString.java @@ -20,11 +20,14 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.golang.GoVer; import ghidra.app.util.bin.format.golang.structmapping.*; import ghidra.program.model.data.*; +import ghidra.util.BigEndianDataConverter; /** - * A pascal-ish string, using a LEB128 value as the length of the following bytes. + * A pascal-ish string, using a LEB128 (or a uint16 in pre-1.16) value as the length of the + * following bytes. *

* Used mainly in lower-level RTTI structures, this class is a ghidra'ism used to parse the * golang rtti data and does not have a counterpart in the golang src. @@ -51,9 +54,16 @@ public class GoVarlenString implements StructureReader { readFrom(context.getReader()); } + private boolean useLEB128() { + return ((GoRttiMapper) context.getDataTypeMapper()).getGolangVersion() + .isAtLeast(GoVer.V1_17); + } + private void readFrom(BinaryReader reader) throws IOException { long startPos = reader.getPointerIndex(); - int strLen = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + int strLen = useLEB128() + ? reader.readNextUnsignedVarIntExact(LEB128::unsigned) + : reader.readNextUnsignedShort(BigEndianDataConverter.INSTANCE); this.strlenLen = (int) (reader.getPointerIndex() - startPos); this.bytes = reader.readNextByteArray(strLen); } @@ -68,9 +78,9 @@ public class GoVarlenString implements StructureReader { } /** - * Returns the string length's length (length of the leb128 number) + * Returns the size of the string length field. * - * @return string length's length + * @return size of the string length field */ public int getStrlenLen() { return strlenLen; @@ -100,8 +110,11 @@ public class GoVarlenString implements StructureReader { * @return data type needed to hold the string length field */ public DataTypeInstance getStrlenDataType() { - return DataTypeInstance.getDataTypeInstance(UnsignedLeb128DataType.dataType, strlenLen, - false); + DataType dt = useLEB128() + ? UnsignedLeb128DataType.dataType + : AbstractIntegerDataType.getUnsignedDataType(2, null); + + return DataTypeInstance.getDataTypeInstance(dt, strlenLen, false); } /** @@ -119,5 +132,4 @@ public class GoVarlenString implements StructureReader { return String.format("GoVarlenString [context=%s, strlenLen=%s, bytes=%s, getString()=%s]", context, strlenLen, Arrays.toString(bytes), getString()); } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoArrayType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoArrayType.java index 1719ffceb9..f17c7f4e07 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoArrayType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoArrayType.java @@ -20,8 +20,7 @@ import java.util.Set; import ghidra.app.util.bin.format.golang.structmapping.*; import ghidra.app.util.viewer.field.AddressAnnotatedStringHandler; -import ghidra.program.model.data.ArrayDataType; -import ghidra.program.model.data.DataType; +import ghidra.program.model.data.*; /** * {@link GoType} structure that defines an array. @@ -72,7 +71,15 @@ public class GoArrayType extends GoType { if (self != null) { return self; } - return new ArrayDataType(elementDt, (int) len, -1); + return isValidLength() + ? new ArrayDataType(elementDt, (int) len, -1) + : new TypedefDataType(elementDt.getCategoryPath(), + ".invalid_arraysize_%d_%s".formatted(len, elementDt.getName()), + new ArrayDataType(elementDt, 1, -1), elementDt.getDataTypeManager()); + } + + private boolean isValidLength() { + return 0 <= len && len <= Integer.MAX_VALUE; } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructField.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructField.java index 1474c29559..d379b3e5f0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructField.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructField.java @@ -42,10 +42,10 @@ public class GoStructField { @MarkupReference("getType") private long typ; // direct ptr to GoType - @FieldMapping(optional = true) //<=1.18 + @FieldMapping(presentWhen = "-1.18") private long offsetAnon; // offsetAnon >> 1 == actual offset, bit 0 = embedded flag - @FieldMapping(optional = true) //>=1.19 + @FieldMapping(presentWhen = "1.19+") private long offset; /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java index 690b4aa795..85cc8b6c53 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java @@ -31,7 +31,7 @@ import ghidra.util.task.TaskMonitor; /** * Information about {@link StructureMapping} classes and their metadata. *

- * To use the full might and majesty of StructureMapping(tm), a DataTypeMapper must be created. It + * To use the full might and majesty of StructureMapping™, a DataTypeMapper must be created. It * must be able to {@link #addArchiveSearchCategoryPath(CategoryPath...) find} * ({@link #addProgramSearchCategoryPath(CategoryPath...) more find}) the Ghidra structure data * types being used, and it must {@link #registerStructure(Class) know} about all classes that are @@ -166,9 +166,11 @@ public class DataTypeMapper implements AutoCloseable { * @param structure mapped class type * @param clazz class that represents a structure, marked with {@link StructureMapping} * annotation + * @param context {@link DataTypeMapperContext} * @throws IOException if the class's Ghidra structure data type could not be found */ - public void registerStructure(Class clazz) throws IOException { + public void registerStructure(Class clazz, DataTypeMapperContext context) + throws IOException { StructureMapping sma = clazz.getAnnotation(StructureMapping.class); List structNames = sma != null ? Arrays.asList(sma.structureName()) : List.of(); Structure structDT = getType(structNames, Structure.class); @@ -187,7 +189,7 @@ public class DataTypeMapper implements AutoCloseable { try { StructureMappingInfo structMappingInfo = - StructureMappingInfo.fromClass(clazz, structDT); + StructureMappingInfo.fromClass(clazz, structDT, context); mappingInfo.put(clazz, structMappingInfo); } catch (IllegalArgumentException e) { @@ -199,11 +201,13 @@ public class DataTypeMapper implements AutoCloseable { * Registers the specified {@link StructureMapping structure mapping} classes. * * @param classes list of classes to register + * @param context {@link DataTypeMapperContext} * @throws IOException if a class's Ghidra structure data type could not be found */ - public void registerStructures(List> classes) throws IOException { + public void registerStructures(List> classes, DataTypeMapperContext context) + throws IOException { for (Class clazz : classes) { - registerStructure(clazz); + registerStructure(clazz, context); } } @@ -544,5 +548,4 @@ public class DataTypeMapper implements AutoCloseable { } return new StructureContext<>(this, smi, null); } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapperContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapperContext.java new file mode 100644 index 0000000000..1b19de89e3 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapperContext.java @@ -0,0 +1,32 @@ +/* ### + * 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.util.bin.format.golang.structmapping; + +/** + * Context passed to StructureMapping logic when binding a structure's fields to a java class's + * fields. + */ +public interface DataTypeMapperContext { + + /** + * Tests if a field should be included when creating bindings between a structure and a class. + * + * @param presentWhen free-form string that is interpreted by each {@link DataTypeMapper} + * @return boolean true if field should be bound, false if field should not be bound + */ + boolean isFieldPresent(String presentWhen); + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/FieldMapping.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/FieldMapping.java index 29ccc9f54f..bb8ee0f9e2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/FieldMapping.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/FieldMapping.java @@ -62,6 +62,22 @@ public @interface FieldMapping { */ boolean optional() default false; + /** + * Marks this field as only present in certain context configurations. + *

+ * The specified string is interpreted by the specific {@link DataTypeMapper} and its + * {@link DataTypeMapperContext context}. + *

+ * For example, a version number could be used to allow some optional fields to be skipped + * depending on the the concrete {@link DataTypeMapper}'s information during structure + * mapping registration. + *

+ * Similar to {@link #optional()} + * + * @return String + */ + String presentWhen() default ""; + /** * Specifies the name of a setter method that will be used to assign the deserialized value * to the java field. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/StructureMappingInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/StructureMappingInfo.java index 10927ebdd9..f6c4392be8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/StructureMappingInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/StructureMappingInfo.java @@ -39,18 +39,19 @@ public class StructureMappingInfo { * @param targetClass structure mapped class * @param structDataType Ghidra {@link DataType} that defines the binary layout of the mapped * fields of the class, or null if this is a self-reading {@link StructureReader} class + * @param context {@link DataTypeMapperContext} * @return new {@link StructureMappingInfo} for the specified class * @throws IllegalArgumentException if targetClass isn't tagged as a structure mapped class */ public static StructureMappingInfo fromClass(Class targetClass, - Structure structDataType) { + Structure structDataType, DataTypeMapperContext context) { StructureMapping sma = targetClass.getAnnotation(StructureMapping.class); if (sma == null) { throw new IllegalArgumentException( "Missing @StructureMapping annotation on " + targetClass.getSimpleName()); } - return new StructureMappingInfo<>(targetClass, structDataType, sma); + return new StructureMappingInfo<>(targetClass, structDataType, sma, context); } private final Class targetClass; @@ -69,17 +70,17 @@ public class StructureMappingInfo { private Field structureContextField; private StructureMappingInfo(Class targetClass, Structure structDataType, - StructureMapping sma) { + StructureMapping sma, DataTypeMapperContext context) { this.targetClass = targetClass; this.structureDataType = structDataType; this.structureName = structureDataType != null ? structureDataType.getName() : sma.structureName()[0]; - this.fieldNameLookup = indexStructFields(structDataType); + this.fieldNameLookup = indexStructFields(); this.useFieldMappingInfo = !StructureReader.class.isAssignableFrom(targetClass); this.instanceCreator = findInstanceCreator(); - readFieldInfo(targetClass); + readFieldInfo(targetClass, context); Collections.sort(outputFields, (foi1, foi2) -> Integer.compare(foi1.getOrdinal(), foi2.getOrdinal())); @@ -249,10 +250,10 @@ public class StructureMappingInfo { } } - private void readFieldInfo(Class clazz) { + private void readFieldInfo(Class clazz, DataTypeMapperContext context) { Class superclass = clazz.getSuperclass(); if (superclass != null) { - readFieldInfo(superclass); + readFieldInfo(superclass, context); } for (Field field : clazz.getDeclaredFields()) { @@ -260,7 +261,7 @@ public class StructureMappingInfo { FieldMapping fma = field.getAnnotation(FieldMapping.class); FieldOutput foa = field.getAnnotation(FieldOutput.class); if (fma != null || foa != null) { - FieldMappingInfo fmi = readFieldMappingInfo(field, fma); + FieldMappingInfo fmi = readFieldMappingInfo(field, fma, context); if (fmi == null) { // was marked optional field, just skip continue; @@ -287,7 +288,14 @@ public class StructureMappingInfo { } } - private FieldMappingInfo readFieldMappingInfo(Field field, FieldMapping fma) { + private FieldMappingInfo readFieldMappingInfo(Field field, FieldMapping fma, + DataTypeMapperContext context) { + + if (fma != null && !context.isFieldPresent(fma.presentWhen())) { + // skip if this field was marked as not present + return null; + } + String[] fieldNames = getFieldNamesToSearchFor(field, fma); DataTypeComponent dtc = getFirstMatchingField(fieldNames); if (useFieldMappingInfo && dtc == null) { @@ -379,12 +387,12 @@ public class StructureMappingInfo { return struct.isZeroLength() ? 0 : struct.getLength(); } - private static Map indexStructFields(Structure struct) { - if (struct == null) { + private Map indexStructFields() { + if (structureDataType == null) { return Map.of(); } Map result = new HashMap<>(); - for (DataTypeComponent dtc : struct.getDefinedComponents()) { + for (DataTypeComponent dtc : structureDataType.getDefinedComponents()) { String fieldName = dtc.getFieldName(); if (fieldName != null) { result.put(fieldName.toLowerCase(), dtc); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java index ff8ff98917..2406b9079d 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java @@ -45,6 +45,8 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { Map.entry(GENERIC_CLIB_32_GDT_PATH, "2644097909188870631"), Map.entry(GENERIC_CLIB_64_GDT_PATH, "3193699959493190971"), Map.entry(MAC_OS_10_9_GDT_PATH, "2650667045259492112"), + Map.entry("typeinfo/golang/golang_1.15_anybit_any.gdt", "3600806988729184131"), + Map.entry("typeinfo/golang/golang_1.16_anybit_any.gdt", "3597021567582750001"), Map.entry("typeinfo/golang/golang_1.17_anybit_any.gdt", "3533627828569507753"), Map.entry("typeinfo/golang/golang_1.18_anybit_any.gdt", "3528902399865061936"), Map.entry("typeinfo/golang/golang_1.19_anybit_any.gdt", "3533812166493410774"), diff --git a/Ghidra/Processors/AARCH64/data/languages/AARCH64.ldefs b/Ghidra/Processors/AARCH64/data/languages/AARCH64.ldefs index 9d83a37e1e..dad5496b43 100644 --- a/Ghidra/Processors/AARCH64/data/languages/AARCH64.ldefs +++ b/Ghidra/Processors/AARCH64/data/languages/AARCH64.ldefs @@ -15,6 +15,7 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Ghidra/Processors/AARCH64/data/languages/AARCH64_golang.register.info b/Ghidra/Processors/AARCH64/data/languages/AARCH64_golang.register.info index ff42eb3c66..683cba8998 100644 --- a/Ghidra/Processors/AARCH64/data/languages/AARCH64_golang.register.info +++ b/Ghidra/Processors/AARCH64/data/languages/AARCH64_golang.register.info @@ -6,5 +6,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/Ghidra/Processors/x86/data/languages/x86-32-golang.register.info b/Ghidra/Processors/x86/data/languages/x86-32-golang.register.info index 28c42ff062..98b573e051 100644 --- a/Ghidra/Processors/x86/data/languages/x86-32-golang.register.info +++ b/Ghidra/Processors/x86/data/languages/x86-32-golang.register.info @@ -5,5 +5,6 @@ + \ No newline at end of file diff --git a/Ghidra/Processors/x86/data/languages/x86-64-golang.cspec b/Ghidra/Processors/x86/data/languages/x86-64-golang.cspec index 8196a9a68e..ed1d68d372 100644 --- a/Ghidra/Processors/x86/data/languages/x86-64-golang.cspec +++ b/Ghidra/Processors/x86/data/languages/x86-64-golang.cspec @@ -26,16 +26,16 @@ - + - + - + - + @@ -63,7 +63,7 @@ - + @@ -195,6 +195,14 @@ + + + + + + + + @@ -228,8 +236,6 @@ - - diff --git a/Ghidra/Processors/x86/data/languages/x86-64-golang.register.info b/Ghidra/Processors/x86/data/languages/x86-64-golang.register.info index 49222ecea8..802caffb0a 100644 --- a/Ghidra/Processors/x86/data/languages/x86-64-golang.register.info +++ b/Ghidra/Processors/x86/data/languages/x86-64-golang.register.info @@ -6,5 +6,14 @@ + + + + + + + + + \ No newline at end of file