package pkg import ( "encoding/binary" "fmt" "io" "strconv" "strings" ) type asmOutLine struct { pos int word int kindData int64 valueData []byte indent int kind string value string } var spacingLine = asmOutLine{ pos: -1, kindData: -1, valueData: nil, indent: 0, kind: "", value: "", } func Disassemble(r io.Reader, real bool, showHeader bool, force bool, raw bool) (s string, err error) { var lines []asmOutLine sb := new(strings.Builder) header := true pos := new(int) for err == nil { if header { var kind uint64 var size uint64 var bsize []byte p := *pos if _, kind, err = nextUint64(r, pos); err != nil { break } if bsize, size, err = nextUint64(r, pos); err != nil { break } if showHeader { lines = append(lines, asmOutLine{p, 8, int64(kind), bsize, 0, "head " + intToKind(kind), ""}) } for i := 0; uint64(i) < size; i++ { var did Checksum var dkind uint64 p := *pos if _, dkind, err = nextUint64(r, pos); err != nil { break } if _, did, err = nextIdent(r, pos); err != nil { break } if showHeader { lines = append(lines, asmOutLine{p, 8, int64(dkind), nil, 1, intToKind(dkind), Encode(did)}) } } header = false } var k uint32 p := *pos if _, k, err = nextUint32(r, pos); err != nil { break } kind := IRValueKind(k) switch kind { case IRKindEnd: var a uint32 var ba []byte if ba, a, err = nextUint32(r, pos); err != nil { break } if a&1 != 0 { var sum Checksum if _, sum, err = nextIdent(r, pos); err != nil { break } lines = append(lines, asmOutLine{p, 4, int64(kind), ba, 1, "end ", Encode(sum)}) } else { lines = append(lines, asmOutLine{p, 4, int64(kind), []byte{0, 0, 0, 0}, 1, "end ", ""}) } lines = append(lines, spacingLine) header = true continue case IRKindIdent: var a []byte // discard ancillary if a, _, err = nextUint32(r, pos); err != nil { break } var sum Checksum if _, sum, err = nextIdent(r, pos); err != nil { break } lines = append(lines, asmOutLine{p, 4, int64(kind), a, 1, "id ", Encode(sum)}) continue case IRKindUint32: var i uint32 var bi []byte if bi, i, err = nextUint32(r, pos); err != nil { break } lines = append(lines, asmOutLine{p, 4, int64(kind), bi, 1, "int ", strconv.FormatUint(uint64(i), 10)}) case IRKindString: var l uint32 var bl []byte if bl, l, err = nextUint32(r, pos); err != nil { break } s := make([]byte, l+(wordSize-(l)%wordSize)%wordSize) var n int if n, err = r.Read(s); err != nil { break } *pos = *pos + n lines = append(lines, asmOutLine{p, 4, int64(kind), bl, 1, "str ", strconv.Quote(string(s[:l]))}) continue default: var bi []byte if bi, _, err = nextUint32(r, pos); err != nil { break } lines = append(lines, asmOutLine{p, 4, int64(kind), bi, 1, "????", ""}) } } if err != io.EOF { return } err = nil for _, line := range lines { if raw { if line.pos != -1 { sb.WriteString(fmt.Sprintf("%s\t%s\n", line.kind, line.value)) } } else { if line.pos == -1 { sb.WriteString("\n") } else if line.word == 4 { sb.WriteString(fmt.Sprintf("%06x: %04x %04x%s %s %s\n", line.pos, binary.LittleEndian.AppendUint32(nil, uint32(line.kindData)), line.valueData, headerSpacing(showHeader), line.kind, line.value)) } else { kind := binary.LittleEndian.AppendUint64(nil, uint64(line.kindData)) value := line.valueData if len(value) == 8 { sb.WriteString(fmt.Sprintf("%06x: %04x %04x %04x %04x %s %s\n", line.pos, kind[:4], kind[4:], value[:4], value[4:], line.kind, line.value)) } else { sb.WriteString(fmt.Sprintf("%06x: %04x %04x %s %s\n", line.pos, kind[:4], kind[4:], line.kind, line.value)) } } } } return sb.String(), err } func nextUint32(r io.Reader, pos *int) ([]byte, uint32, error) { i := make([]byte, 4) _, err := r.Read(i) if err != nil { return i, 0, err } p := *pos + 4 *pos = p return i, binary.LittleEndian.Uint32(i), nil } func nextUint64(r io.Reader, pos *int) ([]byte, uint64, error) { i := make([]byte, 8) _, err := r.Read(i) if err != nil { return i, 0, err } p := *pos + 8 *pos = p return i, binary.LittleEndian.Uint64(i), nil } func nextIdent(r io.Reader, pos *int) ([]byte, Checksum, error) { i := make([]byte, 48) if _, err := r.Read(i); err != nil { return i, Checksum{}, err } p := *pos + 48 *pos = p return i, Checksum(i), nil } func intToKind(i uint64) string { switch Kind(i) { case KindHTTPGet: return "http" case KindTar: return "tar " case KindExec: return "exec" case KindExecNet: return "exen" case KindFile: return "file" default: return fmt.Sprintf("$%d ", i-KindCustomOffset) } } func headerSpacing(showHeader bool) string { if showHeader { return " " } return "" }