diff --git a/objfile/objfile.go b/objfile/objfile.go index 2bfd60a..306ff19 100644 --- a/objfile/objfile.go +++ b/objfile/objfile.go @@ -10,6 +10,7 @@ package objfile import ( "bytes" "encoding/binary" + "errors" "fmt" "io" "os" @@ -839,7 +840,7 @@ func (e *Entry) readVarint(address uint64) (int, int, error) { } } -func (e *Entry) readRTypeName(runtimeVersion string, typeFlags tflag, namePtr uint64, is64bit bool, littleendian bool) (name string, err error) { +func (e *Entry) readRTypeName(runtimeVersion string, typeFlags tflag, namePtr uint64, is64bit bool, littleendian bool) (name string, tag string, err error) { // name str (for <= 1.16 encodes length like this, beyond it uses a varint encoding) // The first byte is a bit field containing: // @@ -872,23 +873,24 @@ func (e *Entry) readRTypeName(runtimeVersion string, typeFlags tflag, namePtr ui case "1.5": fallthrough case "1.6": + // TODO: parse field tag // pointer to GoString nameLen, err := e.ReadPointerSizeMem(namePtr+ptrSize, is64bit, littleendian) if err != nil { - return "", fmt.Errorf("Failed to read name") + return "", "", fmt.Errorf("Failed to read name") } deref, err := e.ReadPointerSizeMem(namePtr, is64bit, littleendian) if err != nil { - return "", fmt.Errorf("Failed to read name") + return "", "", fmt.Errorf("Failed to read name") } name_raw, err := e.raw.read_memory(deref, nameLen) if err != nil { - return "", fmt.Errorf("Failed to read name") + return "", "", fmt.Errorf("Failed to read name") } - return string(name_raw), nil + return string(name_raw), "", nil case "1.7": // types flags exists >= 1.7 fallthrough case "1.8": // type flag tflagExtraStart exists >= 1.8 @@ -908,22 +910,41 @@ func (e *Entry) readRTypeName(runtimeVersion string, typeFlags tflag, namePtr ui case "1.15": fallthrough case "1.16": - name_len_raw, err := e.raw.read_memory(namePtr, 3) + flag, err := e.raw.read_memory(namePtr, 1) if err != nil { - return "", fmt.Errorf("Failed to read name") + return "", "", errors.New("Failed to read flag") } - name_len := uint16(uint16(name_len_raw[1])<<8 | uint16(name_len_raw[2])) + name_len_raw, err := e.raw.read_memory(namePtr+1, 2) + if err != nil { + return "", "", fmt.Errorf("Failed to read name") + } + + name_len := uint16(uint16(name_len_raw[0])<<8 | uint16(name_len_raw[1])) name_raw, err := e.raw.read_memory(namePtr+3, uint64(name_len)) if err != nil { - return "", fmt.Errorf("Failed to read name") + return "", "", fmt.Errorf("Failed to read name") } name := string(name_raw) + + if flag[0] == 3 { + tag_len_raw, err := e.raw.read_memory(namePtr+3+uint64(name_len), 2) + if err != nil { + return "", "", errors.New("Failed to read tag") + } + + tag_len := uint16(uint16(tag_len_raw[0])<<8 | uint16(tag_len_raw[1])) + tag_raw, err := e.raw.read_memory(namePtr+3+uint64(name_len)+2, uint64(tag_len)) + if err != nil { + return "", "", errors.New("Failed to read tag") + } + tag = string(tag_raw) + } if typeFlags&tflagExtraStar != 0 { - return name[1:], nil + return name[1:], tag, nil } else { - return name, nil + return name, tag, nil } case "1.17": fallthrough @@ -932,24 +953,39 @@ func (e *Entry) readRTypeName(runtimeVersion string, typeFlags tflag, namePtr ui case "1.19": fallthrough case "1.20": + flag, err := e.raw.read_memory(namePtr, 1) + if err != nil { + return "", "", errors.New("Failed to read flag") + } varint_len, namelen, err := e.readVarint(namePtr + 1) if err != nil { - return "", fmt.Errorf("Failed to read name") + // TODO: replace all misuses of fmt.Errorf to errors.New + return "", "", fmt.Errorf("Failed to read name") } - name_raw, err := e.raw.read_memory(namePtr+1+uint64(varint_len), uint64(namelen)) if err != nil { - return "", fmt.Errorf("Failed to read name") + return "", "", fmt.Errorf("Failed to read name") } - name := string(name_raw) + + if flag[0] == 3 { + varint_len, taglen, err := e.readVarint(namePtr + 1 + uint64(varint_len) + uint64(namelen)) + if err != nil { + return "", "", errors.New("Failed to read tag") + } + tag_raw, err := e.raw.read_memory(namePtr+1+uint64(varint_len)+uint64(namelen)+uint64(varint_len), uint64(taglen)) + if err != nil { + return "", "", errors.New("Failed to read tag") + } + tag = string(tag_raw) + } if typeFlags&tflagExtraStar != 0 { - return name[1:], nil + return name[1:], tag, nil } else { - return name, nil + return name, tag, nil } } - return "", fmt.Errorf("Failed to read name") + return "", "", fmt.Errorf("Failed to read name") } func decodePtrSizeBytes(data []byte, is64bit bool, littleendian bool) (result uint64) { @@ -1056,7 +1092,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } - name, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1074,7 +1110,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } - name, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1093,7 +1129,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } - name, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1110,7 +1146,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } - name, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, 0, uint64(rtype.Str), is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1140,7 +1176,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } name_ptr := moduleData.Types + uint64(rtype.Str) - name, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1156,7 +1192,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } name_ptr := moduleData.Types + uint64(rtype.Str) - name, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1186,7 +1222,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } name_ptr := moduleData.Types + uint64(rtype.Str) - name, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1202,7 +1238,7 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty return parsedTypesIn, fmt.Errorf("Failed to parse type") } name_ptr := moduleData.Types + uint64(rtype.Str) - name, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) + name, _, err := e.readRTypeName(runtimeVersion, rtype.Tflag, name_ptr, is64bit, littleendian) if err != nil { return parsedTypesIn, fmt.Errorf("Failed to read type name") } @@ -1597,9 +1633,12 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty field, found := parsedTypesIn.Get(typeAddr) if found { typeNameAddr := decodePtrSizeBytes(data[0:ptrSize], is64bit, littleendian) - typeName, err := e.readRTypeName(runtimeVersion, 0, typeNameAddr, is64bit, littleendian) + typeName, tag, err := e.readRTypeName(runtimeVersion, 0, typeNameAddr, is64bit, littleendian) if err == nil { - structDef += fmt.Sprintf("\n %-10s %s", typeName, field.(Type).Str) + if tag != "" { + tag = fmt.Sprintf("`%s`", tag) + } + structDef += fmt.Sprintf("\n %-10s %s %s", typeName, field.(Type).Str, tag) cstructDef += fmt.Sprintf(" %-10s %s;\n", field.(Type).CStr, replace_cpp_keywords(typeName)) } } @@ -1690,9 +1729,13 @@ func (e *Entry) ParseType_impl(runtimeVersion string, moduleData *ModuleData, ty field, found := parsedTypesIn.Get(typeAddr) if found { typeNameAddr := decodePtrSizeBytes(data[0:ptrSize], is64bit, littleendian) - typeName, err := e.readRTypeName(runtimeVersion, 0, typeNameAddr, is64bit, littleendian) + typeName, tag, err := e.readRTypeName(runtimeVersion, 0, typeNameAddr, is64bit, littleendian) if err == nil { - structDef += fmt.Sprintf("\n %-10s %s", typeName, field.(Type).Str) + if tag != "" { + tag = fmt.Sprintf("`%s`", tag) + } + // FIXME: if a nested struct contains field tags, the `Str` name is not correct + structDef += fmt.Sprintf("\n %-10s %s %s", typeName, field.(Type).Str, tag) cstructDef += fmt.Sprintf(" %-10s %s;\n", field.(Type).CStr, replace_cpp_keywords(typeName)) } }