mandiant / GoReSym

Go symbol recovery tool
MIT License
498 stars 62 forks source link

The "Types" and "Interfaces" output fields are always null for programs built by Go 1.21.1 amd64 #39

Closed agentzh closed 9 months ago

agentzh commented 9 months ago

Simplest Go programs can reproduce this issue using the latest master branch of GoReSym with go 1.21.1 (downgrading to go 1.20.1 to recompile the same Go programs make the Types field appear again).

    "Types": null,
    "Interfaces": null,
stevemk14ebr commented 9 months ago

Thanks for the report! I've not added go 1.21 support yet, it's been on my TODO. To fix, the version switches in this file need to add the 1.21 case:

example: https://github.com/mandiant/GoReSym/blob/944c2b703e250ffde21b3e6dfac81b0032fc0257/objfile/objfile.go#L1177

There are two or so spot like this that need extended. Assuming the layout is the same as 1.20, possible other adjustments are required if the internal structures changed.

agentzh commented 9 months ago

@stevemk14ebr Thanks for your quick reply! I've tried adding the case "1.21" code as suggested but the Types, Interfaces and Files fields in the JSON output are still null. So it seems that the layout has changed in 1.21? I tested the following patch on my side:

diff --git a/objfile/objfile.go b/objfile/objfile.go
index 2bfd60a..5be52f5 100644
--- a/objfile/objfile.go
+++ b/objfile/objfile.go
@@ -288,6 +288,8 @@ func (e *Entry) ModuleDataTable(pclntabVA uint64, runtimeVersion string, version
                // this routine needs the pclntab version, NOT the go runtime version (ex: go 1.15 generates 1.2 style tables)
                switch version {
                case "1.20":
+                       fallthrough
+               case "1.21":
                        if is64bit {
                                var module ModuleData120_64
                                err := module.parse(moduleDataCandidate.Moduledata, littleendian)
@@ -932,6 +934,8 @@ func (e *Entry) readRTypeName(runtimeVersion string, typeFlags tflag, namePtr ui
        case "1.19":
                fallthrough
        case "1.20":
+               fallthrough
+       case "1.21":
                varint_len, namelen, err := e.readVarint(namePtr + 1)
                if err != nil {
                        return "", fmt.Errorf("Failed to read name")
@@ -1175,6 +1179,8 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty
        case "1.19":
                fallthrough
        case "1.20":
+               fallthrough
+       case "1.21":
                if is64bit {
                        var rtype Rtype114_115_116_117_118_64
                        rtype_raw, err := e.raw.read_memory(typeAddress, uint64(unsafe.Sizeof(rtype)))
@@ -1480,6 +1486,8 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty
                case "1.19":
                        fallthrough
                case "1.20":
+                       fallthrough
+               case "1.21":
                        var methodsStartAddr uint64 = typeAddress + uint64(_type.baseSize) + ptrSize
                        var methods GoSlice64 = GoSlice64{}
                        if is64bit {
@@ -1637,6 +1645,8 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty
                case "1.19":
                        fallthrough
                case "1.20":
+                       fallthrough
+               case "1.21":
                        // type structType struct {
                        //      rtype
                        //      pkgPath name // pointer
agentzh commented 9 months ago

@stevemk14ebr Sorry, never mind, I forgot to specify the -t and -p options while testing the patch above. So that patch actually works :) Will you merge that into the upstream version please?

Thanks!

stevemk14ebr commented 9 months ago

Great! Would you submit the patch as a PR please? This way you could become a contributor