From b8cc805bc645aa3b635c3251e201e4e2ae5e43d5 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 8 Sep 2014 00:08:51 -0400 Subject: [PATCH] build: move package sources from src/pkg to src Preparation was in CL 134570043. This CL contains only the effect of 'hg mv src/pkg/* src'. For more about the move, see golang.org/s/go14nopkg. --- dwarf/buf.go | 181 ++ dwarf/const.go | 454 +++++ dwarf/entry.go | 401 ++++ dwarf/open.go | 87 + dwarf/testdata/typedef.c | 85 + dwarf/testdata/typedef.elf | Bin 0 -> 12448 bytes dwarf/testdata/typedef.elf4 | Bin 0 -> 9496 bytes dwarf/testdata/typedef.macho | Bin 0 -> 5024 bytes dwarf/type.go | 685 +++++++ dwarf/type_test.go | 122 ++ dwarf/typeunit.go | 166 ++ dwarf/unit.go | 90 + elf/elf.go | 1773 +++++++++++++++++ elf/elf_test.go | 49 + elf/file.go | 945 +++++++++ elf/file_test.go | 345 ++++ elf/symbols_test.go | 834 ++++++++ elf/testdata/gcc-386-freebsd-exec | Bin 0 -> 5742 bytes elf/testdata/gcc-amd64-linux-exec | Bin 0 -> 8844 bytes .../gcc-amd64-openbsd-debug-with-rela.obj | Bin 0 -> 6544 bytes elf/testdata/go-relocation-test-clang-x86.obj | Bin 0 -> 1900 bytes .../go-relocation-test-gcc424-x86-64.obj | Bin 0 -> 3088 bytes .../go-relocation-test-gcc441-x86-64.obj | Bin 0 -> 2936 bytes .../go-relocation-test-gcc441-x86.obj | Bin 0 -> 1884 bytes .../go-relocation-test-gcc482-aarch64.obj | Bin 0 -> 3392 bytes elf/testdata/hello-world-core.gz | Bin 0 -> 12678 bytes elf/testdata/hello.c | 7 + goobj/read.go | 666 +++++++ goobj/read_test.go | 28 + gosym/pclinetest.asm | 58 + gosym/pclinetest.h | 9 + gosym/pclntab.go | 453 +++++ gosym/pclntab_test.go | 274 +++ gosym/symtab.go | 710 +++++++ macho/fat.go | 146 ++ macho/file.go | 524 +++++ macho/file_test.go | 210 ++ macho/macho.go | 316 +++ macho/testdata/fat-gcc-386-amd64-darwin-exec | Bin 0 -> 28992 bytes macho/testdata/gcc-386-darwin-exec | Bin 0 -> 12588 bytes macho/testdata/gcc-amd64-darwin-exec | Bin 0 -> 8512 bytes macho/testdata/gcc-amd64-darwin-exec-debug | Bin 0 -> 4540 bytes macho/testdata/hello.c | 8 + pe/file.go | 390 ++++ pe/file_test.go | 243 +++ pe/pe.go | 134 ++ pe/testdata/gcc-386-mingw-exec | Bin 0 -> 29941 bytes pe/testdata/gcc-386-mingw-obj | Bin 0 -> 2372 bytes pe/testdata/gcc-amd64-mingw-exec | Bin 0 -> 273083 bytes pe/testdata/gcc-amd64-mingw-obj | Bin 0 -> 736 bytes pe/testdata/hello.c | 8 + plan9obj/file.go | 328 +++ plan9obj/file_test.go | 81 + plan9obj/plan9obj.go | 36 + plan9obj/testdata/386-plan9-exec | Bin 0 -> 37232 bytes plan9obj/testdata/amd64-plan9-exec | Bin 0 -> 34279 bytes plan9obj/testdata/hello.c | 8 + 57 files changed, 10854 insertions(+) create mode 100644 dwarf/buf.go create mode 100644 dwarf/const.go create mode 100644 dwarf/entry.go create mode 100644 dwarf/open.go create mode 100644 dwarf/testdata/typedef.c create mode 100755 dwarf/testdata/typedef.elf create mode 100644 dwarf/testdata/typedef.elf4 create mode 100644 dwarf/testdata/typedef.macho create mode 100644 dwarf/type.go create mode 100644 dwarf/type_test.go create mode 100644 dwarf/typeunit.go create mode 100644 dwarf/unit.go create mode 100644 elf/elf.go create mode 100644 elf/elf_test.go create mode 100644 elf/file.go create mode 100644 elf/file_test.go create mode 100644 elf/symbols_test.go create mode 100755 elf/testdata/gcc-386-freebsd-exec create mode 100755 elf/testdata/gcc-amd64-linux-exec create mode 100644 elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj create mode 100644 elf/testdata/go-relocation-test-clang-x86.obj create mode 100644 elf/testdata/go-relocation-test-gcc424-x86-64.obj create mode 100644 elf/testdata/go-relocation-test-gcc441-x86-64.obj create mode 100644 elf/testdata/go-relocation-test-gcc441-x86.obj create mode 100644 elf/testdata/go-relocation-test-gcc482-aarch64.obj create mode 100644 elf/testdata/hello-world-core.gz create mode 100644 elf/testdata/hello.c create mode 100644 goobj/read.go create mode 100644 goobj/read_test.go create mode 100644 gosym/pclinetest.asm create mode 100644 gosym/pclinetest.h create mode 100644 gosym/pclntab.go create mode 100644 gosym/pclntab_test.go create mode 100644 gosym/symtab.go create mode 100644 macho/fat.go create mode 100644 macho/file.go create mode 100644 macho/file_test.go create mode 100644 macho/macho.go create mode 100644 macho/testdata/fat-gcc-386-amd64-darwin-exec create mode 100755 macho/testdata/gcc-386-darwin-exec create mode 100755 macho/testdata/gcc-amd64-darwin-exec create mode 100644 macho/testdata/gcc-amd64-darwin-exec-debug create mode 100644 macho/testdata/hello.c create mode 100644 pe/file.go create mode 100644 pe/file_test.go create mode 100644 pe/pe.go create mode 100644 pe/testdata/gcc-386-mingw-exec create mode 100644 pe/testdata/gcc-386-mingw-obj create mode 100644 pe/testdata/gcc-amd64-mingw-exec create mode 100644 pe/testdata/gcc-amd64-mingw-obj create mode 100644 pe/testdata/hello.c create mode 100644 plan9obj/file.go create mode 100644 plan9obj/file_test.go create mode 100644 plan9obj/plan9obj.go create mode 100755 plan9obj/testdata/386-plan9-exec create mode 100755 plan9obj/testdata/amd64-plan9-exec create mode 100644 plan9obj/testdata/hello.c diff --git a/dwarf/buf.go b/dwarf/buf.go new file mode 100644 index 0000000..53c46eb --- /dev/null +++ b/dwarf/buf.go @@ -0,0 +1,181 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Buffered reading and decoding of DWARF data streams. + +package dwarf + +import ( + "encoding/binary" + "strconv" +) + +// Data buffer being decoded. +type buf struct { + dwarf *Data + order binary.ByteOrder + format dataFormat + name string + off Offset + data []byte + err error +} + +// Data format, other than byte order. This affects the handling of +// certain field formats. +type dataFormat interface { + // DWARF version number. Zero means unknown. + version() int + + // 64-bit DWARF format? + dwarf64() (dwarf64 bool, isKnown bool) + + // Size of an address, in bytes. Zero means unknown. + addrsize() int +} + +// Some parts of DWARF have no data format, e.g., abbrevs. +type unknownFormat struct{} + +func (u unknownFormat) version() int { + return 0 +} + +func (u unknownFormat) dwarf64() (bool, bool) { + return false, false +} + +func (u unknownFormat) addrsize() int { + return 0 +} + +func makeBuf(d *Data, format dataFormat, name string, off Offset, data []byte) buf { + return buf{d, d.order, format, name, off, data, nil} +} + +func (b *buf) uint8() uint8 { + if len(b.data) < 1 { + b.error("underflow") + return 0 + } + val := b.data[0] + b.data = b.data[1:] + b.off++ + return val +} + +func (b *buf) bytes(n int) []byte { + if len(b.data) < n { + b.error("underflow") + return nil + } + data := b.data[0:n] + b.data = b.data[n:] + b.off += Offset(n) + return data +} + +func (b *buf) skip(n int) { b.bytes(n) } + +func (b *buf) string() string { + for i := 0; i < len(b.data); i++ { + if b.data[i] == 0 { + s := string(b.data[0:i]) + b.data = b.data[i+1:] + b.off += Offset(i + 1) + return s + } + } + b.error("underflow") + return "" +} + +func (b *buf) uint16() uint16 { + a := b.bytes(2) + if a == nil { + return 0 + } + return b.order.Uint16(a) +} + +func (b *buf) uint32() uint32 { + a := b.bytes(4) + if a == nil { + return 0 + } + return b.order.Uint32(a) +} + +func (b *buf) uint64() uint64 { + a := b.bytes(8) + if a == nil { + return 0 + } + return b.order.Uint64(a) +} + +// Read a varint, which is 7 bits per byte, little endian. +// the 0x80 bit means read another byte. +func (b *buf) varint() (c uint64, bits uint) { + for i := 0; i < len(b.data); i++ { + byte := b.data[i] + c |= uint64(byte&0x7F) << bits + bits += 7 + if byte&0x80 == 0 { + b.off += Offset(i + 1) + b.data = b.data[i+1:] + return c, bits + } + } + return 0, 0 +} + +// Unsigned int is just a varint. +func (b *buf) uint() uint64 { + x, _ := b.varint() + return x +} + +// Signed int is a sign-extended varint. +func (b *buf) int() int64 { + ux, bits := b.varint() + x := int64(ux) + if x&(1<<(bits-1)) != 0 { + x |= -1 << bits + } + return x +} + +// Address-sized uint. +func (b *buf) addr() uint64 { + switch b.format.addrsize() { + case 1: + return uint64(b.uint8()) + case 2: + return uint64(b.uint16()) + case 4: + return uint64(b.uint32()) + case 8: + return uint64(b.uint64()) + } + b.error("unknown address size") + return 0 +} + +func (b *buf) error(s string) { + if b.err == nil { + b.data = nil + b.err = DecodeError{b.name, b.off, s} + } +} + +type DecodeError struct { + Name string + Offset Offset + Err string +} + +func (e DecodeError) Error() string { + return "decoding dwarf section " + e.Name + " at offset 0x" + strconv.FormatInt(int64(e.Offset), 16) + ": " + e.Err +} diff --git a/dwarf/const.go b/dwarf/const.go new file mode 100644 index 0000000..93c6888 --- /dev/null +++ b/dwarf/const.go @@ -0,0 +1,454 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Constants + +package dwarf + +import "strconv" + +// An Attr identifies the attribute type in a DWARF Entry's Field. +type Attr uint32 + +const ( + AttrSibling Attr = 0x01 + AttrLocation Attr = 0x02 + AttrName Attr = 0x03 + AttrOrdering Attr = 0x09 + AttrByteSize Attr = 0x0B + AttrBitOffset Attr = 0x0C + AttrBitSize Attr = 0x0D + AttrStmtList Attr = 0x10 + AttrLowpc Attr = 0x11 + AttrHighpc Attr = 0x12 + AttrLanguage Attr = 0x13 + AttrDiscr Attr = 0x15 + AttrDiscrValue Attr = 0x16 + AttrVisibility Attr = 0x17 + AttrImport Attr = 0x18 + AttrStringLength Attr = 0x19 + AttrCommonRef Attr = 0x1A + AttrCompDir Attr = 0x1B + AttrConstValue Attr = 0x1C + AttrContainingType Attr = 0x1D + AttrDefaultValue Attr = 0x1E + AttrInline Attr = 0x20 + AttrIsOptional Attr = 0x21 + AttrLowerBound Attr = 0x22 + AttrProducer Attr = 0x25 + AttrPrototyped Attr = 0x27 + AttrReturnAddr Attr = 0x2A + AttrStartScope Attr = 0x2C + AttrStrideSize Attr = 0x2E + AttrUpperBound Attr = 0x2F + AttrAbstractOrigin Attr = 0x31 + AttrAccessibility Attr = 0x32 + AttrAddrClass Attr = 0x33 + AttrArtificial Attr = 0x34 + AttrBaseTypes Attr = 0x35 + AttrCalling Attr = 0x36 + AttrCount Attr = 0x37 + AttrDataMemberLoc Attr = 0x38 + AttrDeclColumn Attr = 0x39 + AttrDeclFile Attr = 0x3A + AttrDeclLine Attr = 0x3B + AttrDeclaration Attr = 0x3C + AttrDiscrList Attr = 0x3D + AttrEncoding Attr = 0x3E + AttrExternal Attr = 0x3F + AttrFrameBase Attr = 0x40 + AttrFriend Attr = 0x41 + AttrIdentifierCase Attr = 0x42 + AttrMacroInfo Attr = 0x43 + AttrNamelistItem Attr = 0x44 + AttrPriority Attr = 0x45 + AttrSegment Attr = 0x46 + AttrSpecification Attr = 0x47 + AttrStaticLink Attr = 0x48 + AttrType Attr = 0x49 + AttrUseLocation Attr = 0x4A + AttrVarParam Attr = 0x4B + AttrVirtuality Attr = 0x4C + AttrVtableElemLoc Attr = 0x4D + AttrAllocated Attr = 0x4E + AttrAssociated Attr = 0x4F + AttrDataLocation Attr = 0x50 + AttrStride Attr = 0x51 + AttrEntrypc Attr = 0x52 + AttrUseUTF8 Attr = 0x53 + AttrExtension Attr = 0x54 + AttrRanges Attr = 0x55 + AttrTrampoline Attr = 0x56 + AttrCallColumn Attr = 0x57 + AttrCallFile Attr = 0x58 + AttrCallLine Attr = 0x59 + AttrDescription Attr = 0x5A +) + +var attrNames = [...]string{ + AttrSibling: "Sibling", + AttrLocation: "Location", + AttrName: "Name", + AttrOrdering: "Ordering", + AttrByteSize: "ByteSize", + AttrBitOffset: "BitOffset", + AttrBitSize: "BitSize", + AttrStmtList: "StmtList", + AttrLowpc: "Lowpc", + AttrHighpc: "Highpc", + AttrLanguage: "Language", + AttrDiscr: "Discr", + AttrDiscrValue: "DiscrValue", + AttrVisibility: "Visibility", + AttrImport: "Import", + AttrStringLength: "StringLength", + AttrCommonRef: "CommonRef", + AttrCompDir: "CompDir", + AttrConstValue: "ConstValue", + AttrContainingType: "ContainingType", + AttrDefaultValue: "DefaultValue", + AttrInline: "Inline", + AttrIsOptional: "IsOptional", + AttrLowerBound: "LowerBound", + AttrProducer: "Producer", + AttrPrototyped: "Prototyped", + AttrReturnAddr: "ReturnAddr", + AttrStartScope: "StartScope", + AttrStrideSize: "StrideSize", + AttrUpperBound: "UpperBound", + AttrAbstractOrigin: "AbstractOrigin", + AttrAccessibility: "Accessibility", + AttrAddrClass: "AddrClass", + AttrArtificial: "Artificial", + AttrBaseTypes: "BaseTypes", + AttrCalling: "Calling", + AttrCount: "Count", + AttrDataMemberLoc: "DataMemberLoc", + AttrDeclColumn: "DeclColumn", + AttrDeclFile: "DeclFile", + AttrDeclLine: "DeclLine", + AttrDeclaration: "Declaration", + AttrDiscrList: "DiscrList", + AttrEncoding: "Encoding", + AttrExternal: "External", + AttrFrameBase: "FrameBase", + AttrFriend: "Friend", + AttrIdentifierCase: "IdentifierCase", + AttrMacroInfo: "MacroInfo", + AttrNamelistItem: "NamelistItem", + AttrPriority: "Priority", + AttrSegment: "Segment", + AttrSpecification: "Specification", + AttrStaticLink: "StaticLink", + AttrType: "Type", + AttrUseLocation: "UseLocation", + AttrVarParam: "VarParam", + AttrVirtuality: "Virtuality", + AttrVtableElemLoc: "VtableElemLoc", + AttrAllocated: "Allocated", + AttrAssociated: "Associated", + AttrDataLocation: "DataLocation", + AttrStride: "Stride", + AttrEntrypc: "Entrypc", + AttrUseUTF8: "UseUTF8", + AttrExtension: "Extension", + AttrRanges: "Ranges", + AttrTrampoline: "Trampoline", + AttrCallColumn: "CallColumn", + AttrCallFile: "CallFile", + AttrCallLine: "CallLine", + AttrDescription: "Description", +} + +func (a Attr) String() string { + if int(a) < len(attrNames) { + s := attrNames[a] + if s != "" { + return s + } + } + return strconv.Itoa(int(a)) +} + +func (a Attr) GoString() string { + if int(a) < len(attrNames) { + s := attrNames[a] + if s != "" { + return "dwarf.Attr" + s + } + } + return "dwarf.Attr(" + strconv.FormatInt(int64(a), 10) + ")" +} + +// A format is a DWARF data encoding format. +type format uint32 + +const ( + // value formats + formAddr format = 0x01 + formDwarfBlock2 format = 0x03 + formDwarfBlock4 format = 0x04 + formData2 format = 0x05 + formData4 format = 0x06 + formData8 format = 0x07 + formString format = 0x08 + formDwarfBlock format = 0x09 + formDwarfBlock1 format = 0x0A + formData1 format = 0x0B + formFlag format = 0x0C + formSdata format = 0x0D + formStrp format = 0x0E + formUdata format = 0x0F + formRefAddr format = 0x10 + formRef1 format = 0x11 + formRef2 format = 0x12 + formRef4 format = 0x13 + formRef8 format = 0x14 + formRefUdata format = 0x15 + formIndirect format = 0x16 + // The following are new in DWARF 4. + formSecOffset format = 0x17 + formExprloc format = 0x18 + formFlagPresent format = 0x19 + formRefSig8 format = 0x20 + // Extensions for multi-file compression (.dwz) + // http://www.dwarfstd.org/ShowIssue.php?issue=120604.1 + formGnuRefAlt format = 0x1f20 + formGnuStrpAlt format = 0x1f21 +) + +// A Tag is the classification (the type) of an Entry. +type Tag uint32 + +const ( + TagArrayType Tag = 0x01 + TagClassType Tag = 0x02 + TagEntryPoint Tag = 0x03 + TagEnumerationType Tag = 0x04 + TagFormalParameter Tag = 0x05 + TagImportedDeclaration Tag = 0x08 + TagLabel Tag = 0x0A + TagLexDwarfBlock Tag = 0x0B + TagMember Tag = 0x0D + TagPointerType Tag = 0x0F + TagReferenceType Tag = 0x10 + TagCompileUnit Tag = 0x11 + TagStringType Tag = 0x12 + TagStructType Tag = 0x13 + TagSubroutineType Tag = 0x15 + TagTypedef Tag = 0x16 + TagUnionType Tag = 0x17 + TagUnspecifiedParameters Tag = 0x18 + TagVariant Tag = 0x19 + TagCommonDwarfBlock Tag = 0x1A + TagCommonInclusion Tag = 0x1B + TagInheritance Tag = 0x1C + TagInlinedSubroutine Tag = 0x1D + TagModule Tag = 0x1E + TagPtrToMemberType Tag = 0x1F + TagSetType Tag = 0x20 + TagSubrangeType Tag = 0x21 + TagWithStmt Tag = 0x22 + TagAccessDeclaration Tag = 0x23 + TagBaseType Tag = 0x24 + TagCatchDwarfBlock Tag = 0x25 + TagConstType Tag = 0x26 + TagConstant Tag = 0x27 + TagEnumerator Tag = 0x28 + TagFileType Tag = 0x29 + TagFriend Tag = 0x2A + TagNamelist Tag = 0x2B + TagNamelistItem Tag = 0x2C + TagPackedType Tag = 0x2D + TagSubprogram Tag = 0x2E + TagTemplateTypeParameter Tag = 0x2F + TagTemplateValueParameter Tag = 0x30 + TagThrownType Tag = 0x31 + TagTryDwarfBlock Tag = 0x32 + TagVariantPart Tag = 0x33 + TagVariable Tag = 0x34 + TagVolatileType Tag = 0x35 + // The following are new in DWARF 3. + TagDwarfProcedure Tag = 0x36 + TagRestrictType Tag = 0x37 + TagInterfaceType Tag = 0x38 + TagNamespace Tag = 0x39 + TagImportedModule Tag = 0x3A + TagUnspecifiedType Tag = 0x3B + TagPartialUnit Tag = 0x3C + TagImportedUnit Tag = 0x3D + TagMutableType Tag = 0x3E // Later removed from DWARF. + TagCondition Tag = 0x3F + TagSharedType Tag = 0x40 + // The following are new in DWARF 4. + TagTypeUnit Tag = 0x41 + TagRvalueReferenceType Tag = 0x42 + TagTemplateAlias Tag = 0x43 +) + +var tagNames = [...]string{ + TagArrayType: "ArrayType", + TagClassType: "ClassType", + TagEntryPoint: "EntryPoint", + TagEnumerationType: "EnumerationType", + TagFormalParameter: "FormalParameter", + TagImportedDeclaration: "ImportedDeclaration", + TagLabel: "Label", + TagLexDwarfBlock: "LexDwarfBlock", + TagMember: "Member", + TagPointerType: "PointerType", + TagReferenceType: "ReferenceType", + TagCompileUnit: "CompileUnit", + TagStringType: "StringType", + TagStructType: "StructType", + TagSubroutineType: "SubroutineType", + TagTypedef: "Typedef", + TagUnionType: "UnionType", + TagUnspecifiedParameters: "UnspecifiedParameters", + TagVariant: "Variant", + TagCommonDwarfBlock: "CommonDwarfBlock", + TagCommonInclusion: "CommonInclusion", + TagInheritance: "Inheritance", + TagInlinedSubroutine: "InlinedSubroutine", + TagModule: "Module", + TagPtrToMemberType: "PtrToMemberType", + TagSetType: "SetType", + TagSubrangeType: "SubrangeType", + TagWithStmt: "WithStmt", + TagAccessDeclaration: "AccessDeclaration", + TagBaseType: "BaseType", + TagCatchDwarfBlock: "CatchDwarfBlock", + TagConstType: "ConstType", + TagConstant: "Constant", + TagEnumerator: "Enumerator", + TagFileType: "FileType", + TagFriend: "Friend", + TagNamelist: "Namelist", + TagNamelistItem: "NamelistItem", + TagPackedType: "PackedType", + TagSubprogram: "Subprogram", + TagTemplateTypeParameter: "TemplateTypeParameter", + TagTemplateValueParameter: "TemplateValueParameter", + TagThrownType: "ThrownType", + TagTryDwarfBlock: "TryDwarfBlock", + TagVariantPart: "VariantPart", + TagVariable: "Variable", + TagVolatileType: "VolatileType", + TagDwarfProcedure: "DwarfProcedure", + TagRestrictType: "RestrictType", + TagInterfaceType: "InterfaceType", + TagNamespace: "Namespace", + TagImportedModule: "ImportedModule", + TagUnspecifiedType: "UnspecifiedType", + TagPartialUnit: "PartialUnit", + TagImportedUnit: "ImportedUnit", + TagMutableType: "MutableType", + TagCondition: "Condition", + TagSharedType: "SharedType", + TagTypeUnit: "TypeUnit", + TagRvalueReferenceType: "RvalueReferenceType", + TagTemplateAlias: "TemplateAlias", +} + +func (t Tag) String() string { + if int(t) < len(tagNames) { + s := tagNames[t] + if s != "" { + return s + } + } + return strconv.Itoa(int(t)) +} + +func (t Tag) GoString() string { + if int(t) < len(tagNames) { + s := tagNames[t] + if s != "" { + return "dwarf.Tag" + s + } + } + return "dwarf.Tag(" + strconv.FormatInt(int64(t), 10) + ")" +} + +// Location expression operators. +// The debug info encodes value locations like 8(R3) +// as a sequence of these op codes. +// This package does not implement full expressions; +// the opPlusUconst operator is expected by the type parser. +const ( + opAddr = 0x03 /* 1 op, const addr */ + opDeref = 0x06 + opConst1u = 0x08 /* 1 op, 1 byte const */ + opConst1s = 0x09 /* " signed */ + opConst2u = 0x0A /* 1 op, 2 byte const */ + opConst2s = 0x0B /* " signed */ + opConst4u = 0x0C /* 1 op, 4 byte const */ + opConst4s = 0x0D /* " signed */ + opConst8u = 0x0E /* 1 op, 8 byte const */ + opConst8s = 0x0F /* " signed */ + opConstu = 0x10 /* 1 op, LEB128 const */ + opConsts = 0x11 /* " signed */ + opDup = 0x12 + opDrop = 0x13 + opOver = 0x14 + opPick = 0x15 /* 1 op, 1 byte stack index */ + opSwap = 0x16 + opRot = 0x17 + opXderef = 0x18 + opAbs = 0x19 + opAnd = 0x1A + opDiv = 0x1B + opMinus = 0x1C + opMod = 0x1D + opMul = 0x1E + opNeg = 0x1F + opNot = 0x20 + opOr = 0x21 + opPlus = 0x22 + opPlusUconst = 0x23 /* 1 op, ULEB128 addend */ + opShl = 0x24 + opShr = 0x25 + opShra = 0x26 + opXor = 0x27 + opSkip = 0x2F /* 1 op, signed 2-byte constant */ + opBra = 0x28 /* 1 op, signed 2-byte constant */ + opEq = 0x29 + opGe = 0x2A + opGt = 0x2B + opLe = 0x2C + opLt = 0x2D + opNe = 0x2E + opLit0 = 0x30 + /* OpLitN = OpLit0 + N for N = 0..31 */ + opReg0 = 0x50 + /* OpRegN = OpReg0 + N for N = 0..31 */ + opBreg0 = 0x70 /* 1 op, signed LEB128 constant */ + /* OpBregN = OpBreg0 + N for N = 0..31 */ + opRegx = 0x90 /* 1 op, ULEB128 register */ + opFbreg = 0x91 /* 1 op, SLEB128 offset */ + opBregx = 0x92 /* 2 op, ULEB128 reg; SLEB128 off */ + opPiece = 0x93 /* 1 op, ULEB128 size of piece */ + opDerefSize = 0x94 /* 1-byte size of data retrieved */ + opXderefSize = 0x95 /* 1-byte size of data retrieved */ + opNop = 0x96 + /* next four new in Dwarf v3 */ + opPushObjAddr = 0x97 + opCall2 = 0x98 /* 2-byte offset of DIE */ + opCall4 = 0x99 /* 4-byte offset of DIE */ + opCallRef = 0x9A /* 4- or 8- byte offset of DIE */ + /* 0xE0-0xFF reserved for user-specific */ +) + +// Basic type encodings -- the value for AttrEncoding in a TagBaseType Entry. +const ( + encAddress = 0x01 + encBoolean = 0x02 + encComplexFloat = 0x03 + encFloat = 0x04 + encSigned = 0x05 + encSignedChar = 0x06 + encUnsigned = 0x07 + encUnsignedChar = 0x08 + encImaginaryFloat = 0x09 +) diff --git a/dwarf/entry.go b/dwarf/entry.go new file mode 100644 index 0000000..665c684 --- /dev/null +++ b/dwarf/entry.go @@ -0,0 +1,401 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// DWARF debug information entry parser. +// An entry is a sequence of data items of a given format. +// The first word in the entry is an index into what DWARF +// calls the ``abbreviation table.'' An abbreviation is really +// just a type descriptor: it's an array of attribute tag/value format pairs. + +package dwarf + +import ( + "errors" + "strconv" +) + +// a single entry's description: a sequence of attributes +type abbrev struct { + tag Tag + children bool + field []afield +} + +type afield struct { + attr Attr + fmt format +} + +// a map from entry format ids to their descriptions +type abbrevTable map[uint32]abbrev + +// ParseAbbrev returns the abbreviation table that starts at byte off +// in the .debug_abbrev section. +func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) { + if m, ok := d.abbrevCache[off]; ok { + return m, nil + } + + data := d.abbrev + if off > uint32(len(data)) { + data = nil + } else { + data = data[off:] + } + b := makeBuf(d, unknownFormat{}, "abbrev", 0, data) + + // Error handling is simplified by the buf getters + // returning an endless stream of 0s after an error. + m := make(abbrevTable) + for { + // Table ends with id == 0. + id := uint32(b.uint()) + if id == 0 { + break + } + + // Walk over attributes, counting. + n := 0 + b1 := b // Read from copy of b. + b1.uint() + b1.uint8() + for { + tag := b1.uint() + fmt := b1.uint() + if tag == 0 && fmt == 0 { + break + } + n++ + } + if b1.err != nil { + return nil, b1.err + } + + // Walk over attributes again, this time writing them down. + var a abbrev + a.tag = Tag(b.uint()) + a.children = b.uint8() != 0 + a.field = make([]afield, n) + for i := range a.field { + a.field[i].attr = Attr(b.uint()) + a.field[i].fmt = format(b.uint()) + } + b.uint() + b.uint() + + m[id] = a + } + if b.err != nil { + return nil, b.err + } + d.abbrevCache[off] = m + return m, nil +} + +// An entry is a sequence of attribute/value pairs. +type Entry struct { + Offset Offset // offset of Entry in DWARF info + Tag Tag // tag (kind of Entry) + Children bool // whether Entry is followed by children + Field []Field +} + +// A Field is a single attribute/value pair in an Entry. +type Field struct { + Attr Attr + Val interface{} +} + +// Val returns the value associated with attribute Attr in Entry, +// or nil if there is no such attribute. +// +// A common idiom is to merge the check for nil return with +// the check that the value has the expected dynamic type, as in: +// v, ok := e.Val(AttrSibling).(int64); +// +func (e *Entry) Val(a Attr) interface{} { + for _, f := range e.Field { + if f.Attr == a { + return f.Val + } + } + return nil +} + +// An Offset represents the location of an Entry within the DWARF info. +// (See Reader.Seek.) +type Offset uint32 + +// Entry reads a single entry from buf, decoding +// according to the given abbreviation table. +func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry { + off := b.off + id := uint32(b.uint()) + if id == 0 { + return &Entry{} + } + a, ok := atab[id] + if !ok { + b.error("unknown abbreviation table index") + return nil + } + e := &Entry{ + Offset: off, + Tag: a.tag, + Children: a.children, + Field: make([]Field, len(a.field)), + } + for i := range e.Field { + e.Field[i].Attr = a.field[i].attr + fmt := a.field[i].fmt + if fmt == formIndirect { + fmt = format(b.uint()) + } + var val interface{} + switch fmt { + default: + b.error("unknown entry attr format 0x" + strconv.FormatInt(int64(fmt), 16)) + + // address + case formAddr: + val = b.addr() + + // block + case formDwarfBlock1: + val = b.bytes(int(b.uint8())) + case formDwarfBlock2: + val = b.bytes(int(b.uint16())) + case formDwarfBlock4: + val = b.bytes(int(b.uint32())) + case formDwarfBlock: + val = b.bytes(int(b.uint())) + + // constant + case formData1: + val = int64(b.uint8()) + case formData2: + val = int64(b.uint16()) + case formData4: + val = int64(b.uint32()) + case formData8: + val = int64(b.uint64()) + case formSdata: + val = int64(b.int()) + case formUdata: + val = int64(b.uint()) + + // flag + case formFlag: + val = b.uint8() == 1 + // New in DWARF 4. + case formFlagPresent: + // The attribute is implicitly indicated as present, and no value is + // encoded in the debugging information entry itself. + val = true + + // reference to other entry + case formRefAddr: + vers := b.format.version() + if vers == 0 { + b.error("unknown version for DW_FORM_ref_addr") + } else if vers == 2 { + val = Offset(b.addr()) + } else { + is64, known := b.format.dwarf64() + if !known { + b.error("unknown size for DW_FORM_ref_addr") + } else if is64 { + val = Offset(b.uint64()) + } else { + val = Offset(b.uint32()) + } + } + case formRef1: + val = Offset(b.uint8()) + ubase + case formRef2: + val = Offset(b.uint16()) + ubase + case formRef4: + val = Offset(b.uint32()) + ubase + case formRef8: + val = Offset(b.uint64()) + ubase + case formRefUdata: + val = Offset(b.uint()) + ubase + + // string + case formString: + val = b.string() + case formStrp: + off := b.uint32() // offset into .debug_str + if b.err != nil { + return nil + } + b1 := makeBuf(b.dwarf, unknownFormat{}, "str", 0, b.dwarf.str) + b1.skip(int(off)) + val = b1.string() + if b1.err != nil { + b.err = b1.err + return nil + } + + // lineptr, loclistptr, macptr, rangelistptr + // New in DWARF 4, but clang can generate them with -gdwarf-2. + // Section reference, replacing use of formData4 and formData8. + case formSecOffset, formGnuRefAlt, formGnuStrpAlt: + is64, known := b.format.dwarf64() + if !known { + b.error("unknown size for form 0x" + strconv.FormatInt(int64(fmt), 16)) + } else if is64 { + val = int64(b.uint64()) + } else { + val = int64(b.uint32()) + } + + // exprloc + // New in DWARF 4. + case formExprloc: + val = b.bytes(int(b.uint())) + + // reference + // New in DWARF 4. + case formRefSig8: + // 64-bit type signature. + val = b.uint64() + } + e.Field[i].Val = val + } + if b.err != nil { + return nil + } + return e +} + +// A Reader allows reading Entry structures from a DWARF ``info'' section. +// The Entry structures are arranged in a tree. The Reader's Next function +// return successive entries from a pre-order traversal of the tree. +// If an entry has children, its Children field will be true, and the children +// follow, terminated by an Entry with Tag 0. +type Reader struct { + b buf + d *Data + err error + unit int + lastChildren bool // .Children of last entry returned by Next + lastSibling Offset // .Val(AttrSibling) of last entry returned by Next +} + +// Reader returns a new Reader for Data. +// The reader is positioned at byte offset 0 in the DWARF ``info'' section. +func (d *Data) Reader() *Reader { + r := &Reader{d: d} + r.Seek(0) + return r +} + +// Seek positions the Reader at offset off in the encoded entry stream. +// Offset 0 can be used to denote the first entry. +func (r *Reader) Seek(off Offset) { + d := r.d + r.err = nil + r.lastChildren = false + if off == 0 { + if len(d.unit) == 0 { + return + } + u := &d.unit[0] + r.unit = 0 + r.b = makeBuf(r.d, u, "info", u.off, u.data) + return + } + + // TODO(rsc): binary search (maybe a new package) + var i int + var u *unit + for i = range d.unit { + u = &d.unit[i] + if u.off <= off && off < u.off+Offset(len(u.data)) { + r.unit = i + r.b = makeBuf(r.d, u, "info", off, u.data[off-u.off:]) + return + } + } + r.err = errors.New("offset out of range") +} + +// maybeNextUnit advances to the next unit if this one is finished. +func (r *Reader) maybeNextUnit() { + for len(r.b.data) == 0 && r.unit+1 < len(r.d.unit) { + r.unit++ + u := &r.d.unit[r.unit] + r.b = makeBuf(r.d, u, "info", u.off, u.data) + } +} + +// Next reads the next entry from the encoded entry stream. +// It returns nil, nil when it reaches the end of the section. +// It returns an error if the current offset is invalid or the data at the +// offset cannot be decoded as a valid Entry. +func (r *Reader) Next() (*Entry, error) { + if r.err != nil { + return nil, r.err + } + r.maybeNextUnit() + if len(r.b.data) == 0 { + return nil, nil + } + u := &r.d.unit[r.unit] + e := r.b.entry(u.atable, u.base) + if r.b.err != nil { + r.err = r.b.err + return nil, r.err + } + if e != nil { + r.lastChildren = e.Children + if r.lastChildren { + r.lastSibling, _ = e.Val(AttrSibling).(Offset) + } + } else { + r.lastChildren = false + } + return e, nil +} + +// SkipChildren skips over the child entries associated with +// the last Entry returned by Next. If that Entry did not have +// children or Next has not been called, SkipChildren is a no-op. +func (r *Reader) SkipChildren() { + if r.err != nil || !r.lastChildren { + return + } + + // If the last entry had a sibling attribute, + // that attribute gives the offset of the next + // sibling, so we can avoid decoding the + // child subtrees. + if r.lastSibling >= r.b.off { + r.Seek(r.lastSibling) + return + } + + for { + e, err := r.Next() + if err != nil || e == nil || e.Tag == 0 { + break + } + if e.Children { + r.SkipChildren() + } + } +} + +// clone returns a copy of the reader. This is used by the typeReader +// interface. +func (r *Reader) clone() typeReader { + return r.d.Reader() +} + +// offset returns the current buffer offset. This is used by the +// typeReader interface. +func (r *Reader) offset() Offset { + return r.b.off +} diff --git a/dwarf/open.go b/dwarf/open.go new file mode 100644 index 0000000..c1b3f37 --- /dev/null +++ b/dwarf/open.go @@ -0,0 +1,87 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package dwarf provides access to DWARF debugging information loaded from +// executable files, as defined in the DWARF 2.0 Standard at +// http://dwarfstd.org/doc/dwarf-2.0.0.pdf +package dwarf + +import "encoding/binary" + +// Data represents the DWARF debugging information +// loaded from an executable file (for example, an ELF or Mach-O executable). +type Data struct { + // raw data + abbrev []byte + aranges []byte + frame []byte + info []byte + line []byte + pubnames []byte + ranges []byte + str []byte + + // parsed data + abbrevCache map[uint32]abbrevTable + order binary.ByteOrder + typeCache map[Offset]Type + typeSigs map[uint64]*typeUnit + unit []unit +} + +// New returns a new Data object initialized from the given parameters. +// Rather than calling this function directly, clients should typically use +// the DWARF method of the File type of the appropriate package debug/elf, +// debug/macho, or debug/pe. +// +// The []byte arguments are the data from the corresponding debug section +// in the object file; for example, for an ELF object, abbrev is the contents of +// the ".debug_abbrev" section. +func New(abbrev, aranges, frame, info, line, pubnames, ranges, str []byte) (*Data, error) { + d := &Data{ + abbrev: abbrev, + aranges: aranges, + frame: frame, + info: info, + line: line, + pubnames: pubnames, + ranges: ranges, + str: str, + abbrevCache: make(map[uint32]abbrevTable), + typeCache: make(map[Offset]Type), + typeSigs: make(map[uint64]*typeUnit), + } + + // Sniff .debug_info to figure out byte order. + // bytes 4:6 are the version, a tiny 16-bit number (1, 2, 3). + if len(d.info) < 6 { + return nil, DecodeError{"info", Offset(len(d.info)), "too short"} + } + x, y := d.info[4], d.info[5] + switch { + case x == 0 && y == 0: + return nil, DecodeError{"info", 4, "unsupported version 0"} + case x == 0: + d.order = binary.BigEndian + case y == 0: + d.order = binary.LittleEndian + default: + return nil, DecodeError{"info", 4, "cannot determine byte order"} + } + + u, err := d.parseUnits() + if err != nil { + return nil, err + } + d.unit = u + return d, nil +} + +// AddTypes will add one .debug_types section to the DWARF data. A +// typical object with DWARF version 4 debug info will have multiple +// .debug_types sections. The name is used for error reporting only, +// and serves to distinguish one .debug_types section from another. +func (d *Data) AddTypes(name string, types []byte) error { + return d.parseTypes(name, types) +} diff --git a/dwarf/testdata/typedef.c b/dwarf/testdata/typedef.c new file mode 100644 index 0000000..f05f015 --- /dev/null +++ b/dwarf/testdata/typedef.c @@ -0,0 +1,85 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Linux ELF: +gcc -gdwarf-2 -m64 -c typedef.c && gcc -gdwarf-2 -m64 -o typedef.elf typedef.o + +OS X Mach-O: +gcc -gdwarf-2 -m64 -c typedef.c -o typedef.macho +*/ +#include + +typedef volatile int* t_ptr_volatile_int; +typedef const char *t_ptr_const_char; +typedef long t_long; +typedef unsigned short t_ushort; +typedef int t_func_int_of_float_double(float, double); +typedef int (*t_ptr_func_int_of_float_double)(float, double); +typedef int (*t_ptr_func_int_of_float_complex)(float complex); +typedef int (*t_ptr_func_int_of_double_complex)(double complex); +typedef int (*t_ptr_func_int_of_long_double_complex)(long double complex); +typedef int *t_func_ptr_int_of_char_schar_uchar(char, signed char, unsigned char); +typedef void t_func_void_of_char(char); +typedef void t_func_void_of_void(void); +typedef void t_func_void_of_ptr_char_dots(char*, ...); +typedef struct my_struct { + volatile int vi; + char x : 1; + int y : 4; + int z[0]; + long long array[40]; + int zz[0]; +} t_my_struct; +typedef struct my_struct1 { + int zz [1]; +} t_my_struct1; +typedef union my_union { + volatile int vi; + char x : 1; + int y : 4; + long long array[40]; +} t_my_union; +typedef enum my_enum { + e1 = 1, + e2 = 2, + e3 = -5, + e4 = 1000000000000000LL, +} t_my_enum; + +typedef struct list t_my_list; +struct list { + short val; + t_my_list *next; +}; + +typedef struct tree { + struct tree *left, *right; + unsigned long long val; +} t_my_tree; + +t_ptr_volatile_int *a2; +t_ptr_const_char **a3a; +t_long *a4; +t_ushort *a5; +t_func_int_of_float_double *a6; +t_ptr_func_int_of_float_double *a7; +t_func_ptr_int_of_char_schar_uchar *a8; +t_func_void_of_char *a9; +t_func_void_of_void *a10; +t_func_void_of_ptr_char_dots *a11; +t_my_struct *a12; +t_my_struct1 *a12a; +t_my_union *a12b; +t_my_enum *a13; +t_my_list *a14; +t_my_tree *a15; +t_ptr_func_int_of_float_complex *a16; +t_ptr_func_int_of_double_complex *a17; +t_ptr_func_int_of_long_double_complex *a18; + +int main() +{ + return 0; +} diff --git a/dwarf/testdata/typedef.elf b/dwarf/testdata/typedef.elf new file mode 100755 index 0000000000000000000000000000000000000000..b2062d2c4bb828dcb229f12869f77fd5d9522278 GIT binary patch literal 12448 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
uyG6y&-61`(FbhA4 zP_*q1yIl_UXcHZFZ~rcYzNixQ+x}qy9_&D|Ucke@R7Cu(_#}=;4?h9Fku*&&;ym)w z;km)$%@2EDh&16c~%Zxd4jKXy{) z=PxK9Smz2T{u~!wFX#X$>gHTVbXc9veW3WBbY6vXd`38*1;xGISpn??jf0~8&dWN! zZ)*A#O~0sV5)@D$V#+2Pikdl1&ujW&PzMPH-(t|kPC%!d2il4EFCtlP!+7vIf?Q?0 zy-6N#l4qLacQwiPG|7ui^64gdxk*0PB!8eu#$4=*7jw5u=5MCPe0U8v%KSaoDC>1m z&p+ApeyB+%SMFFBo6Y5&=3-;H*lI5JnTy@!Vo$l)UM_EVbZPEbm$x_f8SW>zW5YCC XVn3r}TkK?sKkBd1!lL@t9;5#P8kMr% literal 0 HcmV?d00001 diff --git a/dwarf/type.go b/dwarf/type.go new file mode 100644 index 0000000..fa40b2b --- /dev/null +++ b/dwarf/type.go @@ -0,0 +1,685 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// DWARF type information structures. +// The format is heavily biased toward C, but for simplicity +// the String methods use a pseudo-Go syntax. + +package dwarf + +import "strconv" + +// A Type conventionally represents a pointer to any of the +// specific Type structures (CharType, StructType, etc.). +type Type interface { + Common() *CommonType + String() string + Size() int64 +} + +// A CommonType holds fields common to multiple types. +// If a field is not known or not applicable for a given type, +// the zero value is used. +type CommonType struct { + ByteSize int64 // size of value of this type, in bytes + Name string // name that can be used to refer to type +} + +func (c *CommonType) Common() *CommonType { return c } + +func (c *CommonType) Size() int64 { return c.ByteSize } + +// Basic types + +// A BasicType holds fields common to all basic types. +type BasicType struct { + CommonType + BitSize int64 + BitOffset int64 +} + +func (b *BasicType) Basic() *BasicType { return b } + +func (t *BasicType) String() string { + if t.Name != "" { + return t.Name + } + return "?" +} + +// A CharType represents a signed character type. +type CharType struct { + BasicType +} + +// A UcharType represents an unsigned character type. +type UcharType struct { + BasicType +} + +// An IntType represents a signed integer type. +type IntType struct { + BasicType +} + +// A UintType represents an unsigned integer type. +type UintType struct { + BasicType +} + +// A FloatType represents a floating point type. +type FloatType struct { + BasicType +} + +// A ComplexType represents a complex floating point type. +type ComplexType struct { + BasicType +} + +// A BoolType represents a boolean type. +type BoolType struct { + BasicType +} + +// An AddrType represents a machine address type. +type AddrType struct { + BasicType +} + +// An UnspecifiedType represents an implicit, unknown, ambiguous or nonexistent type. +type UnspecifiedType struct { + BasicType +} + +// qualifiers + +// A QualType represents a type that has the C/C++ "const", "restrict", or "volatile" qualifier. +type QualType struct { + CommonType + Qual string + Type Type +} + +func (t *QualType) String() string { return t.Qual + " " + t.Type.String() } + +func (t *QualType) Size() int64 { return t.Type.Size() } + +// An ArrayType represents a fixed size array type. +type ArrayType struct { + CommonType + Type Type + StrideBitSize int64 // if > 0, number of bits to hold each element + Count int64 // if == -1, an incomplete array, like char x[]. +} + +func (t *ArrayType) String() string { + return "[" + strconv.FormatInt(t.Count, 10) + "]" + t.Type.String() +} + +func (t *ArrayType) Size() int64 { + if t.Count == -1 { + return 0 + } + return t.Count * t.Type.Size() +} + +// A VoidType represents the C void type. +type VoidType struct { + CommonType +} + +func (t *VoidType) String() string { return "void" } + +// A PtrType represents a pointer type. +type PtrType struct { + CommonType + Type Type +} + +func (t *PtrType) String() string { return "*" + t.Type.String() } + +// A StructType represents a struct, union, or C++ class type. +type StructType struct { + CommonType + StructName string + Kind string // "struct", "union", or "class". + Field []*StructField + Incomplete bool // if true, struct, union, class is declared but not defined +} + +// A StructField represents a field in a struct, union, or C++ class type. +type StructField struct { + Name string + Type Type + ByteOffset int64 + ByteSize int64 + BitOffset int64 // within the ByteSize bytes at ByteOffset + BitSize int64 // zero if not a bit field +} + +func (t *StructType) String() string { + if t.StructName != "" { + return t.Kind + " " + t.StructName + } + return t.Defn() +} + +func (t *StructType) Defn() string { + s := t.Kind + if t.StructName != "" { + s += " " + t.StructName + } + if t.Incomplete { + s += " /*incomplete*/" + return s + } + s += " {" + for i, f := range t.Field { + if i > 0 { + s += "; " + } + s += f.Name + " " + f.Type.String() + s += "@" + strconv.FormatInt(f.ByteOffset, 10) + if f.BitSize > 0 { + s += " : " + strconv.FormatInt(f.BitSize, 10) + s += "@" + strconv.FormatInt(f.BitOffset, 10) + } + } + s += "}" + return s +} + +// An EnumType represents an enumerated type. +// The only indication of its native integer type is its ByteSize +// (inside CommonType). +type EnumType struct { + CommonType + EnumName string + Val []*EnumValue +} + +// An EnumValue represents a single enumeration value. +type EnumValue struct { + Name string + Val int64 +} + +func (t *EnumType) String() string { + s := "enum" + if t.EnumName != "" { + s += " " + t.EnumName + } + s += " {" + for i, v := range t.Val { + if i > 0 { + s += "; " + } + s += v.Name + "=" + strconv.FormatInt(v.Val, 10) + } + s += "}" + return s +} + +// A FuncType represents a function type. +type FuncType struct { + CommonType + ReturnType Type + ParamType []Type +} + +func (t *FuncType) String() string { + s := "func(" + for i, t := range t.ParamType { + if i > 0 { + s += ", " + } + s += t.String() + } + s += ")" + if t.ReturnType != nil { + s += " " + t.ReturnType.String() + } + return s +} + +// A DotDotDotType represents the variadic ... function parameter. +type DotDotDotType struct { + CommonType +} + +func (t *DotDotDotType) String() string { return "..." } + +// A TypedefType represents a named type. +type TypedefType struct { + CommonType + Type Type +} + +func (t *TypedefType) String() string { return t.Name } + +func (t *TypedefType) Size() int64 { return t.Type.Size() } + +// typeReader is used to read from either the info section or the +// types section. +type typeReader interface { + Seek(Offset) + Next() (*Entry, error) + clone() typeReader + offset() Offset +} + +// Type reads the type at off in the DWARF ``info'' section. +func (d *Data) Type(off Offset) (Type, error) { + return d.readType("info", d.Reader(), off, d.typeCache) +} + +// readType reads a type from r at off of name using and updating a +// type cache. +func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Offset]Type) (Type, error) { + if t, ok := typeCache[off]; ok { + return t, nil + } + r.Seek(off) + e, err := r.Next() + if err != nil { + return nil, err + } + if e == nil || e.Offset != off { + return nil, DecodeError{name, off, "no type at offset"} + } + + // Parse type from Entry. + // Must always set typeCache[off] before calling + // d.Type recursively, to handle circular types correctly. + var typ Type + + nextDepth := 0 + + // Get next child; set err if error happens. + next := func() *Entry { + if !e.Children { + return nil + } + // Only return direct children. + // Skip over composite entries that happen to be nested + // inside this one. Most DWARF generators wouldn't generate + // such a thing, but clang does. + // See golang.org/issue/6472. + for { + kid, err1 := r.Next() + if err1 != nil { + err = err1 + return nil + } + if kid == nil { + err = DecodeError{name, r.offset(), "unexpected end of DWARF entries"} + return nil + } + if kid.Tag == 0 { + if nextDepth > 0 { + nextDepth-- + continue + } + return nil + } + if kid.Children { + nextDepth++ + } + if nextDepth > 0 { + continue + } + return kid + } + } + + // Get Type referred to by Entry's AttrType field. + // Set err if error happens. Not having a type is an error. + typeOf := func(e *Entry) Type { + tval := e.Val(AttrType) + var t Type + switch toff := tval.(type) { + case Offset: + if t, err = d.readType(name, r.clone(), toff, typeCache); err != nil { + return nil + } + case uint64: + if t, err = d.sigToType(toff); err != nil { + return nil + } + default: + // It appears that no Type means "void". + return new(VoidType) + } + return t + } + + switch e.Tag { + case TagArrayType: + // Multi-dimensional array. (DWARF v2 §5.4) + // Attributes: + // AttrType:subtype [required] + // AttrStrideSize: size in bits of each element of the array + // AttrByteSize: size of entire array + // Children: + // TagSubrangeType or TagEnumerationType giving one dimension. + // dimensions are in left to right order. + t := new(ArrayType) + typ = t + typeCache[off] = t + if t.Type = typeOf(e); err != nil { + goto Error + } + t.StrideBitSize, _ = e.Val(AttrStrideSize).(int64) + + // Accumulate dimensions, + var dims []int64 + for kid := next(); kid != nil; kid = next() { + // TODO(rsc): Can also be TagEnumerationType + // but haven't seen that in the wild yet. + switch kid.Tag { + case TagSubrangeType: + count, ok := kid.Val(AttrCount).(int64) + if !ok { + // Old binaries may have an upper bound instead. + count, ok = kid.Val(AttrUpperBound).(int64) + if ok { + count++ // Length is one more than upper bound. + } else if len(dims) == 0 { + count = -1 // As in x[]. + } + } + dims = append(dims, count) + case TagEnumerationType: + err = DecodeError{name, kid.Offset, "cannot handle enumeration type as array bound"} + goto Error + } + } + if len(dims) == 0 { + // LLVM generates this for x[]. + dims = []int64{-1} + } + + t.Count = dims[0] + for i := len(dims) - 1; i >= 1; i-- { + t.Type = &ArrayType{Type: t.Type, Count: dims[i]} + } + + case TagBaseType: + // Basic type. (DWARF v2 §5.1) + // Attributes: + // AttrName: name of base type in programming language of the compilation unit [required] + // AttrEncoding: encoding value for type (encFloat etc) [required] + // AttrByteSize: size of type in bytes [required] + // AttrBitOffset: for sub-byte types, size in bits + // AttrBitSize: for sub-byte types, bit offset of high order bit in the AttrByteSize bytes + name, _ := e.Val(AttrName).(string) + enc, ok := e.Val(AttrEncoding).(int64) + if !ok { + err = DecodeError{name, e.Offset, "missing encoding attribute for " + name} + goto Error + } + switch enc { + default: + err = DecodeError{name, e.Offset, "unrecognized encoding attribute value"} + goto Error + + case encAddress: + typ = new(AddrType) + case encBoolean: + typ = new(BoolType) + case encComplexFloat: + typ = new(ComplexType) + case encFloat: + typ = new(FloatType) + case encSigned: + typ = new(IntType) + case encUnsigned: + typ = new(UintType) + case encSignedChar: + typ = new(CharType) + case encUnsignedChar: + typ = new(UcharType) + } + typeCache[off] = typ + t := typ.(interface { + Basic() *BasicType + }).Basic() + t.Name = name + t.BitSize, _ = e.Val(AttrBitSize).(int64) + t.BitOffset, _ = e.Val(AttrBitOffset).(int64) + + case TagClassType, TagStructType, TagUnionType: + // Structure, union, or class type. (DWARF v2 §5.5) + // Attributes: + // AttrName: name of struct, union, or class + // AttrByteSize: byte size [required] + // AttrDeclaration: if true, struct/union/class is incomplete + // Children: + // TagMember to describe one member. + // AttrName: name of member [required] + // AttrType: type of member [required] + // AttrByteSize: size in bytes + // AttrBitOffset: bit offset within bytes for bit fields + // AttrBitSize: bit size for bit fields + // AttrDataMemberLoc: location within struct [required for struct, class] + // There is much more to handle C++, all ignored for now. + t := new(StructType) + typ = t + typeCache[off] = t + switch e.Tag { + case TagClassType: + t.Kind = "class" + case TagStructType: + t.Kind = "struct" + case TagUnionType: + t.Kind = "union" + } + t.StructName, _ = e.Val(AttrName).(string) + t.Incomplete = e.Val(AttrDeclaration) != nil + t.Field = make([]*StructField, 0, 8) + var lastFieldType *Type + var lastFieldBitOffset int64 + for kid := next(); kid != nil; kid = next() { + if kid.Tag == TagMember { + f := new(StructField) + if f.Type = typeOf(kid); err != nil { + goto Error + } + switch loc := kid.Val(AttrDataMemberLoc).(type) { + case []byte: + // TODO: Should have original compilation + // unit here, not unknownFormat. + b := makeBuf(d, unknownFormat{}, "location", 0, loc) + if b.uint8() != opPlusUconst { + err = DecodeError{name, kid.Offset, "unexpected opcode"} + goto Error + } + f.ByteOffset = int64(b.uint()) + if b.err != nil { + err = b.err + goto Error + } + case int64: + f.ByteOffset = loc + } + + haveBitOffset := false + f.Name, _ = kid.Val(AttrName).(string) + f.ByteSize, _ = kid.Val(AttrByteSize).(int64) + f.BitOffset, haveBitOffset = kid.Val(AttrBitOffset).(int64) + f.BitSize, _ = kid.Val(AttrBitSize).(int64) + t.Field = append(t.Field, f) + + bito := f.BitOffset + if !haveBitOffset { + bito = f.ByteOffset * 8 + } + if bito == lastFieldBitOffset && t.Kind != "union" { + // Last field was zero width. Fix array length. + // (DWARF writes out 0-length arrays as if they were 1-length arrays.) + zeroArray(lastFieldType) + } + lastFieldType = &f.Type + lastFieldBitOffset = bito + } + } + if t.Kind != "union" { + b, ok := e.Val(AttrByteSize).(int64) + if ok && b*8 == lastFieldBitOffset { + // Final field must be zero width. Fix array length. + zeroArray(lastFieldType) + } + } + + case TagConstType, TagVolatileType, TagRestrictType: + // Type modifier (DWARF v2 §5.2) + // Attributes: + // AttrType: subtype + t := new(QualType) + typ = t + typeCache[off] = t + if t.Type = typeOf(e); err != nil { + goto Error + } + switch e.Tag { + case TagConstType: + t.Qual = "const" + case TagRestrictType: + t.Qual = "restrict" + case TagVolatileType: + t.Qual = "volatile" + } + + case TagEnumerationType: + // Enumeration type (DWARF v2 §5.6) + // Attributes: + // AttrName: enum name if any + // AttrByteSize: bytes required to represent largest value + // Children: + // TagEnumerator: + // AttrName: name of constant + // AttrConstValue: value of constant + t := new(EnumType) + typ = t + typeCache[off] = t + t.EnumName, _ = e.Val(AttrName).(string) + t.Val = make([]*EnumValue, 0, 8) + for kid := next(); kid != nil; kid = next() { + if kid.Tag == TagEnumerator { + f := new(EnumValue) + f.Name, _ = kid.Val(AttrName).(string) + f.Val, _ = kid.Val(AttrConstValue).(int64) + n := len(t.Val) + if n >= cap(t.Val) { + val := make([]*EnumValue, n, n*2) + copy(val, t.Val) + t.Val = val + } + t.Val = t.Val[0 : n+1] + t.Val[n] = f + } + } + + case TagPointerType: + // Type modifier (DWARF v2 §5.2) + // Attributes: + // AttrType: subtype [not required! void* has no AttrType] + // AttrAddrClass: address class [ignored] + t := new(PtrType) + typ = t + typeCache[off] = t + if e.Val(AttrType) == nil { + t.Type = &VoidType{} + break + } + t.Type = typeOf(e) + + case TagSubroutineType: + // Subroutine type. (DWARF v2 §5.7) + // Attributes: + // AttrType: type of return value if any + // AttrName: possible name of type [ignored] + // AttrPrototyped: whether used ANSI C prototype [ignored] + // Children: + // TagFormalParameter: typed parameter + // AttrType: type of parameter + // TagUnspecifiedParameter: final ... + t := new(FuncType) + typ = t + typeCache[off] = t + if t.ReturnType = typeOf(e); err != nil { + goto Error + } + t.ParamType = make([]Type, 0, 8) + for kid := next(); kid != nil; kid = next() { + var tkid Type + switch kid.Tag { + default: + continue + case TagFormalParameter: + if tkid = typeOf(kid); err != nil { + goto Error + } + case TagUnspecifiedParameters: + tkid = &DotDotDotType{} + } + t.ParamType = append(t.ParamType, tkid) + } + + case TagTypedef: + // Typedef (DWARF v2 §5.3) + // Attributes: + // AttrName: name [required] + // AttrType: type definition [required] + t := new(TypedefType) + typ = t + typeCache[off] = t + t.Name, _ = e.Val(AttrName).(string) + t.Type = typeOf(e) + + case TagUnspecifiedType: + // Unspecified type (DWARF v3 §5.2) + // Attributes: + // AttrName: name + t := new(UnspecifiedType) + typ = t + typeCache[off] = t + t.Name, _ = e.Val(AttrName).(string) + } + + if err != nil { + goto Error + } + + { + b, ok := e.Val(AttrByteSize).(int64) + if !ok { + b = -1 + } + typ.Common().ByteSize = b + } + return typ, nil + +Error: + // If the parse fails, take the type out of the cache + // so that the next call with this offset doesn't hit + // the cache and return success. + delete(typeCache, off) + return nil, err +} + +func zeroArray(t *Type) { + if t == nil { + return + } + at, ok := (*t).(*ArrayType) + if !ok || at.Type.Size() == 0 { + return + } + // Make a copy to avoid invalidating typeCache. + tt := *at + tt.Count = 0 + *t = &tt +} diff --git a/dwarf/type_test.go b/dwarf/type_test.go new file mode 100644 index 0000000..2cb85e7 --- /dev/null +++ b/dwarf/type_test.go @@ -0,0 +1,122 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package dwarf_test + +import ( + . "debug/dwarf" + "debug/elf" + "debug/macho" + "testing" +) + +var typedefTests = map[string]string{ + "t_ptr_volatile_int": "*volatile int", + "t_ptr_const_char": "*const char", + "t_long": "long int", + "t_ushort": "short unsigned int", + "t_func_int_of_float_double": "func(float, double) int", + "t_ptr_func_int_of_float_double": "*func(float, double) int", + "t_ptr_func_int_of_float_complex": "*func(complex float) int", + "t_ptr_func_int_of_double_complex": "*func(complex double) int", + "t_ptr_func_int_of_long_double_complex": "*func(complex long double) int", + "t_func_ptr_int_of_char_schar_uchar": "func(char, signed char, unsigned char) *int", + "t_func_void_of_char": "func(char) void", + "t_func_void_of_void": "func() void", + "t_func_void_of_ptr_char_dots": "func(*char, ...) void", + "t_my_struct": "struct my_struct {vi volatile int@0; x char@4 : 1@7; y int@4 : 4@27; z [0]int@8; array [40]long long int@8; zz [0]int@328}", + "t_my_struct1": "struct my_struct1 {zz [1]int@0}", + "t_my_union": "union my_union {vi volatile int@0; x char@0 : 1@7; y int@0 : 4@28; array [40]long long int@0}", + "t_my_enum": "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}", + "t_my_list": "struct list {val short int@0; next *t_my_list@8}", + "t_my_tree": "struct tree {left *struct tree@0; right *struct tree@8; val long long unsigned int@16}", +} + +// As Apple converts gcc to a clang-based front end +// they keep breaking the DWARF output. This map lists the +// conversion from real answer to Apple answer. +var machoBug = map[string]string{ + "func(*char, ...) void": "func(*char) void", + "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}": "enum my_enum {e1=1; e2=2; e3=-5; e4=-1530494976}", +} + +func elfData(t *testing.T, name string) *Data { + f, err := elf.Open(name) + if err != nil { + t.Fatal(err) + } + + d, err := f.DWARF() + if err != nil { + t.Fatal(err) + } + return d +} + +func machoData(t *testing.T, name string) *Data { + f, err := macho.Open(name) + if err != nil { + t.Fatal(err) + } + + d, err := f.DWARF() + if err != nil { + t.Fatal(err) + } + return d +} + +func TestTypedefsELF(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf"), "elf") } + +func TestTypedefsMachO(t *testing.T) { + testTypedefs(t, machoData(t, "testdata/typedef.macho"), "macho") +} + +func TestTypedefsELFDwarf4(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf4"), "elf") } + +func testTypedefs(t *testing.T, d *Data, kind string) { + r := d.Reader() + seen := make(map[string]bool) + for { + e, err := r.Next() + if err != nil { + t.Fatal("r.Next:", err) + } + if e == nil { + break + } + if e.Tag == TagTypedef { + typ, err := d.Type(e.Offset) + if err != nil { + t.Fatal("d.Type:", err) + } + t1 := typ.(*TypedefType) + var typstr string + if ts, ok := t1.Type.(*StructType); ok { + typstr = ts.Defn() + } else { + typstr = t1.Type.String() + } + + if want, ok := typedefTests[t1.Name]; ok { + if seen[t1.Name] { + t.Errorf("multiple definitions for %s", t1.Name) + } + seen[t1.Name] = true + if typstr != want && (kind != "macho" || typstr != machoBug[want]) { + t.Errorf("%s:\n\thave %s\n\twant %s", t1.Name, typstr, want) + } + } + } + if e.Tag != TagCompileUnit { + r.SkipChildren() + } + } + + for k := range typedefTests { + if !seen[k] { + t.Errorf("missing %s", k) + } + } +} diff --git a/dwarf/typeunit.go b/dwarf/typeunit.go new file mode 100644 index 0000000..3fd1c99 --- /dev/null +++ b/dwarf/typeunit.go @@ -0,0 +1,166 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package dwarf + +import ( + "fmt" + "strconv" +) + +// Parse the type units stored in a DWARF4 .debug_types section. Each +// type unit defines a single primary type and an 8-byte signature. +// Other sections may then use formRefSig8 to refer to the type. + +// The typeUnit format is a single type with a signature. It holds +// the same data as a compilation unit. +type typeUnit struct { + unit + toff Offset // Offset to signature type within data. + name string // Name of .debug_type section. + cache Type // Cache the type, nil to start. +} + +// Parse a .debug_types section. +func (d *Data) parseTypes(name string, types []byte) error { + b := makeBuf(d, unknownFormat{}, name, 0, types) + for len(b.data) > 0 { + base := b.off + dwarf64 := false + n := b.uint32() + if n == 0xffffffff { + n64 := b.uint64() + if n64 != uint64(uint32(n64)) { + b.error("type unit length overflow") + return b.err + } + n = uint32(n64) + dwarf64 = true + } + hdroff := b.off + vers := b.uint16() + if vers != 4 { + b.error("unsupported DWARF version " + strconv.Itoa(int(vers))) + return b.err + } + var ao uint32 + if !dwarf64 { + ao = b.uint32() + } else { + ao64 := b.uint64() + if ao64 != uint64(uint32(ao64)) { + b.error("type unit abbrev offset overflow") + return b.err + } + ao = uint32(ao64) + } + atable, err := d.parseAbbrev(ao) + if err != nil { + return err + } + asize := b.uint8() + sig := b.uint64() + + var toff uint32 + if !dwarf64 { + toff = b.uint32() + } else { + to64 := b.uint64() + if to64 != uint64(uint32(to64)) { + b.error("type unit type offset overflow") + return b.err + } + toff = uint32(to64) + } + + boff := b.off + d.typeSigs[sig] = &typeUnit{ + unit: unit{ + base: base, + off: boff, + data: b.bytes(int(Offset(n) - (b.off - hdroff))), + atable: atable, + asize: int(asize), + vers: int(vers), + is64: dwarf64, + }, + toff: Offset(toff), + name: name, + } + if b.err != nil { + return b.err + } + } + return nil +} + +// Return the type for a type signature. +func (d *Data) sigToType(sig uint64) (Type, error) { + tu := d.typeSigs[sig] + if tu == nil { + return nil, fmt.Errorf("no type unit with signature %v", sig) + } + if tu.cache != nil { + return tu.cache, nil + } + + b := makeBuf(d, tu, tu.name, tu.off, tu.data) + r := &typeUnitReader{d: d, tu: tu, b: b} + t, err := d.readType(tu.name, r, Offset(tu.toff), make(map[Offset]Type)) + if err != nil { + return nil, err + } + + tu.cache = t + return t, nil +} + +// typeUnitReader is a typeReader for a tagTypeUnit. +type typeUnitReader struct { + d *Data + tu *typeUnit + b buf + err error +} + +// Seek to a new position in the type unit. +func (tur *typeUnitReader) Seek(off Offset) { + tur.err = nil + doff := off - tur.tu.off + if doff < 0 || doff >= Offset(len(tur.tu.data)) { + tur.err = fmt.Errorf("%s: offset %d out of range; max %d", tur.tu.name, doff, len(tur.tu.data)) + return + } + tur.b = makeBuf(tur.d, tur.tu, tur.tu.name, off, tur.tu.data[doff:]) +} + +// Next reads the next Entry from the type unit. +func (tur *typeUnitReader) Next() (*Entry, error) { + if tur.err != nil { + return nil, tur.err + } + if len(tur.tu.data) == 0 { + return nil, nil + } + e := tur.b.entry(tur.tu.atable, tur.tu.base) + if tur.b.err != nil { + tur.err = tur.b.err + return nil, tur.err + } + return e, nil +} + +// clone returns a new reader for the type unit. +func (tur *typeUnitReader) clone() typeReader { + return &typeUnitReader{ + d: tur.d, + tu: tur.tu, + b: makeBuf(tur.d, tur.tu, tur.tu.name, tur.tu.off, tur.tu.data), + } +} + +// offset returns the current offset. +func (tur *typeUnitReader) offset() Offset { + return tur.b.off +} diff --git a/dwarf/unit.go b/dwarf/unit.go new file mode 100644 index 0000000..0fbc8e0 --- /dev/null +++ b/dwarf/unit.go @@ -0,0 +1,90 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package dwarf + +import "strconv" + +// DWARF debug info is split into a sequence of compilation units. +// Each unit has its own abbreviation table and address size. + +type unit struct { + base Offset // byte offset of header within the aggregate info + off Offset // byte offset of data within the aggregate info + data []byte + atable abbrevTable + asize int + vers int + is64 bool // True for 64-bit DWARF format +} + +// Implement the dataFormat interface. + +func (u *unit) version() int { + return u.vers +} + +func (u *unit) dwarf64() (bool, bool) { + return u.is64, true +} + +func (u *unit) addrsize() int { + return u.asize +} + +func (d *Data) parseUnits() ([]unit, error) { + // Count units. + nunit := 0 + b := makeBuf(d, unknownFormat{}, "info", 0, d.info) + for len(b.data) > 0 { + len := b.uint32() + if len == 0xffffffff { + len64 := b.uint64() + if len64 != uint64(uint32(len64)) { + b.error("unit length overflow") + break + } + len = uint32(len64) + } + b.skip(int(len)) + nunit++ + } + if b.err != nil { + return nil, b.err + } + + // Again, this time writing them down. + b = makeBuf(d, unknownFormat{}, "info", 0, d.info) + units := make([]unit, nunit) + for i := range units { + u := &units[i] + u.base = b.off + n := b.uint32() + if n == 0xffffffff { + u.is64 = true + n = uint32(b.uint64()) + } + vers := b.uint16() + if vers != 2 && vers != 3 && vers != 4 { + b.error("unsupported DWARF version " + strconv.Itoa(int(vers))) + break + } + u.vers = int(vers) + atable, err := d.parseAbbrev(b.uint32()) + if err != nil { + if b.err == nil { + b.err = err + } + break + } + u.atable = atable + u.asize = int(b.uint8()) + u.off = b.off + u.data = b.bytes(int(n - (2 + 4 + 1))) + } + if b.err != nil { + return nil, b.err + } + return units, nil +} diff --git a/elf/elf.go b/elf/elf.go new file mode 100644 index 0000000..cde296c --- /dev/null +++ b/elf/elf.go @@ -0,0 +1,1773 @@ +/* + * ELF constants and data structures + * + * Derived from: + * $FreeBSD: src/sys/sys/elf32.h,v 1.8.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf64.h,v 1.10.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf_common.h,v 1.15.8.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/alpha/include/elf.h,v 1.14 2003/09/25 01:10:22 peter Exp $ + * $FreeBSD: src/sys/amd64/include/elf.h,v 1.18 2004/08/03 08:21:48 dfr Exp $ + * $FreeBSD: src/sys/arm/include/elf.h,v 1.5.2.1 2006/06/30 21:42:52 cognet Exp $ + * $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $ + * $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $ + * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $ + * "ELF for the ARM® 64-bit Architecture (AArch64)" (ARM IHI 0056B) + * + * Copyright (c) 1996-1998 John D. Polstra. All rights reserved. + * Copyright (c) 2001 David E. O'Brien + * Portions Copyright 2009 The Go Authors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package elf + +import "strconv" + +/* + * Constants + */ + +// Indexes into the Header.Ident array. +const ( + EI_CLASS = 4 /* Class of machine. */ + EI_DATA = 5 /* Data format. */ + EI_VERSION = 6 /* ELF format version. */ + EI_OSABI = 7 /* Operating system / ABI identification */ + EI_ABIVERSION = 8 /* ABI version */ + EI_PAD = 9 /* Start of padding (per SVR4 ABI). */ + EI_NIDENT = 16 /* Size of e_ident array. */ +) + +// Initial magic number for ELF files. +const ELFMAG = "\177ELF" + +// Version is found in Header.Ident[EI_VERSION] and Header.Version. +type Version byte + +const ( + EV_NONE Version = 0 + EV_CURRENT Version = 1 +) + +var versionStrings = []intName{ + {0, "EV_NONE"}, + {1, "EV_CURRENT"}, +} + +func (i Version) String() string { return stringName(uint32(i), versionStrings, false) } +func (i Version) GoString() string { return stringName(uint32(i), versionStrings, true) } + +// Class is found in Header.Ident[EI_CLASS] and Header.Class. +type Class byte + +const ( + ELFCLASSNONE Class = 0 /* Unknown class. */ + ELFCLASS32 Class = 1 /* 32-bit architecture. */ + ELFCLASS64 Class = 2 /* 64-bit architecture. */ +) + +var classStrings = []intName{ + {0, "ELFCLASSNONE"}, + {1, "ELFCLASS32"}, + {2, "ELFCLASS64"}, +} + +func (i Class) String() string { return stringName(uint32(i), classStrings, false) } +func (i Class) GoString() string { return stringName(uint32(i), classStrings, true) } + +// Data is found in Header.Ident[EI_DATA] and Header.Data. +type Data byte + +const ( + ELFDATANONE Data = 0 /* Unknown data format. */ + ELFDATA2LSB Data = 1 /* 2's complement little-endian. */ + ELFDATA2MSB Data = 2 /* 2's complement big-endian. */ +) + +var dataStrings = []intName{ + {0, "ELFDATANONE"}, + {1, "ELFDATA2LSB"}, + {2, "ELFDATA2MSB"}, +} + +func (i Data) String() string { return stringName(uint32(i), dataStrings, false) } +func (i Data) GoString() string { return stringName(uint32(i), dataStrings, true) } + +// OSABI is found in Header.Ident[EI_OSABI] and Header.OSABI. +type OSABI byte + +const ( + ELFOSABI_NONE OSABI = 0 /* UNIX System V ABI */ + ELFOSABI_HPUX OSABI = 1 /* HP-UX operating system */ + ELFOSABI_NETBSD OSABI = 2 /* NetBSD */ + ELFOSABI_LINUX OSABI = 3 /* GNU/Linux */ + ELFOSABI_HURD OSABI = 4 /* GNU/Hurd */ + ELFOSABI_86OPEN OSABI = 5 /* 86Open common IA32 ABI */ + ELFOSABI_SOLARIS OSABI = 6 /* Solaris */ + ELFOSABI_AIX OSABI = 7 /* AIX */ + ELFOSABI_IRIX OSABI = 8 /* IRIX */ + ELFOSABI_FREEBSD OSABI = 9 /* FreeBSD */ + ELFOSABI_TRU64 OSABI = 10 /* TRU64 UNIX */ + ELFOSABI_MODESTO OSABI = 11 /* Novell Modesto */ + ELFOSABI_OPENBSD OSABI = 12 /* OpenBSD */ + ELFOSABI_OPENVMS OSABI = 13 /* Open VMS */ + ELFOSABI_NSK OSABI = 14 /* HP Non-Stop Kernel */ + ELFOSABI_ARM OSABI = 97 /* ARM */ + ELFOSABI_STANDALONE OSABI = 255 /* Standalone (embedded) application */ +) + +var osabiStrings = []intName{ + {0, "ELFOSABI_NONE"}, + {1, "ELFOSABI_HPUX"}, + {2, "ELFOSABI_NETBSD"}, + {3, "ELFOSABI_LINUX"}, + {4, "ELFOSABI_HURD"}, + {5, "ELFOSABI_86OPEN"}, + {6, "ELFOSABI_SOLARIS"}, + {7, "ELFOSABI_AIX"}, + {8, "ELFOSABI_IRIX"}, + {9, "ELFOSABI_FREEBSD"}, + {10, "ELFOSABI_TRU64"}, + {11, "ELFOSABI_MODESTO"}, + {12, "ELFOSABI_OPENBSD"}, + {13, "ELFOSABI_OPENVMS"}, + {14, "ELFOSABI_NSK"}, + {97, "ELFOSABI_ARM"}, + {255, "ELFOSABI_STANDALONE"}, +} + +func (i OSABI) String() string { return stringName(uint32(i), osabiStrings, false) } +func (i OSABI) GoString() string { return stringName(uint32(i), osabiStrings, true) } + +// Type is found in Header.Type. +type Type uint16 + +const ( + ET_NONE Type = 0 /* Unknown type. */ + ET_REL Type = 1 /* Relocatable. */ + ET_EXEC Type = 2 /* Executable. */ + ET_DYN Type = 3 /* Shared object. */ + ET_CORE Type = 4 /* Core file. */ + ET_LOOS Type = 0xfe00 /* First operating system specific. */ + ET_HIOS Type = 0xfeff /* Last operating system-specific. */ + ET_LOPROC Type = 0xff00 /* First processor-specific. */ + ET_HIPROC Type = 0xffff /* Last processor-specific. */ +) + +var typeStrings = []intName{ + {0, "ET_NONE"}, + {1, "ET_REL"}, + {2, "ET_EXEC"}, + {3, "ET_DYN"}, + {4, "ET_CORE"}, + {0xfe00, "ET_LOOS"}, + {0xfeff, "ET_HIOS"}, + {0xff00, "ET_LOPROC"}, + {0xffff, "ET_HIPROC"}, +} + +func (i Type) String() string { return stringName(uint32(i), typeStrings, false) } +func (i Type) GoString() string { return stringName(uint32(i), typeStrings, true) } + +// Machine is found in Header.Machine. +type Machine uint16 + +const ( + EM_NONE Machine = 0 /* Unknown machine. */ + EM_M32 Machine = 1 /* AT&T WE32100. */ + EM_SPARC Machine = 2 /* Sun SPARC. */ + EM_386 Machine = 3 /* Intel i386. */ + EM_68K Machine = 4 /* Motorola 68000. */ + EM_88K Machine = 5 /* Motorola 88000. */ + EM_860 Machine = 7 /* Intel i860. */ + EM_MIPS Machine = 8 /* MIPS R3000 Big-Endian only. */ + EM_S370 Machine = 9 /* IBM System/370. */ + EM_MIPS_RS3_LE Machine = 10 /* MIPS R3000 Little-Endian. */ + EM_PARISC Machine = 15 /* HP PA-RISC. */ + EM_VPP500 Machine = 17 /* Fujitsu VPP500. */ + EM_SPARC32PLUS Machine = 18 /* SPARC v8plus. */ + EM_960 Machine = 19 /* Intel 80960. */ + EM_PPC Machine = 20 /* PowerPC 32-bit. */ + EM_PPC64 Machine = 21 /* PowerPC 64-bit. */ + EM_S390 Machine = 22 /* IBM System/390. */ + EM_V800 Machine = 36 /* NEC V800. */ + EM_FR20 Machine = 37 /* Fujitsu FR20. */ + EM_RH32 Machine = 38 /* TRW RH-32. */ + EM_RCE Machine = 39 /* Motorola RCE. */ + EM_ARM Machine = 40 /* ARM. */ + EM_SH Machine = 42 /* Hitachi SH. */ + EM_SPARCV9 Machine = 43 /* SPARC v9 64-bit. */ + EM_TRICORE Machine = 44 /* Siemens TriCore embedded processor. */ + EM_ARC Machine = 45 /* Argonaut RISC Core. */ + EM_H8_300 Machine = 46 /* Hitachi H8/300. */ + EM_H8_300H Machine = 47 /* Hitachi H8/300H. */ + EM_H8S Machine = 48 /* Hitachi H8S. */ + EM_H8_500 Machine = 49 /* Hitachi H8/500. */ + EM_IA_64 Machine = 50 /* Intel IA-64 Processor. */ + EM_MIPS_X Machine = 51 /* Stanford MIPS-X. */ + EM_COLDFIRE Machine = 52 /* Motorola ColdFire. */ + EM_68HC12 Machine = 53 /* Motorola M68HC12. */ + EM_MMA Machine = 54 /* Fujitsu MMA. */ + EM_PCP Machine = 55 /* Siemens PCP. */ + EM_NCPU Machine = 56 /* Sony nCPU. */ + EM_NDR1 Machine = 57 /* Denso NDR1 microprocessor. */ + EM_STARCORE Machine = 58 /* Motorola Star*Core processor. */ + EM_ME16 Machine = 59 /* Toyota ME16 processor. */ + EM_ST100 Machine = 60 /* STMicroelectronics ST100 processor. */ + EM_TINYJ Machine = 61 /* Advanced Logic Corp. TinyJ processor. */ + EM_X86_64 Machine = 62 /* Advanced Micro Devices x86-64 */ + EM_AARCH64 Machine = 183 /* ARM 64-bit Architecture (AArch64) */ + + /* Non-standard or deprecated. */ + EM_486 Machine = 6 /* Intel i486. */ + EM_MIPS_RS4_BE Machine = 10 /* MIPS R4000 Big-Endian */ + EM_ALPHA_STD Machine = 41 /* Digital Alpha (standard value). */ + EM_ALPHA Machine = 0x9026 /* Alpha (written in the absence of an ABI) */ +) + +var machineStrings = []intName{ + {0, "EM_NONE"}, + {1, "EM_M32"}, + {2, "EM_SPARC"}, + {3, "EM_386"}, + {4, "EM_68K"}, + {5, "EM_88K"}, + {7, "EM_860"}, + {8, "EM_MIPS"}, + {9, "EM_S370"}, + {10, "EM_MIPS_RS3_LE"}, + {15, "EM_PARISC"}, + {17, "EM_VPP500"}, + {18, "EM_SPARC32PLUS"}, + {19, "EM_960"}, + {20, "EM_PPC"}, + {21, "EM_PPC64"}, + {22, "EM_S390"}, + {36, "EM_V800"}, + {37, "EM_FR20"}, + {38, "EM_RH32"}, + {39, "EM_RCE"}, + {40, "EM_ARM"}, + {42, "EM_SH"}, + {43, "EM_SPARCV9"}, + {44, "EM_TRICORE"}, + {45, "EM_ARC"}, + {46, "EM_H8_300"}, + {47, "EM_H8_300H"}, + {48, "EM_H8S"}, + {49, "EM_H8_500"}, + {50, "EM_IA_64"}, + {51, "EM_MIPS_X"}, + {52, "EM_COLDFIRE"}, + {53, "EM_68HC12"}, + {54, "EM_MMA"}, + {55, "EM_PCP"}, + {56, "EM_NCPU"}, + {57, "EM_NDR1"}, + {58, "EM_STARCORE"}, + {59, "EM_ME16"}, + {60, "EM_ST100"}, + {61, "EM_TINYJ"}, + {62, "EM_X86_64"}, + + /* Non-standard or deprecated. */ + {6, "EM_486"}, + {10, "EM_MIPS_RS4_BE"}, + {41, "EM_ALPHA_STD"}, + {0x9026, "EM_ALPHA"}, +} + +func (i Machine) String() string { return stringName(uint32(i), machineStrings, false) } +func (i Machine) GoString() string { return stringName(uint32(i), machineStrings, true) } + +// Special section indices. +type SectionIndex int + +const ( + SHN_UNDEF SectionIndex = 0 /* Undefined, missing, irrelevant. */ + SHN_LORESERVE SectionIndex = 0xff00 /* First of reserved range. */ + SHN_LOPROC SectionIndex = 0xff00 /* First processor-specific. */ + SHN_HIPROC SectionIndex = 0xff1f /* Last processor-specific. */ + SHN_LOOS SectionIndex = 0xff20 /* First operating system-specific. */ + SHN_HIOS SectionIndex = 0xff3f /* Last operating system-specific. */ + SHN_ABS SectionIndex = 0xfff1 /* Absolute values. */ + SHN_COMMON SectionIndex = 0xfff2 /* Common data. */ + SHN_XINDEX SectionIndex = 0xffff /* Escape -- index stored elsewhere. */ + SHN_HIRESERVE SectionIndex = 0xffff /* Last of reserved range. */ +) + +var shnStrings = []intName{ + {0, "SHN_UNDEF"}, + {0xff00, "SHN_LOPROC"}, + {0xff20, "SHN_LOOS"}, + {0xfff1, "SHN_ABS"}, + {0xfff2, "SHN_COMMON"}, + {0xffff, "SHN_XINDEX"}, +} + +func (i SectionIndex) String() string { return stringName(uint32(i), shnStrings, false) } +func (i SectionIndex) GoString() string { return stringName(uint32(i), shnStrings, true) } + +// Section type. +type SectionType uint32 + +const ( + SHT_NULL SectionType = 0 /* inactive */ + SHT_PROGBITS SectionType = 1 /* program defined information */ + SHT_SYMTAB SectionType = 2 /* symbol table section */ + SHT_STRTAB SectionType = 3 /* string table section */ + SHT_RELA SectionType = 4 /* relocation section with addends */ + SHT_HASH SectionType = 5 /* symbol hash table section */ + SHT_DYNAMIC SectionType = 6 /* dynamic section */ + SHT_NOTE SectionType = 7 /* note section */ + SHT_NOBITS SectionType = 8 /* no space section */ + SHT_REL SectionType = 9 /* relocation section - no addends */ + SHT_SHLIB SectionType = 10 /* reserved - purpose unknown */ + SHT_DYNSYM SectionType = 11 /* dynamic symbol table section */ + SHT_INIT_ARRAY SectionType = 14 /* Initialization function pointers. */ + SHT_FINI_ARRAY SectionType = 15 /* Termination function pointers. */ + SHT_PREINIT_ARRAY SectionType = 16 /* Pre-initialization function ptrs. */ + SHT_GROUP SectionType = 17 /* Section group. */ + SHT_SYMTAB_SHNDX SectionType = 18 /* Section indexes (see SHN_XINDEX). */ + SHT_LOOS SectionType = 0x60000000 /* First of OS specific semantics */ + SHT_GNU_ATTRIBUTES SectionType = 0x6ffffff5 /* GNU object attributes */ + SHT_GNU_HASH SectionType = 0x6ffffff6 /* GNU hash table */ + SHT_GNU_LIBLIST SectionType = 0x6ffffff7 /* GNU prelink library list */ + SHT_GNU_VERDEF SectionType = 0x6ffffffd /* GNU version definition section */ + SHT_GNU_VERNEED SectionType = 0x6ffffffe /* GNU version needs section */ + SHT_GNU_VERSYM SectionType = 0x6fffffff /* GNU version symbol table */ + SHT_HIOS SectionType = 0x6fffffff /* Last of OS specific semantics */ + SHT_LOPROC SectionType = 0x70000000 /* reserved range for processor */ + SHT_HIPROC SectionType = 0x7fffffff /* specific section header types */ + SHT_LOUSER SectionType = 0x80000000 /* reserved range for application */ + SHT_HIUSER SectionType = 0xffffffff /* specific indexes */ +) + +var shtStrings = []intName{ + {0, "SHT_NULL"}, + {1, "SHT_PROGBITS"}, + {2, "SHT_SYMTAB"}, + {3, "SHT_STRTAB"}, + {4, "SHT_RELA"}, + {5, "SHT_HASH"}, + {6, "SHT_DYNAMIC"}, + {7, "SHT_NOTE"}, + {8, "SHT_NOBITS"}, + {9, "SHT_REL"}, + {10, "SHT_SHLIB"}, + {11, "SHT_DYNSYM"}, + {14, "SHT_INIT_ARRAY"}, + {15, "SHT_FINI_ARRAY"}, + {16, "SHT_PREINIT_ARRAY"}, + {17, "SHT_GROUP"}, + {18, "SHT_SYMTAB_SHNDX"}, + {0x60000000, "SHT_LOOS"}, + {0x6ffffff5, "SHT_GNU_ATTRIBUTES"}, + {0x6ffffff6, "SHT_GNU_HASH"}, + {0x6ffffff7, "SHT_GNU_LIBLIST"}, + {0x6ffffffd, "SHT_GNU_VERDEF"}, + {0x6ffffffe, "SHT_GNU_VERNEED"}, + {0x6fffffff, "SHT_GNU_VERSYM"}, + {0x70000000, "SHT_LOPROC"}, + {0x7fffffff, "SHT_HIPROC"}, + {0x80000000, "SHT_LOUSER"}, + {0xffffffff, "SHT_HIUSER"}, +} + +func (i SectionType) String() string { return stringName(uint32(i), shtStrings, false) } +func (i SectionType) GoString() string { return stringName(uint32(i), shtStrings, true) } + +// Section flags. +type SectionFlag uint32 + +const ( + SHF_WRITE SectionFlag = 0x1 /* Section contains writable data. */ + SHF_ALLOC SectionFlag = 0x2 /* Section occupies memory. */ + SHF_EXECINSTR SectionFlag = 0x4 /* Section contains instructions. */ + SHF_MERGE SectionFlag = 0x10 /* Section may be merged. */ + SHF_STRINGS SectionFlag = 0x20 /* Section contains strings. */ + SHF_INFO_LINK SectionFlag = 0x40 /* sh_info holds section index. */ + SHF_LINK_ORDER SectionFlag = 0x80 /* Special ordering requirements. */ + SHF_OS_NONCONFORMING SectionFlag = 0x100 /* OS-specific processing required. */ + SHF_GROUP SectionFlag = 0x200 /* Member of section group. */ + SHF_TLS SectionFlag = 0x400 /* Section contains TLS data. */ + SHF_MASKOS SectionFlag = 0x0ff00000 /* OS-specific semantics. */ + SHF_MASKPROC SectionFlag = 0xf0000000 /* Processor-specific semantics. */ +) + +var shfStrings = []intName{ + {0x1, "SHF_WRITE"}, + {0x2, "SHF_ALLOC"}, + {0x4, "SHF_EXECINSTR"}, + {0x10, "SHF_MERGE"}, + {0x20, "SHF_STRINGS"}, + {0x40, "SHF_INFO_LINK"}, + {0x80, "SHF_LINK_ORDER"}, + {0x100, "SHF_OS_NONCONFORMING"}, + {0x200, "SHF_GROUP"}, + {0x400, "SHF_TLS"}, +} + +func (i SectionFlag) String() string { return flagName(uint32(i), shfStrings, false) } +func (i SectionFlag) GoString() string { return flagName(uint32(i), shfStrings, true) } + +// Prog.Type +type ProgType int + +const ( + PT_NULL ProgType = 0 /* Unused entry. */ + PT_LOAD ProgType = 1 /* Loadable segment. */ + PT_DYNAMIC ProgType = 2 /* Dynamic linking information segment. */ + PT_INTERP ProgType = 3 /* Pathname of interpreter. */ + PT_NOTE ProgType = 4 /* Auxiliary information. */ + PT_SHLIB ProgType = 5 /* Reserved (not used). */ + PT_PHDR ProgType = 6 /* Location of program header itself. */ + PT_TLS ProgType = 7 /* Thread local storage segment */ + PT_LOOS ProgType = 0x60000000 /* First OS-specific. */ + PT_HIOS ProgType = 0x6fffffff /* Last OS-specific. */ + PT_LOPROC ProgType = 0x70000000 /* First processor-specific type. */ + PT_HIPROC ProgType = 0x7fffffff /* Last processor-specific type. */ +) + +var ptStrings = []intName{ + {0, "PT_NULL"}, + {1, "PT_LOAD"}, + {2, "PT_DYNAMIC"}, + {3, "PT_INTERP"}, + {4, "PT_NOTE"}, + {5, "PT_SHLIB"}, + {6, "PT_PHDR"}, + {7, "PT_TLS"}, + {0x60000000, "PT_LOOS"}, + {0x6fffffff, "PT_HIOS"}, + {0x70000000, "PT_LOPROC"}, + {0x7fffffff, "PT_HIPROC"}, +} + +func (i ProgType) String() string { return stringName(uint32(i), ptStrings, false) } +func (i ProgType) GoString() string { return stringName(uint32(i), ptStrings, true) } + +// Prog.Flag +type ProgFlag uint32 + +const ( + PF_X ProgFlag = 0x1 /* Executable. */ + PF_W ProgFlag = 0x2 /* Writable. */ + PF_R ProgFlag = 0x4 /* Readable. */ + PF_MASKOS ProgFlag = 0x0ff00000 /* Operating system-specific. */ + PF_MASKPROC ProgFlag = 0xf0000000 /* Processor-specific. */ +) + +var pfStrings = []intName{ + {0x1, "PF_X"}, + {0x2, "PF_W"}, + {0x4, "PF_R"}, +} + +func (i ProgFlag) String() string { return flagName(uint32(i), pfStrings, false) } +func (i ProgFlag) GoString() string { return flagName(uint32(i), pfStrings, true) } + +// Dyn.Tag +type DynTag int + +const ( + DT_NULL DynTag = 0 /* Terminating entry. */ + DT_NEEDED DynTag = 1 /* String table offset of a needed shared library. */ + DT_PLTRELSZ DynTag = 2 /* Total size in bytes of PLT relocations. */ + DT_PLTGOT DynTag = 3 /* Processor-dependent address. */ + DT_HASH DynTag = 4 /* Address of symbol hash table. */ + DT_STRTAB DynTag = 5 /* Address of string table. */ + DT_SYMTAB DynTag = 6 /* Address of symbol table. */ + DT_RELA DynTag = 7 /* Address of ElfNN_Rela relocations. */ + DT_RELASZ DynTag = 8 /* Total size of ElfNN_Rela relocations. */ + DT_RELAENT DynTag = 9 /* Size of each ElfNN_Rela relocation entry. */ + DT_STRSZ DynTag = 10 /* Size of string table. */ + DT_SYMENT DynTag = 11 /* Size of each symbol table entry. */ + DT_INIT DynTag = 12 /* Address of initialization function. */ + DT_FINI DynTag = 13 /* Address of finalization function. */ + DT_SONAME DynTag = 14 /* String table offset of shared object name. */ + DT_RPATH DynTag = 15 /* String table offset of library path. [sup] */ + DT_SYMBOLIC DynTag = 16 /* Indicates "symbolic" linking. [sup] */ + DT_REL DynTag = 17 /* Address of ElfNN_Rel relocations. */ + DT_RELSZ DynTag = 18 /* Total size of ElfNN_Rel relocations. */ + DT_RELENT DynTag = 19 /* Size of each ElfNN_Rel relocation. */ + DT_PLTREL DynTag = 20 /* Type of relocation used for PLT. */ + DT_DEBUG DynTag = 21 /* Reserved (not used). */ + DT_TEXTREL DynTag = 22 /* Indicates there may be relocations in non-writable segments. [sup] */ + DT_JMPREL DynTag = 23 /* Address of PLT relocations. */ + DT_BIND_NOW DynTag = 24 /* [sup] */ + DT_INIT_ARRAY DynTag = 25 /* Address of the array of pointers to initialization functions */ + DT_FINI_ARRAY DynTag = 26 /* Address of the array of pointers to termination functions */ + DT_INIT_ARRAYSZ DynTag = 27 /* Size in bytes of the array of initialization functions. */ + DT_FINI_ARRAYSZ DynTag = 28 /* Size in bytes of the array of termination functions. */ + DT_RUNPATH DynTag = 29 /* String table offset of a null-terminated library search path string. */ + DT_FLAGS DynTag = 30 /* Object specific flag values. */ + DT_ENCODING DynTag = 32 /* Values greater than or equal to DT_ENCODING + and less than DT_LOOS follow the rules for + the interpretation of the d_un union + as follows: even == 'd_ptr', even == 'd_val' + or none */ + DT_PREINIT_ARRAY DynTag = 32 /* Address of the array of pointers to pre-initialization functions. */ + DT_PREINIT_ARRAYSZ DynTag = 33 /* Size in bytes of the array of pre-initialization functions. */ + DT_LOOS DynTag = 0x6000000d /* First OS-specific */ + DT_HIOS DynTag = 0x6ffff000 /* Last OS-specific */ + DT_VERSYM DynTag = 0x6ffffff0 + DT_VERNEED DynTag = 0x6ffffffe + DT_VERNEEDNUM DynTag = 0x6fffffff + DT_LOPROC DynTag = 0x70000000 /* First processor-specific type. */ + DT_HIPROC DynTag = 0x7fffffff /* Last processor-specific type. */ +) + +var dtStrings = []intName{ + {0, "DT_NULL"}, + {1, "DT_NEEDED"}, + {2, "DT_PLTRELSZ"}, + {3, "DT_PLTGOT"}, + {4, "DT_HASH"}, + {5, "DT_STRTAB"}, + {6, "DT_SYMTAB"}, + {7, "DT_RELA"}, + {8, "DT_RELASZ"}, + {9, "DT_RELAENT"}, + {10, "DT_STRSZ"}, + {11, "DT_SYMENT"}, + {12, "DT_INIT"}, + {13, "DT_FINI"}, + {14, "DT_SONAME"}, + {15, "DT_RPATH"}, + {16, "DT_SYMBOLIC"}, + {17, "DT_REL"}, + {18, "DT_RELSZ"}, + {19, "DT_RELENT"}, + {20, "DT_PLTREL"}, + {21, "DT_DEBUG"}, + {22, "DT_TEXTREL"}, + {23, "DT_JMPREL"}, + {24, "DT_BIND_NOW"}, + {25, "DT_INIT_ARRAY"}, + {26, "DT_FINI_ARRAY"}, + {27, "DT_INIT_ARRAYSZ"}, + {28, "DT_FINI_ARRAYSZ"}, + {29, "DT_RUNPATH"}, + {30, "DT_FLAGS"}, + {32, "DT_ENCODING"}, + {32, "DT_PREINIT_ARRAY"}, + {33, "DT_PREINIT_ARRAYSZ"}, + {0x6000000d, "DT_LOOS"}, + {0x6ffff000, "DT_HIOS"}, + {0x6ffffff0, "DT_VERSYM"}, + {0x6ffffffe, "DT_VERNEED"}, + {0x6fffffff, "DT_VERNEEDNUM"}, + {0x70000000, "DT_LOPROC"}, + {0x7fffffff, "DT_HIPROC"}, +} + +func (i DynTag) String() string { return stringName(uint32(i), dtStrings, false) } +func (i DynTag) GoString() string { return stringName(uint32(i), dtStrings, true) } + +// DT_FLAGS values. +type DynFlag int + +const ( + DF_ORIGIN DynFlag = 0x0001 /* Indicates that the object being loaded may + make reference to the + $ORIGIN substitution string */ + DF_SYMBOLIC DynFlag = 0x0002 /* Indicates "symbolic" linking. */ + DF_TEXTREL DynFlag = 0x0004 /* Indicates there may be relocations in non-writable segments. */ + DF_BIND_NOW DynFlag = 0x0008 /* Indicates that the dynamic linker should + process all relocations for the object + containing this entry before transferring + control to the program. */ + DF_STATIC_TLS DynFlag = 0x0010 /* Indicates that the shared object or + executable contains code using a static + thread-local storage scheme. */ +) + +var dflagStrings = []intName{ + {0x0001, "DF_ORIGIN"}, + {0x0002, "DF_SYMBOLIC"}, + {0x0004, "DF_TEXTREL"}, + {0x0008, "DF_BIND_NOW"}, + {0x0010, "DF_STATIC_TLS"}, +} + +func (i DynFlag) String() string { return flagName(uint32(i), dflagStrings, false) } +func (i DynFlag) GoString() string { return flagName(uint32(i), dflagStrings, true) } + +// NType values; used in core files. +type NType int + +const ( + NT_PRSTATUS NType = 1 /* Process status. */ + NT_FPREGSET NType = 2 /* Floating point registers. */ + NT_PRPSINFO NType = 3 /* Process state info. */ +) + +var ntypeStrings = []intName{ + {1, "NT_PRSTATUS"}, + {2, "NT_FPREGSET"}, + {3, "NT_PRPSINFO"}, +} + +func (i NType) String() string { return stringName(uint32(i), ntypeStrings, false) } +func (i NType) GoString() string { return stringName(uint32(i), ntypeStrings, true) } + +/* Symbol Binding - ELFNN_ST_BIND - st_info */ +type SymBind int + +const ( + STB_LOCAL SymBind = 0 /* Local symbol */ + STB_GLOBAL SymBind = 1 /* Global symbol */ + STB_WEAK SymBind = 2 /* like global - lower precedence */ + STB_LOOS SymBind = 10 /* Reserved range for operating system */ + STB_HIOS SymBind = 12 /* specific semantics. */ + STB_LOPROC SymBind = 13 /* reserved range for processor */ + STB_HIPROC SymBind = 15 /* specific semantics. */ +) + +var stbStrings = []intName{ + {0, "STB_LOCAL"}, + {1, "STB_GLOBAL"}, + {2, "STB_WEAK"}, + {10, "STB_LOOS"}, + {12, "STB_HIOS"}, + {13, "STB_LOPROC"}, + {15, "STB_HIPROC"}, +} + +func (i SymBind) String() string { return stringName(uint32(i), stbStrings, false) } +func (i SymBind) GoString() string { return stringName(uint32(i), stbStrings, true) } + +/* Symbol type - ELFNN_ST_TYPE - st_info */ +type SymType int + +const ( + STT_NOTYPE SymType = 0 /* Unspecified type. */ + STT_OBJECT SymType = 1 /* Data object. */ + STT_FUNC SymType = 2 /* Function. */ + STT_SECTION SymType = 3 /* Section. */ + STT_FILE SymType = 4 /* Source file. */ + STT_COMMON SymType = 5 /* Uninitialized common block. */ + STT_TLS SymType = 6 /* TLS object. */ + STT_LOOS SymType = 10 /* Reserved range for operating system */ + STT_HIOS SymType = 12 /* specific semantics. */ + STT_LOPROC SymType = 13 /* reserved range for processor */ + STT_HIPROC SymType = 15 /* specific semantics. */ +) + +var sttStrings = []intName{ + {0, "STT_NOTYPE"}, + {1, "STT_OBJECT"}, + {2, "STT_FUNC"}, + {3, "STT_SECTION"}, + {4, "STT_FILE"}, + {5, "STT_COMMON"}, + {6, "STT_TLS"}, + {10, "STT_LOOS"}, + {12, "STT_HIOS"}, + {13, "STT_LOPROC"}, + {15, "STT_HIPROC"}, +} + +func (i SymType) String() string { return stringName(uint32(i), sttStrings, false) } +func (i SymType) GoString() string { return stringName(uint32(i), sttStrings, true) } + +/* Symbol visibility - ELFNN_ST_VISIBILITY - st_other */ +type SymVis int + +const ( + STV_DEFAULT SymVis = 0x0 /* Default visibility (see binding). */ + STV_INTERNAL SymVis = 0x1 /* Special meaning in relocatable objects. */ + STV_HIDDEN SymVis = 0x2 /* Not visible. */ + STV_PROTECTED SymVis = 0x3 /* Visible but not preemptible. */ +) + +var stvStrings = []intName{ + {0x0, "STV_DEFAULT"}, + {0x1, "STV_INTERNAL"}, + {0x2, "STV_HIDDEN"}, + {0x3, "STV_PROTECTED"}, +} + +func (i SymVis) String() string { return stringName(uint32(i), stvStrings, false) } +func (i SymVis) GoString() string { return stringName(uint32(i), stvStrings, true) } + +/* + * Relocation types. + */ + +// Relocation types for x86-64. +type R_X86_64 int + +const ( + R_X86_64_NONE R_X86_64 = 0 /* No relocation. */ + R_X86_64_64 R_X86_64 = 1 /* Add 64 bit symbol value. */ + R_X86_64_PC32 R_X86_64 = 2 /* PC-relative 32 bit signed sym value. */ + R_X86_64_GOT32 R_X86_64 = 3 /* PC-relative 32 bit GOT offset. */ + R_X86_64_PLT32 R_X86_64 = 4 /* PC-relative 32 bit PLT offset. */ + R_X86_64_COPY R_X86_64 = 5 /* Copy data from shared object. */ + R_X86_64_GLOB_DAT R_X86_64 = 6 /* Set GOT entry to data address. */ + R_X86_64_JMP_SLOT R_X86_64 = 7 /* Set GOT entry to code address. */ + R_X86_64_RELATIVE R_X86_64 = 8 /* Add load address of shared object. */ + R_X86_64_GOTPCREL R_X86_64 = 9 /* Add 32 bit signed pcrel offset to GOT. */ + R_X86_64_32 R_X86_64 = 10 /* Add 32 bit zero extended symbol value */ + R_X86_64_32S R_X86_64 = 11 /* Add 32 bit sign extended symbol value */ + R_X86_64_16 R_X86_64 = 12 /* Add 16 bit zero extended symbol value */ + R_X86_64_PC16 R_X86_64 = 13 /* Add 16 bit signed extended pc relative symbol value */ + R_X86_64_8 R_X86_64 = 14 /* Add 8 bit zero extended symbol value */ + R_X86_64_PC8 R_X86_64 = 15 /* Add 8 bit signed extended pc relative symbol value */ + R_X86_64_DTPMOD64 R_X86_64 = 16 /* ID of module containing symbol */ + R_X86_64_DTPOFF64 R_X86_64 = 17 /* Offset in TLS block */ + R_X86_64_TPOFF64 R_X86_64 = 18 /* Offset in static TLS block */ + R_X86_64_TLSGD R_X86_64 = 19 /* PC relative offset to GD GOT entry */ + R_X86_64_TLSLD R_X86_64 = 20 /* PC relative offset to LD GOT entry */ + R_X86_64_DTPOFF32 R_X86_64 = 21 /* Offset in TLS block */ + R_X86_64_GOTTPOFF R_X86_64 = 22 /* PC relative offset to IE GOT entry */ + R_X86_64_TPOFF32 R_X86_64 = 23 /* Offset in static TLS block */ +) + +var rx86_64Strings = []intName{ + {0, "R_X86_64_NONE"}, + {1, "R_X86_64_64"}, + {2, "R_X86_64_PC32"}, + {3, "R_X86_64_GOT32"}, + {4, "R_X86_64_PLT32"}, + {5, "R_X86_64_COPY"}, + {6, "R_X86_64_GLOB_DAT"}, + {7, "R_X86_64_JMP_SLOT"}, + {8, "R_X86_64_RELATIVE"}, + {9, "R_X86_64_GOTPCREL"}, + {10, "R_X86_64_32"}, + {11, "R_X86_64_32S"}, + {12, "R_X86_64_16"}, + {13, "R_X86_64_PC16"}, + {14, "R_X86_64_8"}, + {15, "R_X86_64_PC8"}, + {16, "R_X86_64_DTPMOD64"}, + {17, "R_X86_64_DTPOFF64"}, + {18, "R_X86_64_TPOFF64"}, + {19, "R_X86_64_TLSGD"}, + {20, "R_X86_64_TLSLD"}, + {21, "R_X86_64_DTPOFF32"}, + {22, "R_X86_64_GOTTPOFF"}, + {23, "R_X86_64_TPOFF32"}, +} + +func (i R_X86_64) String() string { return stringName(uint32(i), rx86_64Strings, false) } +func (i R_X86_64) GoString() string { return stringName(uint32(i), rx86_64Strings, true) } + +// Relocation types for AArch64 (aka arm64) +type R_AARCH64 int + +const ( + R_AARCH64_NONE R_AARCH64 = 0 + R_AARCH64_P32_ABS32 R_AARCH64 = 1 + R_AARCH64_P32_ABS16 R_AARCH64 = 2 + R_AARCH64_P32_PREL32 R_AARCH64 = 3 + R_AARCH64_P32_PREL16 R_AARCH64 = 4 + R_AARCH64_P32_MOVW_UABS_G0 R_AARCH64 = 5 + R_AARCH64_P32_MOVW_UABS_G0_NC R_AARCH64 = 6 + R_AARCH64_P32_MOVW_UABS_G1 R_AARCH64 = 7 + R_AARCH64_P32_MOVW_SABS_G0 R_AARCH64 = 8 + R_AARCH64_P32_LD_PREL_LO19 R_AARCH64 = 9 + R_AARCH64_P32_ADR_PREL_LO21 R_AARCH64 = 10 + R_AARCH64_P32_ADR_PREL_PG_HI21 R_AARCH64 = 11 + R_AARCH64_P32_ADD_ABS_LO12_NC R_AARCH64 = 12 + R_AARCH64_P32_LDST8_ABS_LO12_NC R_AARCH64 = 13 + R_AARCH64_P32_LDST16_ABS_LO12_NC R_AARCH64 = 14 + R_AARCH64_P32_LDST32_ABS_LO12_NC R_AARCH64 = 15 + R_AARCH64_P32_LDST64_ABS_LO12_NC R_AARCH64 = 16 + R_AARCH64_P32_LDST128_ABS_LO12_NC R_AARCH64 = 17 + R_AARCH64_P32_TSTBR14 R_AARCH64 = 18 + R_AARCH64_P32_CONDBR19 R_AARCH64 = 19 + R_AARCH64_P32_JUMP26 R_AARCH64 = 20 + R_AARCH64_P32_CALL26 R_AARCH64 = 21 + R_AARCH64_P32_GOT_LD_PREL19 R_AARCH64 = 25 + R_AARCH64_P32_ADR_GOT_PAGE R_AARCH64 = 26 + R_AARCH64_P32_LD32_GOT_LO12_NC R_AARCH64 = 27 + R_AARCH64_P32_TLSGD_ADR_PAGE21 R_AARCH64 = 81 + R_AARCH64_P32_TLSGD_ADD_LO12_NC R_AARCH64 = 82 + R_AARCH64_P32_TLSIE_ADR_GOTTPREL_PAGE21 R_AARCH64 = 103 + R_AARCH64_P32_TLSIE_LD32_GOTTPREL_LO12_NC R_AARCH64 = 104 + R_AARCH64_P32_TLSIE_LD_GOTTPREL_PREL19 R_AARCH64 = 105 + R_AARCH64_P32_TLSLE_MOVW_TPREL_G1 R_AARCH64 = 106 + R_AARCH64_P32_TLSLE_MOVW_TPREL_G0 R_AARCH64 = 107 + R_AARCH64_P32_TLSLE_MOVW_TPREL_G0_NC R_AARCH64 = 108 + R_AARCH64_P32_TLSLE_ADD_TPREL_HI12 R_AARCH64 = 109 + R_AARCH64_P32_TLSLE_ADD_TPREL_LO12 R_AARCH64 = 110 + R_AARCH64_P32_TLSLE_ADD_TPREL_LO12_NC R_AARCH64 = 111 + R_AARCH64_P32_TLSDESC_LD_PREL19 R_AARCH64 = 122 + R_AARCH64_P32_TLSDESC_ADR_PREL21 R_AARCH64 = 123 + R_AARCH64_P32_TLSDESC_ADR_PAGE21 R_AARCH64 = 124 + R_AARCH64_P32_TLSDESC_LD32_LO12_NC R_AARCH64 = 125 + R_AARCH64_P32_TLSDESC_ADD_LO12_NC R_AARCH64 = 126 + R_AARCH64_P32_TLSDESC_CALL R_AARCH64 = 127 + R_AARCH64_P32_COPY R_AARCH64 = 180 + R_AARCH64_P32_GLOB_DAT R_AARCH64 = 181 + R_AARCH64_P32_JUMP_SLOT R_AARCH64 = 182 + R_AARCH64_P32_RELATIVE R_AARCH64 = 183 + R_AARCH64_P32_TLS_DTPMOD R_AARCH64 = 184 + R_AARCH64_P32_TLS_DTPREL R_AARCH64 = 185 + R_AARCH64_P32_TLS_TPREL R_AARCH64 = 186 + R_AARCH64_P32_TLSDESC R_AARCH64 = 187 + R_AARCH64_P32_IRELATIVE R_AARCH64 = 188 + R_AARCH64_NULL R_AARCH64 = 256 + R_AARCH64_ABS64 R_AARCH64 = 257 + R_AARCH64_ABS32 R_AARCH64 = 258 + R_AARCH64_ABS16 R_AARCH64 = 259 + R_AARCH64_PREL64 R_AARCH64 = 260 + R_AARCH64_PREL32 R_AARCH64 = 261 + R_AARCH64_PREL16 R_AARCH64 = 262 + R_AARCH64_MOVW_UABS_G0 R_AARCH64 = 263 + R_AARCH64_MOVW_UABS_G0_NC R_AARCH64 = 264 + R_AARCH64_MOVW_UABS_G1 R_AARCH64 = 265 + R_AARCH64_MOVW_UABS_G1_NC R_AARCH64 = 266 + R_AARCH64_MOVW_UABS_G2 R_AARCH64 = 267 + R_AARCH64_MOVW_UABS_G2_NC R_AARCH64 = 268 + R_AARCH64_MOVW_UABS_G3 R_AARCH64 = 269 + R_AARCH64_MOVW_SABS_G0 R_AARCH64 = 270 + R_AARCH64_MOVW_SABS_G1 R_AARCH64 = 271 + R_AARCH64_MOVW_SABS_G2 R_AARCH64 = 272 + R_AARCH64_LD_PREL_LO19 R_AARCH64 = 273 + R_AARCH64_ADR_PREL_LO21 R_AARCH64 = 274 + R_AARCH64_ADR_PREL_PG_HI21 R_AARCH64 = 275 + R_AARCH64_ADR_PREL_PG_HI21_NC R_AARCH64 = 276 + R_AARCH64_ADD_ABS_LO12_NC R_AARCH64 = 277 + R_AARCH64_LDST8_ABS_LO12_NC R_AARCH64 = 278 + R_AARCH64_TSTBR14 R_AARCH64 = 279 + R_AARCH64_CONDBR19 R_AARCH64 = 280 + R_AARCH64_JUMP26 R_AARCH64 = 282 + R_AARCH64_CALL26 R_AARCH64 = 283 + R_AARCH64_LDST16_ABS_LO12_NC R_AARCH64 = 284 + R_AARCH64_LDST32_ABS_LO12_NC R_AARCH64 = 285 + R_AARCH64_LDST64_ABS_LO12_NC R_AARCH64 = 286 + R_AARCH64_LDST128_ABS_LO12_NC R_AARCH64 = 299 + R_AARCH64_GOT_LD_PREL19 R_AARCH64 = 309 + R_AARCH64_ADR_GOT_PAGE R_AARCH64 = 311 + R_AARCH64_LD64_GOT_LO12_NC R_AARCH64 = 312 + R_AARCH64_TLSGD_ADR_PAGE21 R_AARCH64 = 513 + R_AARCH64_TLSGD_ADD_LO12_NC R_AARCH64 = 514 + R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 R_AARCH64 = 539 + R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC R_AARCH64 = 540 + R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 R_AARCH64 = 541 + R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC R_AARCH64 = 542 + R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 R_AARCH64 = 543 + R_AARCH64_TLSLE_MOVW_TPREL_G2 R_AARCH64 = 544 + R_AARCH64_TLSLE_MOVW_TPREL_G1 R_AARCH64 = 545 + R_AARCH64_TLSLE_MOVW_TPREL_G1_NC R_AARCH64 = 546 + R_AARCH64_TLSLE_MOVW_TPREL_G0 R_AARCH64 = 547 + R_AARCH64_TLSLE_MOVW_TPREL_G0_NC R_AARCH64 = 548 + R_AARCH64_TLSLE_ADD_TPREL_HI12 R_AARCH64 = 549 + R_AARCH64_TLSLE_ADD_TPREL_LO12 R_AARCH64 = 550 + R_AARCH64_TLSLE_ADD_TPREL_LO12_NC R_AARCH64 = 551 + R_AARCH64_TLSDESC_LD_PREL19 R_AARCH64 = 560 + R_AARCH64_TLSDESC_ADR_PREL21 R_AARCH64 = 561 + R_AARCH64_TLSDESC_ADR_PAGE21 R_AARCH64 = 562 + R_AARCH64_TLSDESC_LD64_LO12_NC R_AARCH64 = 563 + R_AARCH64_TLSDESC_ADD_LO12_NC R_AARCH64 = 564 + R_AARCH64_TLSDESC_OFF_G1 R_AARCH64 = 565 + R_AARCH64_TLSDESC_OFF_G0_NC R_AARCH64 = 566 + R_AARCH64_TLSDESC_LDR R_AARCH64 = 567 + R_AARCH64_TLSDESC_ADD R_AARCH64 = 568 + R_AARCH64_TLSDESC_CALL R_AARCH64 = 569 + R_AARCH64_COPY R_AARCH64 = 1024 + R_AARCH64_GLOB_DAT R_AARCH64 = 1025 + R_AARCH64_JUMP_SLOT R_AARCH64 = 1026 + R_AARCH64_RELATIVE R_AARCH64 = 1027 + R_AARCH64_TLS_DTPMOD64 R_AARCH64 = 1028 + R_AARCH64_TLS_DTPREL64 R_AARCH64 = 1029 + R_AARCH64_TLS_TPREL64 R_AARCH64 = 1030 + R_AARCH64_TLSDESC R_AARCH64 = 1031 + R_AARCH64_IRELATIVE R_AARCH64 = 1032 +) + +var raarch64Strings = []intName{ + {0, "R_AARCH64_NONE"}, + {1, "R_AARCH64_P32_ABS32"}, + {2, "R_AARCH64_P32_ABS16"}, + {3, "R_AARCH64_P32_PREL32"}, + {4, "R_AARCH64_P32_PREL16"}, + {5, "R_AARCH64_P32_MOVW_UABS_G0"}, + {6, "R_AARCH64_P32_MOVW_UABS_G0_NC"}, + {7, "R_AARCH64_P32_MOVW_UABS_G1"}, + {8, "R_AARCH64_P32_MOVW_SABS_G0"}, + {9, "R_AARCH64_P32_LD_PREL_LO19"}, + {10, "R_AARCH64_P32_ADR_PREL_LO21"}, + {11, "R_AARCH64_P32_ADR_PREL_PG_HI21"}, + {12, "R_AARCH64_P32_ADD_ABS_LO12_NC"}, + {13, "R_AARCH64_P32_LDST8_ABS_LO12_NC"}, + {14, "R_AARCH64_P32_LDST16_ABS_LO12_NC"}, + {15, "R_AARCH64_P32_LDST32_ABS_LO12_NC"}, + {16, "R_AARCH64_P32_LDST64_ABS_LO12_NC"}, + {17, "R_AARCH64_P32_LDST128_ABS_LO12_NC"}, + {18, "R_AARCH64_P32_TSTBR14"}, + {19, "R_AARCH64_P32_CONDBR19"}, + {20, "R_AARCH64_P32_JUMP26"}, + {21, "R_AARCH64_P32_CALL26"}, + {25, "R_AARCH64_P32_GOT_LD_PREL19"}, + {26, "R_AARCH64_P32_ADR_GOT_PAGE"}, + {27, "R_AARCH64_P32_LD32_GOT_LO12_NC"}, + {81, "R_AARCH64_P32_TLSGD_ADR_PAGE21"}, + {82, "R_AARCH64_P32_TLSGD_ADD_LO12_NC"}, + {103, "R_AARCH64_P32_TLSIE_ADR_GOTTPREL_PAGE21"}, + {104, "R_AARCH64_P32_TLSIE_LD32_GOTTPREL_LO12_NC"}, + {105, "R_AARCH64_P32_TLSIE_LD_GOTTPREL_PREL19"}, + {106, "R_AARCH64_P32_TLSLE_MOVW_TPREL_G1"}, + {107, "R_AARCH64_P32_TLSLE_MOVW_TPREL_G0"}, + {108, "R_AARCH64_P32_TLSLE_MOVW_TPREL_G0_NC"}, + {109, "R_AARCH64_P32_TLSLE_ADD_TPREL_HI12"}, + {110, "R_AARCH64_P32_TLSLE_ADD_TPREL_LO12"}, + {111, "R_AARCH64_P32_TLSLE_ADD_TPREL_LO12_NC"}, + {122, "R_AARCH64_P32_TLSDESC_LD_PREL19"}, + {123, "R_AARCH64_P32_TLSDESC_ADR_PREL21"}, + {124, "R_AARCH64_P32_TLSDESC_ADR_PAGE21"}, + {125, "R_AARCH64_P32_TLSDESC_LD32_LO12_NC"}, + {126, "R_AARCH64_P32_TLSDESC_ADD_LO12_NC"}, + {127, "R_AARCH64_P32_TLSDESC_CALL"}, + {180, "R_AARCH64_P32_COPY"}, + {181, "R_AARCH64_P32_GLOB_DAT"}, + {182, "R_AARCH64_P32_JUMP_SLOT"}, + {183, "R_AARCH64_P32_RELATIVE"}, + {184, "R_AARCH64_P32_TLS_DTPMOD"}, + {185, "R_AARCH64_P32_TLS_DTPREL"}, + {186, "R_AARCH64_P32_TLS_TPREL"}, + {187, "R_AARCH64_P32_TLSDESC"}, + {188, "R_AARCH64_P32_IRELATIVE"}, + {256, "R_AARCH64_NULL"}, + {257, "R_AARCH64_ABS64"}, + {258, "R_AARCH64_ABS32"}, + {259, "R_AARCH64_ABS16"}, + {260, "R_AARCH64_PREL64"}, + {261, "R_AARCH64_PREL32"}, + {262, "R_AARCH64_PREL16"}, + {263, "R_AARCH64_MOVW_UABS_G0"}, + {264, "R_AARCH64_MOVW_UABS_G0_NC"}, + {265, "R_AARCH64_MOVW_UABS_G1"}, + {266, "R_AARCH64_MOVW_UABS_G1_NC"}, + {267, "R_AARCH64_MOVW_UABS_G2"}, + {268, "R_AARCH64_MOVW_UABS_G2_NC"}, + {269, "R_AARCH64_MOVW_UABS_G3"}, + {270, "R_AARCH64_MOVW_SABS_G0"}, + {271, "R_AARCH64_MOVW_SABS_G1"}, + {272, "R_AARCH64_MOVW_SABS_G2"}, + {273, "R_AARCH64_LD_PREL_LO19"}, + {274, "R_AARCH64_ADR_PREL_LO21"}, + {275, "R_AARCH64_ADR_PREL_PG_HI21"}, + {276, "R_AARCH64_ADR_PREL_PG_HI21_NC"}, + {277, "R_AARCH64_ADD_ABS_LO12_NC"}, + {278, "R_AARCH64_LDST8_ABS_LO12_NC"}, + {279, "R_AARCH64_TSTBR14"}, + {280, "R_AARCH64_CONDBR19"}, + {282, "R_AARCH64_JUMP26"}, + {283, "R_AARCH64_CALL26"}, + {284, "R_AARCH64_LDST16_ABS_LO12_NC"}, + {285, "R_AARCH64_LDST32_ABS_LO12_NC"}, + {286, "R_AARCH64_LDST64_ABS_LO12_NC"}, + {299, "R_AARCH64_LDST128_ABS_LO12_NC"}, + {309, "R_AARCH64_GOT_LD_PREL19"}, + {311, "R_AARCH64_ADR_GOT_PAGE"}, + {312, "R_AARCH64_LD64_GOT_LO12_NC"}, + {513, "R_AARCH64_TLSGD_ADR_PAGE21"}, + {514, "R_AARCH64_TLSGD_ADD_LO12_NC"}, + {539, "R_AARCH64_TLSIE_MOVW_GOTTPREL_G1"}, + {540, "R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC"}, + {541, "R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21"}, + {542, "R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC"}, + {543, "R_AARCH64_TLSIE_LD_GOTTPREL_PREL19"}, + {544, "R_AARCH64_TLSLE_MOVW_TPREL_G2"}, + {545, "R_AARCH64_TLSLE_MOVW_TPREL_G1"}, + {546, "R_AARCH64_TLSLE_MOVW_TPREL_G1_NC"}, + {547, "R_AARCH64_TLSLE_MOVW_TPREL_G0"}, + {548, "R_AARCH64_TLSLE_MOVW_TPREL_G0_NC"}, + {549, "R_AARCH64_TLSLE_ADD_TPREL_HI12"}, + {550, "R_AARCH64_TLSLE_ADD_TPREL_LO12"}, + {551, "R_AARCH64_TLSLE_ADD_TPREL_LO12_NC"}, + {560, "R_AARCH64_TLSDESC_LD_PREL19"}, + {561, "R_AARCH64_TLSDESC_ADR_PREL21"}, + {562, "R_AARCH64_TLSDESC_ADR_PAGE21"}, + {563, "R_AARCH64_TLSDESC_LD64_LO12_NC"}, + {564, "R_AARCH64_TLSDESC_ADD_LO12_NC"}, + {565, "R_AARCH64_TLSDESC_OFF_G1"}, + {566, "R_AARCH64_TLSDESC_OFF_G0_NC"}, + {567, "R_AARCH64_TLSDESC_LDR"}, + {568, "R_AARCH64_TLSDESC_ADD"}, + {569, "R_AARCH64_TLSDESC_CALL"}, + {1024, "R_AARCH64_COPY"}, + {1025, "R_AARCH64_GLOB_DAT"}, + {1026, "R_AARCH64_JUMP_SLOT"}, + {1027, "R_AARCH64_RELATIVE"}, + {1028, "R_AARCH64_TLS_DTPMOD64"}, + {1029, "R_AARCH64_TLS_DTPREL64"}, + {1030, "R_AARCH64_TLS_TPREL64"}, + {1031, "R_AARCH64_TLSDESC"}, + {1032, "R_AARCH64_IRELATIVE"}, +} + +func (i R_AARCH64) String() string { return stringName(uint32(i), raarch64Strings, false) } +func (i R_AARCH64) GoString() string { return stringName(uint32(i), raarch64Strings, true) } + +// Relocation types for Alpha. +type R_ALPHA int + +const ( + R_ALPHA_NONE R_ALPHA = 0 /* No reloc */ + R_ALPHA_REFLONG R_ALPHA = 1 /* Direct 32 bit */ + R_ALPHA_REFQUAD R_ALPHA = 2 /* Direct 64 bit */ + R_ALPHA_GPREL32 R_ALPHA = 3 /* GP relative 32 bit */ + R_ALPHA_LITERAL R_ALPHA = 4 /* GP relative 16 bit w/optimization */ + R_ALPHA_LITUSE R_ALPHA = 5 /* Optimization hint for LITERAL */ + R_ALPHA_GPDISP R_ALPHA = 6 /* Add displacement to GP */ + R_ALPHA_BRADDR R_ALPHA = 7 /* PC+4 relative 23 bit shifted */ + R_ALPHA_HINT R_ALPHA = 8 /* PC+4 relative 16 bit shifted */ + R_ALPHA_SREL16 R_ALPHA = 9 /* PC relative 16 bit */ + R_ALPHA_SREL32 R_ALPHA = 10 /* PC relative 32 bit */ + R_ALPHA_SREL64 R_ALPHA = 11 /* PC relative 64 bit */ + R_ALPHA_OP_PUSH R_ALPHA = 12 /* OP stack push */ + R_ALPHA_OP_STORE R_ALPHA = 13 /* OP stack pop and store */ + R_ALPHA_OP_PSUB R_ALPHA = 14 /* OP stack subtract */ + R_ALPHA_OP_PRSHIFT R_ALPHA = 15 /* OP stack right shift */ + R_ALPHA_GPVALUE R_ALPHA = 16 + R_ALPHA_GPRELHIGH R_ALPHA = 17 + R_ALPHA_GPRELLOW R_ALPHA = 18 + R_ALPHA_IMMED_GP_16 R_ALPHA = 19 + R_ALPHA_IMMED_GP_HI32 R_ALPHA = 20 + R_ALPHA_IMMED_SCN_HI32 R_ALPHA = 21 + R_ALPHA_IMMED_BR_HI32 R_ALPHA = 22 + R_ALPHA_IMMED_LO32 R_ALPHA = 23 + R_ALPHA_COPY R_ALPHA = 24 /* Copy symbol at runtime */ + R_ALPHA_GLOB_DAT R_ALPHA = 25 /* Create GOT entry */ + R_ALPHA_JMP_SLOT R_ALPHA = 26 /* Create PLT entry */ + R_ALPHA_RELATIVE R_ALPHA = 27 /* Adjust by program base */ +) + +var ralphaStrings = []intName{ + {0, "R_ALPHA_NONE"}, + {1, "R_ALPHA_REFLONG"}, + {2, "R_ALPHA_REFQUAD"}, + {3, "R_ALPHA_GPREL32"}, + {4, "R_ALPHA_LITERAL"}, + {5, "R_ALPHA_LITUSE"}, + {6, "R_ALPHA_GPDISP"}, + {7, "R_ALPHA_BRADDR"}, + {8, "R_ALPHA_HINT"}, + {9, "R_ALPHA_SREL16"}, + {10, "R_ALPHA_SREL32"}, + {11, "R_ALPHA_SREL64"}, + {12, "R_ALPHA_OP_PUSH"}, + {13, "R_ALPHA_OP_STORE"}, + {14, "R_ALPHA_OP_PSUB"}, + {15, "R_ALPHA_OP_PRSHIFT"}, + {16, "R_ALPHA_GPVALUE"}, + {17, "R_ALPHA_GPRELHIGH"}, + {18, "R_ALPHA_GPRELLOW"}, + {19, "R_ALPHA_IMMED_GP_16"}, + {20, "R_ALPHA_IMMED_GP_HI32"}, + {21, "R_ALPHA_IMMED_SCN_HI32"}, + {22, "R_ALPHA_IMMED_BR_HI32"}, + {23, "R_ALPHA_IMMED_LO32"}, + {24, "R_ALPHA_COPY"}, + {25, "R_ALPHA_GLOB_DAT"}, + {26, "R_ALPHA_JMP_SLOT"}, + {27, "R_ALPHA_RELATIVE"}, +} + +func (i R_ALPHA) String() string { return stringName(uint32(i), ralphaStrings, false) } +func (i R_ALPHA) GoString() string { return stringName(uint32(i), ralphaStrings, true) } + +// Relocation types for ARM. +type R_ARM int + +const ( + R_ARM_NONE R_ARM = 0 /* No relocation. */ + R_ARM_PC24 R_ARM = 1 + R_ARM_ABS32 R_ARM = 2 + R_ARM_REL32 R_ARM = 3 + R_ARM_PC13 R_ARM = 4 + R_ARM_ABS16 R_ARM = 5 + R_ARM_ABS12 R_ARM = 6 + R_ARM_THM_ABS5 R_ARM = 7 + R_ARM_ABS8 R_ARM = 8 + R_ARM_SBREL32 R_ARM = 9 + R_ARM_THM_PC22 R_ARM = 10 + R_ARM_THM_PC8 R_ARM = 11 + R_ARM_AMP_VCALL9 R_ARM = 12 + R_ARM_SWI24 R_ARM = 13 + R_ARM_THM_SWI8 R_ARM = 14 + R_ARM_XPC25 R_ARM = 15 + R_ARM_THM_XPC22 R_ARM = 16 + R_ARM_COPY R_ARM = 20 /* Copy data from shared object. */ + R_ARM_GLOB_DAT R_ARM = 21 /* Set GOT entry to data address. */ + R_ARM_JUMP_SLOT R_ARM = 22 /* Set GOT entry to code address. */ + R_ARM_RELATIVE R_ARM = 23 /* Add load address of shared object. */ + R_ARM_GOTOFF R_ARM = 24 /* Add GOT-relative symbol address. */ + R_ARM_GOTPC R_ARM = 25 /* Add PC-relative GOT table address. */ + R_ARM_GOT32 R_ARM = 26 /* Add PC-relative GOT offset. */ + R_ARM_PLT32 R_ARM = 27 /* Add PC-relative PLT offset. */ + R_ARM_GNU_VTENTRY R_ARM = 100 + R_ARM_GNU_VTINHERIT R_ARM = 101 + R_ARM_RSBREL32 R_ARM = 250 + R_ARM_THM_RPC22 R_ARM = 251 + R_ARM_RREL32 R_ARM = 252 + R_ARM_RABS32 R_ARM = 253 + R_ARM_RPC24 R_ARM = 254 + R_ARM_RBASE R_ARM = 255 +) + +var rarmStrings = []intName{ + {0, "R_ARM_NONE"}, + {1, "R_ARM_PC24"}, + {2, "R_ARM_ABS32"}, + {3, "R_ARM_REL32"}, + {4, "R_ARM_PC13"}, + {5, "R_ARM_ABS16"}, + {6, "R_ARM_ABS12"}, + {7, "R_ARM_THM_ABS5"}, + {8, "R_ARM_ABS8"}, + {9, "R_ARM_SBREL32"}, + {10, "R_ARM_THM_PC22"}, + {11, "R_ARM_THM_PC8"}, + {12, "R_ARM_AMP_VCALL9"}, + {13, "R_ARM_SWI24"}, + {14, "R_ARM_THM_SWI8"}, + {15, "R_ARM_XPC25"}, + {16, "R_ARM_THM_XPC22"}, + {20, "R_ARM_COPY"}, + {21, "R_ARM_GLOB_DAT"}, + {22, "R_ARM_JUMP_SLOT"}, + {23, "R_ARM_RELATIVE"}, + {24, "R_ARM_GOTOFF"}, + {25, "R_ARM_GOTPC"}, + {26, "R_ARM_GOT32"}, + {27, "R_ARM_PLT32"}, + {100, "R_ARM_GNU_VTENTRY"}, + {101, "R_ARM_GNU_VTINHERIT"}, + {250, "R_ARM_RSBREL32"}, + {251, "R_ARM_THM_RPC22"}, + {252, "R_ARM_RREL32"}, + {253, "R_ARM_RABS32"}, + {254, "R_ARM_RPC24"}, + {255, "R_ARM_RBASE"}, +} + +func (i R_ARM) String() string { return stringName(uint32(i), rarmStrings, false) } +func (i R_ARM) GoString() string { return stringName(uint32(i), rarmStrings, true) } + +// Relocation types for 386. +type R_386 int + +const ( + R_386_NONE R_386 = 0 /* No relocation. */ + R_386_32 R_386 = 1 /* Add symbol value. */ + R_386_PC32 R_386 = 2 /* Add PC-relative symbol value. */ + R_386_GOT32 R_386 = 3 /* Add PC-relative GOT offset. */ + R_386_PLT32 R_386 = 4 /* Add PC-relative PLT offset. */ + R_386_COPY R_386 = 5 /* Copy data from shared object. */ + R_386_GLOB_DAT R_386 = 6 /* Set GOT entry to data address. */ + R_386_JMP_SLOT R_386 = 7 /* Set GOT entry to code address. */ + R_386_RELATIVE R_386 = 8 /* Add load address of shared object. */ + R_386_GOTOFF R_386 = 9 /* Add GOT-relative symbol address. */ + R_386_GOTPC R_386 = 10 /* Add PC-relative GOT table address. */ + R_386_TLS_TPOFF R_386 = 14 /* Negative offset in static TLS block */ + R_386_TLS_IE R_386 = 15 /* Absolute address of GOT for -ve static TLS */ + R_386_TLS_GOTIE R_386 = 16 /* GOT entry for negative static TLS block */ + R_386_TLS_LE R_386 = 17 /* Negative offset relative to static TLS */ + R_386_TLS_GD R_386 = 18 /* 32 bit offset to GOT (index,off) pair */ + R_386_TLS_LDM R_386 = 19 /* 32 bit offset to GOT (index,zero) pair */ + R_386_TLS_GD_32 R_386 = 24 /* 32 bit offset to GOT (index,off) pair */ + R_386_TLS_GD_PUSH R_386 = 25 /* pushl instruction for Sun ABI GD sequence */ + R_386_TLS_GD_CALL R_386 = 26 /* call instruction for Sun ABI GD sequence */ + R_386_TLS_GD_POP R_386 = 27 /* popl instruction for Sun ABI GD sequence */ + R_386_TLS_LDM_32 R_386 = 28 /* 32 bit offset to GOT (index,zero) pair */ + R_386_TLS_LDM_PUSH R_386 = 29 /* pushl instruction for Sun ABI LD sequence */ + R_386_TLS_LDM_CALL R_386 = 30 /* call instruction for Sun ABI LD sequence */ + R_386_TLS_LDM_POP R_386 = 31 /* popl instruction for Sun ABI LD sequence */ + R_386_TLS_LDO_32 R_386 = 32 /* 32 bit offset from start of TLS block */ + R_386_TLS_IE_32 R_386 = 33 /* 32 bit offset to GOT static TLS offset entry */ + R_386_TLS_LE_32 R_386 = 34 /* 32 bit offset within static TLS block */ + R_386_TLS_DTPMOD32 R_386 = 35 /* GOT entry containing TLS index */ + R_386_TLS_DTPOFF32 R_386 = 36 /* GOT entry containing TLS offset */ + R_386_TLS_TPOFF32 R_386 = 37 /* GOT entry of -ve static TLS offset */ +) + +var r386Strings = []intName{ + {0, "R_386_NONE"}, + {1, "R_386_32"}, + {2, "R_386_PC32"}, + {3, "R_386_GOT32"}, + {4, "R_386_PLT32"}, + {5, "R_386_COPY"}, + {6, "R_386_GLOB_DAT"}, + {7, "R_386_JMP_SLOT"}, + {8, "R_386_RELATIVE"}, + {9, "R_386_GOTOFF"}, + {10, "R_386_GOTPC"}, + {14, "R_386_TLS_TPOFF"}, + {15, "R_386_TLS_IE"}, + {16, "R_386_TLS_GOTIE"}, + {17, "R_386_TLS_LE"}, + {18, "R_386_TLS_GD"}, + {19, "R_386_TLS_LDM"}, + {24, "R_386_TLS_GD_32"}, + {25, "R_386_TLS_GD_PUSH"}, + {26, "R_386_TLS_GD_CALL"}, + {27, "R_386_TLS_GD_POP"}, + {28, "R_386_TLS_LDM_32"}, + {29, "R_386_TLS_LDM_PUSH"}, + {30, "R_386_TLS_LDM_CALL"}, + {31, "R_386_TLS_LDM_POP"}, + {32, "R_386_TLS_LDO_32"}, + {33, "R_386_TLS_IE_32"}, + {34, "R_386_TLS_LE_32"}, + {35, "R_386_TLS_DTPMOD32"}, + {36, "R_386_TLS_DTPOFF32"}, + {37, "R_386_TLS_TPOFF32"}, +} + +func (i R_386) String() string { return stringName(uint32(i), r386Strings, false) } +func (i R_386) GoString() string { return stringName(uint32(i), r386Strings, true) } + +// Relocation types for PowerPC. +type R_PPC int + +const ( + R_PPC_NONE R_PPC = 0 /* No relocation. */ + R_PPC_ADDR32 R_PPC = 1 + R_PPC_ADDR24 R_PPC = 2 + R_PPC_ADDR16 R_PPC = 3 + R_PPC_ADDR16_LO R_PPC = 4 + R_PPC_ADDR16_HI R_PPC = 5 + R_PPC_ADDR16_HA R_PPC = 6 + R_PPC_ADDR14 R_PPC = 7 + R_PPC_ADDR14_BRTAKEN R_PPC = 8 + R_PPC_ADDR14_BRNTAKEN R_PPC = 9 + R_PPC_REL24 R_PPC = 10 + R_PPC_REL14 R_PPC = 11 + R_PPC_REL14_BRTAKEN R_PPC = 12 + R_PPC_REL14_BRNTAKEN R_PPC = 13 + R_PPC_GOT16 R_PPC = 14 + R_PPC_GOT16_LO R_PPC = 15 + R_PPC_GOT16_HI R_PPC = 16 + R_PPC_GOT16_HA R_PPC = 17 + R_PPC_PLTREL24 R_PPC = 18 + R_PPC_COPY R_PPC = 19 + R_PPC_GLOB_DAT R_PPC = 20 + R_PPC_JMP_SLOT R_PPC = 21 + R_PPC_RELATIVE R_PPC = 22 + R_PPC_LOCAL24PC R_PPC = 23 + R_PPC_UADDR32 R_PPC = 24 + R_PPC_UADDR16 R_PPC = 25 + R_PPC_REL32 R_PPC = 26 + R_PPC_PLT32 R_PPC = 27 + R_PPC_PLTREL32 R_PPC = 28 + R_PPC_PLT16_LO R_PPC = 29 + R_PPC_PLT16_HI R_PPC = 30 + R_PPC_PLT16_HA R_PPC = 31 + R_PPC_SDAREL16 R_PPC = 32 + R_PPC_SECTOFF R_PPC = 33 + R_PPC_SECTOFF_LO R_PPC = 34 + R_PPC_SECTOFF_HI R_PPC = 35 + R_PPC_SECTOFF_HA R_PPC = 36 + R_PPC_TLS R_PPC = 67 + R_PPC_DTPMOD32 R_PPC = 68 + R_PPC_TPREL16 R_PPC = 69 + R_PPC_TPREL16_LO R_PPC = 70 + R_PPC_TPREL16_HI R_PPC = 71 + R_PPC_TPREL16_HA R_PPC = 72 + R_PPC_TPREL32 R_PPC = 73 + R_PPC_DTPREL16 R_PPC = 74 + R_PPC_DTPREL16_LO R_PPC = 75 + R_PPC_DTPREL16_HI R_PPC = 76 + R_PPC_DTPREL16_HA R_PPC = 77 + R_PPC_DTPREL32 R_PPC = 78 + R_PPC_GOT_TLSGD16 R_PPC = 79 + R_PPC_GOT_TLSGD16_LO R_PPC = 80 + R_PPC_GOT_TLSGD16_HI R_PPC = 81 + R_PPC_GOT_TLSGD16_HA R_PPC = 82 + R_PPC_GOT_TLSLD16 R_PPC = 83 + R_PPC_GOT_TLSLD16_LO R_PPC = 84 + R_PPC_GOT_TLSLD16_HI R_PPC = 85 + R_PPC_GOT_TLSLD16_HA R_PPC = 86 + R_PPC_GOT_TPREL16 R_PPC = 87 + R_PPC_GOT_TPREL16_LO R_PPC = 88 + R_PPC_GOT_TPREL16_HI R_PPC = 89 + R_PPC_GOT_TPREL16_HA R_PPC = 90 + R_PPC_EMB_NADDR32 R_PPC = 101 + R_PPC_EMB_NADDR16 R_PPC = 102 + R_PPC_EMB_NADDR16_LO R_PPC = 103 + R_PPC_EMB_NADDR16_HI R_PPC = 104 + R_PPC_EMB_NADDR16_HA R_PPC = 105 + R_PPC_EMB_SDAI16 R_PPC = 106 + R_PPC_EMB_SDA2I16 R_PPC = 107 + R_PPC_EMB_SDA2REL R_PPC = 108 + R_PPC_EMB_SDA21 R_PPC = 109 + R_PPC_EMB_MRKREF R_PPC = 110 + R_PPC_EMB_RELSEC16 R_PPC = 111 + R_PPC_EMB_RELST_LO R_PPC = 112 + R_PPC_EMB_RELST_HI R_PPC = 113 + R_PPC_EMB_RELST_HA R_PPC = 114 + R_PPC_EMB_BIT_FLD R_PPC = 115 + R_PPC_EMB_RELSDA R_PPC = 116 +) + +var rppcStrings = []intName{ + {0, "R_PPC_NONE"}, + {1, "R_PPC_ADDR32"}, + {2, "R_PPC_ADDR24"}, + {3, "R_PPC_ADDR16"}, + {4, "R_PPC_ADDR16_LO"}, + {5, "R_PPC_ADDR16_HI"}, + {6, "R_PPC_ADDR16_HA"}, + {7, "R_PPC_ADDR14"}, + {8, "R_PPC_ADDR14_BRTAKEN"}, + {9, "R_PPC_ADDR14_BRNTAKEN"}, + {10, "R_PPC_REL24"}, + {11, "R_PPC_REL14"}, + {12, "R_PPC_REL14_BRTAKEN"}, + {13, "R_PPC_REL14_BRNTAKEN"}, + {14, "R_PPC_GOT16"}, + {15, "R_PPC_GOT16_LO"}, + {16, "R_PPC_GOT16_HI"}, + {17, "R_PPC_GOT16_HA"}, + {18, "R_PPC_PLTREL24"}, + {19, "R_PPC_COPY"}, + {20, "R_PPC_GLOB_DAT"}, + {21, "R_PPC_JMP_SLOT"}, + {22, "R_PPC_RELATIVE"}, + {23, "R_PPC_LOCAL24PC"}, + {24, "R_PPC_UADDR32"}, + {25, "R_PPC_UADDR16"}, + {26, "R_PPC_REL32"}, + {27, "R_PPC_PLT32"}, + {28, "R_PPC_PLTREL32"}, + {29, "R_PPC_PLT16_LO"}, + {30, "R_PPC_PLT16_HI"}, + {31, "R_PPC_PLT16_HA"}, + {32, "R_PPC_SDAREL16"}, + {33, "R_PPC_SECTOFF"}, + {34, "R_PPC_SECTOFF_LO"}, + {35, "R_PPC_SECTOFF_HI"}, + {36, "R_PPC_SECTOFF_HA"}, + + {67, "R_PPC_TLS"}, + {68, "R_PPC_DTPMOD32"}, + {69, "R_PPC_TPREL16"}, + {70, "R_PPC_TPREL16_LO"}, + {71, "R_PPC_TPREL16_HI"}, + {72, "R_PPC_TPREL16_HA"}, + {73, "R_PPC_TPREL32"}, + {74, "R_PPC_DTPREL16"}, + {75, "R_PPC_DTPREL16_LO"}, + {76, "R_PPC_DTPREL16_HI"}, + {77, "R_PPC_DTPREL16_HA"}, + {78, "R_PPC_DTPREL32"}, + {79, "R_PPC_GOT_TLSGD16"}, + {80, "R_PPC_GOT_TLSGD16_LO"}, + {81, "R_PPC_GOT_TLSGD16_HI"}, + {82, "R_PPC_GOT_TLSGD16_HA"}, + {83, "R_PPC_GOT_TLSLD16"}, + {84, "R_PPC_GOT_TLSLD16_LO"}, + {85, "R_PPC_GOT_TLSLD16_HI"}, + {86, "R_PPC_GOT_TLSLD16_HA"}, + {87, "R_PPC_GOT_TPREL16"}, + {88, "R_PPC_GOT_TPREL16_LO"}, + {89, "R_PPC_GOT_TPREL16_HI"}, + {90, "R_PPC_GOT_TPREL16_HA"}, + + {101, "R_PPC_EMB_NADDR32"}, + {102, "R_PPC_EMB_NADDR16"}, + {103, "R_PPC_EMB_NADDR16_LO"}, + {104, "R_PPC_EMB_NADDR16_HI"}, + {105, "R_PPC_EMB_NADDR16_HA"}, + {106, "R_PPC_EMB_SDAI16"}, + {107, "R_PPC_EMB_SDA2I16"}, + {108, "R_PPC_EMB_SDA2REL"}, + {109, "R_PPC_EMB_SDA21"}, + {110, "R_PPC_EMB_MRKREF"}, + {111, "R_PPC_EMB_RELSEC16"}, + {112, "R_PPC_EMB_RELST_LO"}, + {113, "R_PPC_EMB_RELST_HI"}, + {114, "R_PPC_EMB_RELST_HA"}, + {115, "R_PPC_EMB_BIT_FLD"}, + {116, "R_PPC_EMB_RELSDA"}, +} + +func (i R_PPC) String() string { return stringName(uint32(i), rppcStrings, false) } +func (i R_PPC) GoString() string { return stringName(uint32(i), rppcStrings, true) } + +// Relocation types for SPARC. +type R_SPARC int + +const ( + R_SPARC_NONE R_SPARC = 0 + R_SPARC_8 R_SPARC = 1 + R_SPARC_16 R_SPARC = 2 + R_SPARC_32 R_SPARC = 3 + R_SPARC_DISP8 R_SPARC = 4 + R_SPARC_DISP16 R_SPARC = 5 + R_SPARC_DISP32 R_SPARC = 6 + R_SPARC_WDISP30 R_SPARC = 7 + R_SPARC_WDISP22 R_SPARC = 8 + R_SPARC_HI22 R_SPARC = 9 + R_SPARC_22 R_SPARC = 10 + R_SPARC_13 R_SPARC = 11 + R_SPARC_LO10 R_SPARC = 12 + R_SPARC_GOT10 R_SPARC = 13 + R_SPARC_GOT13 R_SPARC = 14 + R_SPARC_GOT22 R_SPARC = 15 + R_SPARC_PC10 R_SPARC = 16 + R_SPARC_PC22 R_SPARC = 17 + R_SPARC_WPLT30 R_SPARC = 18 + R_SPARC_COPY R_SPARC = 19 + R_SPARC_GLOB_DAT R_SPARC = 20 + R_SPARC_JMP_SLOT R_SPARC = 21 + R_SPARC_RELATIVE R_SPARC = 22 + R_SPARC_UA32 R_SPARC = 23 + R_SPARC_PLT32 R_SPARC = 24 + R_SPARC_HIPLT22 R_SPARC = 25 + R_SPARC_LOPLT10 R_SPARC = 26 + R_SPARC_PCPLT32 R_SPARC = 27 + R_SPARC_PCPLT22 R_SPARC = 28 + R_SPARC_PCPLT10 R_SPARC = 29 + R_SPARC_10 R_SPARC = 30 + R_SPARC_11 R_SPARC = 31 + R_SPARC_64 R_SPARC = 32 + R_SPARC_OLO10 R_SPARC = 33 + R_SPARC_HH22 R_SPARC = 34 + R_SPARC_HM10 R_SPARC = 35 + R_SPARC_LM22 R_SPARC = 36 + R_SPARC_PC_HH22 R_SPARC = 37 + R_SPARC_PC_HM10 R_SPARC = 38 + R_SPARC_PC_LM22 R_SPARC = 39 + R_SPARC_WDISP16 R_SPARC = 40 + R_SPARC_WDISP19 R_SPARC = 41 + R_SPARC_GLOB_JMP R_SPARC = 42 + R_SPARC_7 R_SPARC = 43 + R_SPARC_5 R_SPARC = 44 + R_SPARC_6 R_SPARC = 45 + R_SPARC_DISP64 R_SPARC = 46 + R_SPARC_PLT64 R_SPARC = 47 + R_SPARC_HIX22 R_SPARC = 48 + R_SPARC_LOX10 R_SPARC = 49 + R_SPARC_H44 R_SPARC = 50 + R_SPARC_M44 R_SPARC = 51 + R_SPARC_L44 R_SPARC = 52 + R_SPARC_REGISTER R_SPARC = 53 + R_SPARC_UA64 R_SPARC = 54 + R_SPARC_UA16 R_SPARC = 55 +) + +var rsparcStrings = []intName{ + {0, "R_SPARC_NONE"}, + {1, "R_SPARC_8"}, + {2, "R_SPARC_16"}, + {3, "R_SPARC_32"}, + {4, "R_SPARC_DISP8"}, + {5, "R_SPARC_DISP16"}, + {6, "R_SPARC_DISP32"}, + {7, "R_SPARC_WDISP30"}, + {8, "R_SPARC_WDISP22"}, + {9, "R_SPARC_HI22"}, + {10, "R_SPARC_22"}, + {11, "R_SPARC_13"}, + {12, "R_SPARC_LO10"}, + {13, "R_SPARC_GOT10"}, + {14, "R_SPARC_GOT13"}, + {15, "R_SPARC_GOT22"}, + {16, "R_SPARC_PC10"}, + {17, "R_SPARC_PC22"}, + {18, "R_SPARC_WPLT30"}, + {19, "R_SPARC_COPY"}, + {20, "R_SPARC_GLOB_DAT"}, + {21, "R_SPARC_JMP_SLOT"}, + {22, "R_SPARC_RELATIVE"}, + {23, "R_SPARC_UA32"}, + {24, "R_SPARC_PLT32"}, + {25, "R_SPARC_HIPLT22"}, + {26, "R_SPARC_LOPLT10"}, + {27, "R_SPARC_PCPLT32"}, + {28, "R_SPARC_PCPLT22"}, + {29, "R_SPARC_PCPLT10"}, + {30, "R_SPARC_10"}, + {31, "R_SPARC_11"}, + {32, "R_SPARC_64"}, + {33, "R_SPARC_OLO10"}, + {34, "R_SPARC_HH22"}, + {35, "R_SPARC_HM10"}, + {36, "R_SPARC_LM22"}, + {37, "R_SPARC_PC_HH22"}, + {38, "R_SPARC_PC_HM10"}, + {39, "R_SPARC_PC_LM22"}, + {40, "R_SPARC_WDISP16"}, + {41, "R_SPARC_WDISP19"}, + {42, "R_SPARC_GLOB_JMP"}, + {43, "R_SPARC_7"}, + {44, "R_SPARC_5"}, + {45, "R_SPARC_6"}, + {46, "R_SPARC_DISP64"}, + {47, "R_SPARC_PLT64"}, + {48, "R_SPARC_HIX22"}, + {49, "R_SPARC_LOX10"}, + {50, "R_SPARC_H44"}, + {51, "R_SPARC_M44"}, + {52, "R_SPARC_L44"}, + {53, "R_SPARC_REGISTER"}, + {54, "R_SPARC_UA64"}, + {55, "R_SPARC_UA16"}, +} + +func (i R_SPARC) String() string { return stringName(uint32(i), rsparcStrings, false) } +func (i R_SPARC) GoString() string { return stringName(uint32(i), rsparcStrings, true) } + +// Magic number for the elf trampoline, chosen wisely to be an immediate value. +const ARM_MAGIC_TRAMP_NUMBER = 0x5c000003 + +// ELF32 File header. +type Header32 struct { + Ident [EI_NIDENT]byte /* File identification. */ + Type uint16 /* File type. */ + Machine uint16 /* Machine architecture. */ + Version uint32 /* ELF format version. */ + Entry uint32 /* Entry point. */ + Phoff uint32 /* Program header file offset. */ + Shoff uint32 /* Section header file offset. */ + Flags uint32 /* Architecture-specific flags. */ + Ehsize uint16 /* Size of ELF header in bytes. */ + Phentsize uint16 /* Size of program header entry. */ + Phnum uint16 /* Number of program header entries. */ + Shentsize uint16 /* Size of section header entry. */ + Shnum uint16 /* Number of section header entries. */ + Shstrndx uint16 /* Section name strings section. */ +} + +// ELF32 Section header. +type Section32 struct { + Name uint32 /* Section name (index into the section header string table). */ + Type uint32 /* Section type. */ + Flags uint32 /* Section flags. */ + Addr uint32 /* Address in memory image. */ + Off uint32 /* Offset in file. */ + Size uint32 /* Size in bytes. */ + Link uint32 /* Index of a related section. */ + Info uint32 /* Depends on section type. */ + Addralign uint32 /* Alignment in bytes. */ + Entsize uint32 /* Size of each entry in section. */ +} + +// ELF32 Program header. +type Prog32 struct { + Type uint32 /* Entry type. */ + Off uint32 /* File offset of contents. */ + Vaddr uint32 /* Virtual address in memory image. */ + Paddr uint32 /* Physical address (not used). */ + Filesz uint32 /* Size of contents in file. */ + Memsz uint32 /* Size of contents in memory. */ + Flags uint32 /* Access permission flags. */ + Align uint32 /* Alignment in memory and file. */ +} + +// ELF32 Dynamic structure. The ".dynamic" section contains an array of them. +type Dyn32 struct { + Tag int32 /* Entry type. */ + Val uint32 /* Integer/Address value. */ +} + +/* + * Relocation entries. + */ + +// ELF32 Relocations that don't need an addend field. +type Rel32 struct { + Off uint32 /* Location to be relocated. */ + Info uint32 /* Relocation type and symbol index. */ +} + +// ELF32 Relocations that need an addend field. +type Rela32 struct { + Off uint32 /* Location to be relocated. */ + Info uint32 /* Relocation type and symbol index. */ + Addend int32 /* Addend. */ +} + +func R_SYM32(info uint32) uint32 { return uint32(info >> 8) } +func R_TYPE32(info uint32) uint32 { return uint32(info & 0xff) } +func R_INFO32(sym, typ uint32) uint32 { return sym<<8 | typ } + +// ELF32 Symbol. +type Sym32 struct { + Name uint32 + Value uint32 + Size uint32 + Info uint8 + Other uint8 + Shndx uint16 +} + +const Sym32Size = 16 + +func ST_BIND(info uint8) SymBind { return SymBind(info >> 4) } +func ST_TYPE(info uint8) SymType { return SymType(info & 0xF) } +func ST_INFO(bind SymBind, typ SymType) uint8 { + return uint8(bind)<<4 | uint8(typ)&0xf +} +func ST_VISIBILITY(other uint8) SymVis { return SymVis(other & 3) } + +/* + * ELF64 + */ + +// ELF64 file header. +type Header64 struct { + Ident [EI_NIDENT]byte /* File identification. */ + Type uint16 /* File type. */ + Machine uint16 /* Machine architecture. */ + Version uint32 /* ELF format version. */ + Entry uint64 /* Entry point. */ + Phoff uint64 /* Program header file offset. */ + Shoff uint64 /* Section header file offset. */ + Flags uint32 /* Architecture-specific flags. */ + Ehsize uint16 /* Size of ELF header in bytes. */ + Phentsize uint16 /* Size of program header entry. */ + Phnum uint16 /* Number of program header entries. */ + Shentsize uint16 /* Size of section header entry. */ + Shnum uint16 /* Number of section header entries. */ + Shstrndx uint16 /* Section name strings section. */ +} + +// ELF64 Section header. +type Section64 struct { + Name uint32 /* Section name (index into the section header string table). */ + Type uint32 /* Section type. */ + Flags uint64 /* Section flags. */ + Addr uint64 /* Address in memory image. */ + Off uint64 /* Offset in file. */ + Size uint64 /* Size in bytes. */ + Link uint32 /* Index of a related section. */ + Info uint32 /* Depends on section type. */ + Addralign uint64 /* Alignment in bytes. */ + Entsize uint64 /* Size of each entry in section. */ +} + +// ELF64 Program header. +type Prog64 struct { + Type uint32 /* Entry type. */ + Flags uint32 /* Access permission flags. */ + Off uint64 /* File offset of contents. */ + Vaddr uint64 /* Virtual address in memory image. */ + Paddr uint64 /* Physical address (not used). */ + Filesz uint64 /* Size of contents in file. */ + Memsz uint64 /* Size of contents in memory. */ + Align uint64 /* Alignment in memory and file. */ +} + +// ELF64 Dynamic structure. The ".dynamic" section contains an array of them. +type Dyn64 struct { + Tag int64 /* Entry type. */ + Val uint64 /* Integer/address value */ +} + +/* + * Relocation entries. + */ + +/* ELF64 relocations that don't need an addend field. */ +type Rel64 struct { + Off uint64 /* Location to be relocated. */ + Info uint64 /* Relocation type and symbol index. */ +} + +/* ELF64 relocations that need an addend field. */ +type Rela64 struct { + Off uint64 /* Location to be relocated. */ + Info uint64 /* Relocation type and symbol index. */ + Addend int64 /* Addend. */ +} + +func R_SYM64(info uint64) uint32 { return uint32(info >> 32) } +func R_TYPE64(info uint64) uint32 { return uint32(info) } +func R_INFO(sym, typ uint32) uint64 { return uint64(sym)<<32 | uint64(typ) } + +// ELF64 symbol table entries. +type Sym64 struct { + Name uint32 /* String table index of name. */ + Info uint8 /* Type and binding information. */ + Other uint8 /* Reserved (not used). */ + Shndx uint16 /* Section index of symbol. */ + Value uint64 /* Symbol value. */ + Size uint64 /* Size of associated object. */ +} + +const Sym64Size = 24 + +type intName struct { + i uint32 + s string +} + +func stringName(i uint32, names []intName, goSyntax bool) string { + for _, n := range names { + if n.i == i { + if goSyntax { + return "elf." + n.s + } + return n.s + } + } + + // second pass - look for smaller to add with. + // assume sorted already + for j := len(names) - 1; j >= 0; j-- { + n := names[j] + if n.i < i { + s := n.s + if goSyntax { + s = "elf." + s + } + return s + "+" + strconv.FormatUint(uint64(i-n.i), 10) + } + } + + return strconv.FormatUint(uint64(i), 10) +} + +func flagName(i uint32, names []intName, goSyntax bool) string { + s := "" + for _, n := range names { + if n.i&i == n.i { + if len(s) > 0 { + s += "+" + } + if goSyntax { + s += "elf." + } + s += n.s + i -= n.i + } + } + if len(s) == 0 { + return "0x" + strconv.FormatUint(uint64(i), 16) + } + if i != 0 { + s += "+0x" + strconv.FormatUint(uint64(i), 16) + } + return s +} diff --git a/elf/elf_test.go b/elf/elf_test.go new file mode 100644 index 0000000..e3c51bb --- /dev/null +++ b/elf/elf_test.go @@ -0,0 +1,49 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package elf + +import ( + "fmt" + "testing" +) + +type nameTest struct { + val interface{} + str string +} + +var nameTests = []nameTest{ + {ELFOSABI_LINUX, "ELFOSABI_LINUX"}, + {ET_EXEC, "ET_EXEC"}, + {EM_860, "EM_860"}, + {SHN_LOPROC, "SHN_LOPROC"}, + {SHT_PROGBITS, "SHT_PROGBITS"}, + {SHF_MERGE + SHF_TLS, "SHF_MERGE+SHF_TLS"}, + {PT_LOAD, "PT_LOAD"}, + {PF_W + PF_R + 0x50, "PF_W+PF_R+0x50"}, + {DT_SYMBOLIC, "DT_SYMBOLIC"}, + {DF_BIND_NOW, "DF_BIND_NOW"}, + {NT_FPREGSET, "NT_FPREGSET"}, + {STB_GLOBAL, "STB_GLOBAL"}, + {STT_COMMON, "STT_COMMON"}, + {STV_HIDDEN, "STV_HIDDEN"}, + {R_X86_64_PC32, "R_X86_64_PC32"}, + {R_ALPHA_OP_PUSH, "R_ALPHA_OP_PUSH"}, + {R_ARM_THM_ABS5, "R_ARM_THM_ABS5"}, + {R_386_GOT32, "R_386_GOT32"}, + {R_PPC_GOT16_HI, "R_PPC_GOT16_HI"}, + {R_SPARC_GOT22, "R_SPARC_GOT22"}, + {ET_LOOS + 5, "ET_LOOS+5"}, + {ProgFlag(0x50), "0x50"}, +} + +func TestNames(t *testing.T) { + for i, tt := range nameTests { + s := fmt.Sprint(tt.val) + if s != tt.str { + t.Errorf("#%d: Sprint(%d) = %q, want %q", i, tt.val, s, tt.str) + } + } +} diff --git a/elf/file.go b/elf/file.go new file mode 100644 index 0000000..c908e7a --- /dev/null +++ b/elf/file.go @@ -0,0 +1,945 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package elf implements access to ELF object files. +package elf + +import ( + "bytes" + "debug/dwarf" + "encoding/binary" + "errors" + "fmt" + "io" + "os" +) + +// TODO: error reporting detail + +/* + * Internal ELF representation + */ + +// A FileHeader represents an ELF file header. +type FileHeader struct { + Class Class + Data Data + Version Version + OSABI OSABI + ABIVersion uint8 + ByteOrder binary.ByteOrder + Type Type + Machine Machine + Entry uint64 +} + +// A File represents an open ELF file. +type File struct { + FileHeader + Sections []*Section + Progs []*Prog + closer io.Closer + gnuNeed []verneed + gnuVersym []byte +} + +// A SectionHeader represents a single ELF section header. +type SectionHeader struct { + Name string + Type SectionType + Flags SectionFlag + Addr uint64 + Offset uint64 + Size uint64 + Link uint32 + Info uint32 + Addralign uint64 + Entsize uint64 +} + +// A Section represents a single section in an ELF file. +type Section struct { + SectionHeader + + // Embed ReaderAt for ReadAt method. + // Do not embed SectionReader directly + // to avoid having Read and Seek. + // If a client wants Read and Seek it must use + // Open() to avoid fighting over the seek offset + // with other clients. + io.ReaderAt + sr *io.SectionReader +} + +// Data reads and returns the contents of the ELF section. +func (s *Section) Data() ([]byte, error) { + dat := make([]byte, s.sr.Size()) + n, err := s.sr.ReadAt(dat, 0) + if n == len(dat) { + err = nil + } + return dat[0:n], err +} + +// stringTable reads and returns the string table given by the +// specified link value. +func (f *File) stringTable(link uint32) ([]byte, error) { + if link <= 0 || link >= uint32(len(f.Sections)) { + return nil, errors.New("section has invalid string table link") + } + return f.Sections[link].Data() +} + +// Open returns a new ReadSeeker reading the ELF section. +func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } + +// A ProgHeader represents a single ELF program header. +type ProgHeader struct { + Type ProgType + Flags ProgFlag + Off uint64 + Vaddr uint64 + Paddr uint64 + Filesz uint64 + Memsz uint64 + Align uint64 +} + +// A Prog represents a single ELF program header in an ELF binary. +type Prog struct { + ProgHeader + + // Embed ReaderAt for ReadAt method. + // Do not embed SectionReader directly + // to avoid having Read and Seek. + // If a client wants Read and Seek it must use + // Open() to avoid fighting over the seek offset + // with other clients. + io.ReaderAt + sr *io.SectionReader +} + +// Open returns a new ReadSeeker reading the ELF program body. +func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63-1) } + +// A Symbol represents an entry in an ELF symbol table section. +type Symbol struct { + Name string + Info, Other byte + Section SectionIndex + Value, Size uint64 +} + +/* + * ELF reader + */ + +type FormatError struct { + off int64 + msg string + val interface{} +} + +func (e *FormatError) Error() string { + msg := e.msg + if e.val != nil { + msg += fmt.Sprintf(" '%v' ", e.val) + } + msg += fmt.Sprintf("in record at byte %#x", e.off) + return msg +} + +// Open opens the named file using os.Open and prepares it for use as an ELF binary. +func Open(name string) (*File, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + ff, err := NewFile(f) + if err != nil { + f.Close() + return nil, err + } + ff.closer = f + return ff, nil +} + +// Close closes the File. +// If the File was created using NewFile directly instead of Open, +// Close has no effect. +func (f *File) Close() error { + var err error + if f.closer != nil { + err = f.closer.Close() + f.closer = nil + } + return err +} + +// SectionByType returns the first section in f with the +// given type, or nil if there is no such section. +func (f *File) SectionByType(typ SectionType) *Section { + for _, s := range f.Sections { + if s.Type == typ { + return s + } + } + return nil +} + +// NewFile creates a new File for accessing an ELF binary in an underlying reader. +// The ELF binary is expected to start at position 0 in the ReaderAt. +func NewFile(r io.ReaderAt) (*File, error) { + sr := io.NewSectionReader(r, 0, 1<<63-1) + // Read and decode ELF identifier + var ident [16]uint8 + if _, err := r.ReadAt(ident[0:], 0); err != nil { + return nil, err + } + if ident[0] != '\x7f' || ident[1] != 'E' || ident[2] != 'L' || ident[3] != 'F' { + return nil, &FormatError{0, "bad magic number", ident[0:4]} + } + + f := new(File) + f.Class = Class(ident[EI_CLASS]) + switch f.Class { + case ELFCLASS32: + case ELFCLASS64: + // ok + default: + return nil, &FormatError{0, "unknown ELF class", f.Class} + } + + f.Data = Data(ident[EI_DATA]) + switch f.Data { + case ELFDATA2LSB: + f.ByteOrder = binary.LittleEndian + case ELFDATA2MSB: + f.ByteOrder = binary.BigEndian + default: + return nil, &FormatError{0, "unknown ELF data encoding", f.Data} + } + + f.Version = Version(ident[EI_VERSION]) + if f.Version != EV_CURRENT { + return nil, &FormatError{0, "unknown ELF version", f.Version} + } + + f.OSABI = OSABI(ident[EI_OSABI]) + f.ABIVersion = ident[EI_ABIVERSION] + + // Read ELF file header + var phoff int64 + var phentsize, phnum int + var shoff int64 + var shentsize, shnum, shstrndx int + shstrndx = -1 + switch f.Class { + case ELFCLASS32: + hdr := new(Header32) + sr.Seek(0, os.SEEK_SET) + if err := binary.Read(sr, f.ByteOrder, hdr); err != nil { + return nil, err + } + f.Type = Type(hdr.Type) + f.Machine = Machine(hdr.Machine) + f.Entry = uint64(hdr.Entry) + if v := Version(hdr.Version); v != f.Version { + return nil, &FormatError{0, "mismatched ELF version", v} + } + phoff = int64(hdr.Phoff) + phentsize = int(hdr.Phentsize) + phnum = int(hdr.Phnum) + shoff = int64(hdr.Shoff) + shentsize = int(hdr.Shentsize) + shnum = int(hdr.Shnum) + shstrndx = int(hdr.Shstrndx) + case ELFCLASS64: + hdr := new(Header64) + sr.Seek(0, os.SEEK_SET) + if err := binary.Read(sr, f.ByteOrder, hdr); err != nil { + return nil, err + } + f.Type = Type(hdr.Type) + f.Machine = Machine(hdr.Machine) + f.Entry = uint64(hdr.Entry) + if v := Version(hdr.Version); v != f.Version { + return nil, &FormatError{0, "mismatched ELF version", v} + } + phoff = int64(hdr.Phoff) + phentsize = int(hdr.Phentsize) + phnum = int(hdr.Phnum) + shoff = int64(hdr.Shoff) + shentsize = int(hdr.Shentsize) + shnum = int(hdr.Shnum) + shstrndx = int(hdr.Shstrndx) + } + + if shnum > 0 && shoff > 0 && (shstrndx < 0 || shstrndx >= shnum) { + return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx} + } + + // Read program headers + f.Progs = make([]*Prog, phnum) + for i := 0; i < phnum; i++ { + off := phoff + int64(i)*int64(phentsize) + sr.Seek(off, os.SEEK_SET) + p := new(Prog) + switch f.Class { + case ELFCLASS32: + ph := new(Prog32) + if err := binary.Read(sr, f.ByteOrder, ph); err != nil { + return nil, err + } + p.ProgHeader = ProgHeader{ + Type: ProgType(ph.Type), + Flags: ProgFlag(ph.Flags), + Off: uint64(ph.Off), + Vaddr: uint64(ph.Vaddr), + Paddr: uint64(ph.Paddr), + Filesz: uint64(ph.Filesz), + Memsz: uint64(ph.Memsz), + Align: uint64(ph.Align), + } + case ELFCLASS64: + ph := new(Prog64) + if err := binary.Read(sr, f.ByteOrder, ph); err != nil { + return nil, err + } + p.ProgHeader = ProgHeader{ + Type: ProgType(ph.Type), + Flags: ProgFlag(ph.Flags), + Off: uint64(ph.Off), + Vaddr: uint64(ph.Vaddr), + Paddr: uint64(ph.Paddr), + Filesz: uint64(ph.Filesz), + Memsz: uint64(ph.Memsz), + Align: uint64(ph.Align), + } + } + p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz)) + p.ReaderAt = p.sr + f.Progs[i] = p + } + + // Read section headers + f.Sections = make([]*Section, shnum) + names := make([]uint32, shnum) + for i := 0; i < shnum; i++ { + off := shoff + int64(i)*int64(shentsize) + sr.Seek(off, os.SEEK_SET) + s := new(Section) + switch f.Class { + case ELFCLASS32: + sh := new(Section32) + if err := binary.Read(sr, f.ByteOrder, sh); err != nil { + return nil, err + } + names[i] = sh.Name + s.SectionHeader = SectionHeader{ + Type: SectionType(sh.Type), + Flags: SectionFlag(sh.Flags), + Addr: uint64(sh.Addr), + Offset: uint64(sh.Off), + Size: uint64(sh.Size), + Link: uint32(sh.Link), + Info: uint32(sh.Info), + Addralign: uint64(sh.Addralign), + Entsize: uint64(sh.Entsize), + } + case ELFCLASS64: + sh := new(Section64) + if err := binary.Read(sr, f.ByteOrder, sh); err != nil { + return nil, err + } + names[i] = sh.Name + s.SectionHeader = SectionHeader{ + Type: SectionType(sh.Type), + Flags: SectionFlag(sh.Flags), + Offset: uint64(sh.Off), + Size: uint64(sh.Size), + Addr: uint64(sh.Addr), + Link: uint32(sh.Link), + Info: uint32(sh.Info), + Addralign: uint64(sh.Addralign), + Entsize: uint64(sh.Entsize), + } + } + s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Size)) + s.ReaderAt = s.sr + f.Sections[i] = s + } + + if len(f.Sections) == 0 { + return f, nil + } + + // Load section header string table. + shstrtab, err := f.Sections[shstrndx].Data() + if err != nil { + return nil, err + } + for i, s := range f.Sections { + var ok bool + s.Name, ok = getString(shstrtab, int(names[i])) + if !ok { + return nil, &FormatError{shoff + int64(i*shentsize), "bad section name index", names[i]} + } + } + + return f, nil +} + +// getSymbols returns a slice of Symbols from parsing the symbol table +// with the given type, along with the associated string table. +func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, error) { + switch f.Class { + case ELFCLASS64: + return f.getSymbols64(typ) + + case ELFCLASS32: + return f.getSymbols32(typ) + } + + return nil, nil, errors.New("not implemented") +} + +// ErrNoSymbols is returned by File.Symbols and File.DynamicSymbols +// if there is no such section in the File. +var ErrNoSymbols = errors.New("no symbol section") + +func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) { + symtabSection := f.SectionByType(typ) + if symtabSection == nil { + return nil, nil, ErrNoSymbols + } + + data, err := symtabSection.Data() + if err != nil { + return nil, nil, errors.New("cannot load symbol section") + } + symtab := bytes.NewReader(data) + if symtab.Len()%Sym32Size != 0 { + return nil, nil, errors.New("length of symbol section is not a multiple of SymSize") + } + + strdata, err := f.stringTable(symtabSection.Link) + if err != nil { + return nil, nil, errors.New("cannot load string table section") + } + + // The first entry is all zeros. + var skip [Sym32Size]byte + symtab.Read(skip[:]) + + symbols := make([]Symbol, symtab.Len()/Sym32Size) + + i := 0 + var sym Sym32 + for symtab.Len() > 0 { + binary.Read(symtab, f.ByteOrder, &sym) + str, _ := getString(strdata, int(sym.Name)) + symbols[i].Name = str + symbols[i].Info = sym.Info + symbols[i].Other = sym.Other + symbols[i].Section = SectionIndex(sym.Shndx) + symbols[i].Value = uint64(sym.Value) + symbols[i].Size = uint64(sym.Size) + i++ + } + + return symbols, strdata, nil +} + +func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) { + symtabSection := f.SectionByType(typ) + if symtabSection == nil { + return nil, nil, ErrNoSymbols + } + + data, err := symtabSection.Data() + if err != nil { + return nil, nil, errors.New("cannot load symbol section") + } + symtab := bytes.NewReader(data) + if symtab.Len()%Sym64Size != 0 { + return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size") + } + + strdata, err := f.stringTable(symtabSection.Link) + if err != nil { + return nil, nil, errors.New("cannot load string table section") + } + + // The first entry is all zeros. + var skip [Sym64Size]byte + symtab.Read(skip[:]) + + symbols := make([]Symbol, symtab.Len()/Sym64Size) + + i := 0 + var sym Sym64 + for symtab.Len() > 0 { + binary.Read(symtab, f.ByteOrder, &sym) + str, _ := getString(strdata, int(sym.Name)) + symbols[i].Name = str + symbols[i].Info = sym.Info + symbols[i].Other = sym.Other + symbols[i].Section = SectionIndex(sym.Shndx) + symbols[i].Value = sym.Value + symbols[i].Size = sym.Size + i++ + } + + return symbols, strdata, nil +} + +// getString extracts a string from an ELF string table. +func getString(section []byte, start int) (string, bool) { + if start < 0 || start >= len(section) { + return "", false + } + + for end := start; end < len(section); end++ { + if section[end] == 0 { + return string(section[start:end]), true + } + } + return "", false +} + +// Section returns a section with the given name, or nil if no such +// section exists. +func (f *File) Section(name string) *Section { + for _, s := range f.Sections { + if s.Name == name { + return s + } + } + return nil +} + +// applyRelocations applies relocations to dst. rels is a relocations section +// in RELA format. +func (f *File) applyRelocations(dst []byte, rels []byte) error { + if f.Class == ELFCLASS64 && f.Machine == EM_X86_64 { + return f.applyRelocationsAMD64(dst, rels) + } + if f.Class == ELFCLASS32 && f.Machine == EM_386 { + return f.applyRelocations386(dst, rels) + } + if f.Class == ELFCLASS64 && f.Machine == EM_AARCH64 { + return f.applyRelocationsARM64(dst, rels) + } + + return errors.New("not implemented") +} + +func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error { + // 24 is the size of Rela64. + if len(rels)%24 != 0 { + return errors.New("length of relocation section is not a multiple of 24") + } + + symbols, _, err := f.getSymbols(SHT_SYMTAB) + if err != nil { + return err + } + + b := bytes.NewReader(rels) + var rela Rela64 + + for b.Len() > 0 { + binary.Read(b, f.ByteOrder, &rela) + symNo := rela.Info >> 32 + t := R_X86_64(rela.Info & 0xffff) + + if symNo == 0 || symNo > uint64(len(symbols)) { + continue + } + sym := &symbols[symNo-1] + if SymType(sym.Info&0xf) != STT_SECTION { + // We don't handle non-section relocations for now. + continue + } + + switch t { + case R_X86_64_64: + if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend)) + case R_X86_64_32: + if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend)) + } + } + + return nil +} + +func (f *File) applyRelocations386(dst []byte, rels []byte) error { + // 8 is the size of Rel32. + if len(rels)%8 != 0 { + return errors.New("length of relocation section is not a multiple of 8") + } + + symbols, _, err := f.getSymbols(SHT_SYMTAB) + if err != nil { + return err + } + + b := bytes.NewReader(rels) + var rel Rel32 + + for b.Len() > 0 { + binary.Read(b, f.ByteOrder, &rel) + symNo := rel.Info >> 8 + t := R_386(rel.Info & 0xff) + + if symNo == 0 || symNo > uint32(len(symbols)) { + continue + } + sym := &symbols[symNo-1] + + if t == R_386_32 { + if rel.Off+4 >= uint32(len(dst)) { + continue + } + val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4]) + val += uint32(sym.Value) + f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val) + } + } + + return nil +} + +func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error { + // 24 is the size of Rela64. + if len(rels)%24 != 0 { + return errors.New("length of relocation section is not a multiple of 24") + } + + symbols, _, err := f.getSymbols(SHT_SYMTAB) + if err != nil { + return err + } + + b := bytes.NewReader(rels) + var rela Rela64 + + for b.Len() > 0 { + binary.Read(b, f.ByteOrder, &rela) + symNo := rela.Info >> 32 + t := R_AARCH64(rela.Info & 0xffff) + + if symNo == 0 || symNo > uint64(len(symbols)) { + continue + } + sym := &symbols[symNo-1] + if SymType(sym.Info&0xf) != STT_SECTION { + // We don't handle non-section relocations for now. + continue + } + + switch t { + case R_AARCH64_ABS64: + if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend)) + case R_AARCH64_ABS32: + if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend)) + } + } + + return nil +} + +func (f *File) DWARF() (*dwarf.Data, error) { + // There are many other DWARF sections, but these + // are the required ones, and the debug/dwarf package + // does not use the others, so don't bother loading them. + var names = [...]string{"abbrev", "info", "str"} + var dat [len(names)][]byte + for i, name := range names { + name = ".debug_" + name + s := f.Section(name) + if s == nil { + continue + } + b, err := s.Data() + if err != nil && uint64(len(b)) < s.Size { + return nil, err + } + dat[i] = b + } + + // If there's a relocation table for .debug_info, we have to process it + // now otherwise the data in .debug_info is invalid for x86-64 objects. + rela := f.Section(".rela.debug_info") + if rela != nil && rela.Type == SHT_RELA && (f.Machine == EM_X86_64 || f.Machine == EM_AARCH64) { + data, err := rela.Data() + if err != nil { + return nil, err + } + err = f.applyRelocations(dat[1], data) + if err != nil { + return nil, err + } + } + + // When using clang we need to process relocations even for 386. + rel := f.Section(".rel.debug_info") + if rel != nil && rel.Type == SHT_REL && f.Machine == EM_386 { + data, err := rel.Data() + if err != nil { + return nil, err + } + err = f.applyRelocations(dat[1], data) + if err != nil { + return nil, err + } + } + + abbrev, info, str := dat[0], dat[1], dat[2] + d, err := dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str) + if err != nil { + return nil, err + } + + // Look for DWARF4 .debug_types sections. + for i, s := range f.Sections { + if s.Name == ".debug_types" { + b, err := s.Data() + if err != nil && uint64(len(b)) < s.Size { + return nil, err + } + + for _, r := range f.Sections { + if r.Type != SHT_RELA && r.Type != SHT_REL { + continue + } + if int(r.Info) != i { + continue + } + rd, err := r.Data() + if err != nil { + return nil, err + } + err = f.applyRelocations(b, rd) + if err != nil { + return nil, err + } + } + + err = d.AddTypes(fmt.Sprintf("types-%d", i), b) + if err != nil { + return nil, err + } + } + } + + return d, nil +} + +// Symbols returns the symbol table for f. The symbols will be listed in the order +// they appear in f. +// +// For compatibility with Go 1.0, Symbols omits the null symbol at index 0. +// After retrieving the symbols as symtab, an externally supplied index x +// corresponds to symtab[x-1], not symtab[x]. +func (f *File) Symbols() ([]Symbol, error) { + sym, _, err := f.getSymbols(SHT_SYMTAB) + return sym, err +} + +// DynamicSymbols returns the dynamic symbol table for f. The symbols +// will be listed in the order they appear in f. +// +// For compatibility with Symbols, DynamicSymbols omits the null symbol at index 0. +// After retrieving the symbols as symtab, an externally supplied index x +// corresponds to symtab[x-1], not symtab[x]. +func (f *File) DynamicSymbols() ([]Symbol, error) { + sym, _, err := f.getSymbols(SHT_DYNSYM) + return sym, err +} + +type ImportedSymbol struct { + Name string + Version string + Library string +} + +// ImportedSymbols returns the names of all symbols +// referred to by the binary f that are expected to be +// satisfied by other libraries at dynamic load time. +// It does not return weak symbols. +func (f *File) ImportedSymbols() ([]ImportedSymbol, error) { + sym, str, err := f.getSymbols(SHT_DYNSYM) + if err != nil { + return nil, err + } + f.gnuVersionInit(str) + var all []ImportedSymbol + for i, s := range sym { + if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF { + all = append(all, ImportedSymbol{Name: s.Name}) + f.gnuVersion(i, &all[len(all)-1]) + } + } + return all, nil +} + +type verneed struct { + File string + Name string +} + +// gnuVersionInit parses the GNU version tables +// for use by calls to gnuVersion. +func (f *File) gnuVersionInit(str []byte) { + // Accumulate verneed information. + vn := f.SectionByType(SHT_GNU_VERNEED) + if vn == nil { + return + } + d, _ := vn.Data() + + var need []verneed + i := 0 + for { + if i+16 > len(d) { + break + } + vers := f.ByteOrder.Uint16(d[i : i+2]) + if vers != 1 { + break + } + cnt := f.ByteOrder.Uint16(d[i+2 : i+4]) + fileoff := f.ByteOrder.Uint32(d[i+4 : i+8]) + aux := f.ByteOrder.Uint32(d[i+8 : i+12]) + next := f.ByteOrder.Uint32(d[i+12 : i+16]) + file, _ := getString(str, int(fileoff)) + + var name string + j := i + int(aux) + for c := 0; c < int(cnt); c++ { + if j+16 > len(d) { + break + } + // hash := f.ByteOrder.Uint32(d[j:j+4]) + // flags := f.ByteOrder.Uint16(d[j+4:j+6]) + other := f.ByteOrder.Uint16(d[j+6 : j+8]) + nameoff := f.ByteOrder.Uint32(d[j+8 : j+12]) + next := f.ByteOrder.Uint32(d[j+12 : j+16]) + name, _ = getString(str, int(nameoff)) + ndx := int(other) + if ndx >= len(need) { + a := make([]verneed, 2*(ndx+1)) + copy(a, need) + need = a + } + + need[ndx] = verneed{file, name} + if next == 0 { + break + } + j += int(next) + } + + if next == 0 { + break + } + i += int(next) + } + + // Versym parallels symbol table, indexing into verneed. + vs := f.SectionByType(SHT_GNU_VERSYM) + if vs == nil { + return + } + d, _ = vs.Data() + + f.gnuNeed = need + f.gnuVersym = d +} + +// gnuVersion adds Library and Version information to sym, +// which came from offset i of the symbol table. +func (f *File) gnuVersion(i int, sym *ImportedSymbol) { + // Each entry is two bytes. + i = (i + 1) * 2 + if i >= len(f.gnuVersym) { + return + } + j := int(f.ByteOrder.Uint16(f.gnuVersym[i:])) + if j < 2 || j >= len(f.gnuNeed) { + return + } + n := &f.gnuNeed[j] + sym.Library = n.File + sym.Version = n.Name +} + +// ImportedLibraries returns the names of all libraries +// referred to by the binary f that are expected to be +// linked with the binary at dynamic link time. +func (f *File) ImportedLibraries() ([]string, error) { + return f.DynString(DT_NEEDED) +} + +// DynString returns the strings listed for the given tag in the file's dynamic +// section. +// +// The tag must be one that takes string values: DT_NEEDED, DT_SONAME, DT_RPATH, or +// DT_RUNPATH. +func (f *File) DynString(tag DynTag) ([]string, error) { + switch tag { + case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH: + default: + return nil, fmt.Errorf("non-string-valued tag %v", tag) + } + ds := f.SectionByType(SHT_DYNAMIC) + if ds == nil { + // not dynamic, so no libraries + return nil, nil + } + d, err := ds.Data() + if err != nil { + return nil, err + } + str, err := f.stringTable(ds.Link) + if err != nil { + return nil, err + } + var all []string + for len(d) > 0 { + var t DynTag + var v uint64 + switch f.Class { + case ELFCLASS32: + t = DynTag(f.ByteOrder.Uint32(d[0:4])) + v = uint64(f.ByteOrder.Uint32(d[4:8])) + d = d[8:] + case ELFCLASS64: + t = DynTag(f.ByteOrder.Uint64(d[0:8])) + v = f.ByteOrder.Uint64(d[8:16]) + d = d[16:] + } + if t == tag { + s, ok := getString(str, int(v)) + if ok { + all = append(all, s) + } + } + } + return all, nil +} diff --git a/elf/file_test.go b/elf/file_test.go new file mode 100644 index 0000000..5e5ba52 --- /dev/null +++ b/elf/file_test.go @@ -0,0 +1,345 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package elf + +import ( + "bytes" + "compress/gzip" + "debug/dwarf" + "encoding/binary" + "io" + "net" + "os" + "path" + "reflect" + "runtime" + "testing" +) + +type fileTest struct { + file string + hdr FileHeader + sections []SectionHeader + progs []ProgHeader + needed []string +} + +var fileTests = []fileTest{ + { + "testdata/gcc-386-freebsd-exec", + FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386, 0x80483cc}, + []SectionHeader{ + {"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {".interp", SHT_PROGBITS, SHF_ALLOC, 0x80480d4, 0xd4, 0x15, 0x0, 0x0, 0x1, 0x0}, + {".hash", SHT_HASH, SHF_ALLOC, 0x80480ec, 0xec, 0x90, 0x3, 0x0, 0x4, 0x4}, + {".dynsym", SHT_DYNSYM, SHF_ALLOC, 0x804817c, 0x17c, 0x110, 0x4, 0x1, 0x4, 0x10}, + {".dynstr", SHT_STRTAB, SHF_ALLOC, 0x804828c, 0x28c, 0xbb, 0x0, 0x0, 0x1, 0x0}, + {".rel.plt", SHT_REL, SHF_ALLOC, 0x8048348, 0x348, 0x20, 0x3, 0x7, 0x4, 0x8}, + {".init", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x8048368, 0x368, 0x11, 0x0, 0x0, 0x4, 0x0}, + {".plt", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x804837c, 0x37c, 0x50, 0x0, 0x0, 0x4, 0x4}, + {".text", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x80483cc, 0x3cc, 0x180, 0x0, 0x0, 0x4, 0x0}, + {".fini", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x804854c, 0x54c, 0xc, 0x0, 0x0, 0x4, 0x0}, + {".rodata", SHT_PROGBITS, SHF_ALLOC, 0x8048558, 0x558, 0xa3, 0x0, 0x0, 0x1, 0x0}, + {".data", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80495fc, 0x5fc, 0xc, 0x0, 0x0, 0x4, 0x0}, + {".eh_frame", SHT_PROGBITS, SHF_ALLOC, 0x8049608, 0x608, 0x4, 0x0, 0x0, 0x4, 0x0}, + {".dynamic", SHT_DYNAMIC, SHF_WRITE + SHF_ALLOC, 0x804960c, 0x60c, 0x98, 0x4, 0x0, 0x4, 0x8}, + {".ctors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496a4, 0x6a4, 0x8, 0x0, 0x0, 0x4, 0x0}, + {".dtors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496ac, 0x6ac, 0x8, 0x0, 0x0, 0x4, 0x0}, + {".jcr", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496b4, 0x6b4, 0x4, 0x0, 0x0, 0x4, 0x0}, + {".got", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496b8, 0x6b8, 0x1c, 0x0, 0x0, 0x4, 0x4}, + {".bss", SHT_NOBITS, SHF_WRITE + SHF_ALLOC, 0x80496d4, 0x6d4, 0x20, 0x0, 0x0, 0x4, 0x0}, + {".comment", SHT_PROGBITS, 0x0, 0x0, 0x6d4, 0x12d, 0x0, 0x0, 0x1, 0x0}, + {".debug_aranges", SHT_PROGBITS, 0x0, 0x0, 0x801, 0x20, 0x0, 0x0, 0x1, 0x0}, + {".debug_pubnames", SHT_PROGBITS, 0x0, 0x0, 0x821, 0x1b, 0x0, 0x0, 0x1, 0x0}, + {".debug_info", SHT_PROGBITS, 0x0, 0x0, 0x83c, 0x11d, 0x0, 0x0, 0x1, 0x0}, + {".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0x959, 0x41, 0x0, 0x0, 0x1, 0x0}, + {".debug_line", SHT_PROGBITS, 0x0, 0x0, 0x99a, 0x35, 0x0, 0x0, 0x1, 0x0}, + {".debug_frame", SHT_PROGBITS, 0x0, 0x0, 0x9d0, 0x30, 0x0, 0x0, 0x4, 0x0}, + {".debug_str", SHT_PROGBITS, 0x0, 0x0, 0xa00, 0xd, 0x0, 0x0, 0x1, 0x0}, + {".shstrtab", SHT_STRTAB, 0x0, 0x0, 0xa0d, 0xf8, 0x0, 0x0, 0x1, 0x0}, + {".symtab", SHT_SYMTAB, 0x0, 0x0, 0xfb8, 0x4b0, 0x1d, 0x38, 0x4, 0x10}, + {".strtab", SHT_STRTAB, 0x0, 0x0, 0x1468, 0x206, 0x0, 0x0, 0x1, 0x0}, + }, + []ProgHeader{ + {PT_PHDR, PF_R + PF_X, 0x34, 0x8048034, 0x8048034, 0xa0, 0xa0, 0x4}, + {PT_INTERP, PF_R, 0xd4, 0x80480d4, 0x80480d4, 0x15, 0x15, 0x1}, + {PT_LOAD, PF_R + PF_X, 0x0, 0x8048000, 0x8048000, 0x5fb, 0x5fb, 0x1000}, + {PT_LOAD, PF_R + PF_W, 0x5fc, 0x80495fc, 0x80495fc, 0xd8, 0xf8, 0x1000}, + {PT_DYNAMIC, PF_R + PF_W, 0x60c, 0x804960c, 0x804960c, 0x98, 0x98, 0x4}, + }, + []string{"libc.so.6"}, + }, + { + "testdata/gcc-amd64-linux-exec", + FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64, 0x4003e0}, + []SectionHeader{ + {"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {".interp", SHT_PROGBITS, SHF_ALLOC, 0x400200, 0x200, 0x1c, 0x0, 0x0, 0x1, 0x0}, + {".note.ABI-tag", SHT_NOTE, SHF_ALLOC, 0x40021c, 0x21c, 0x20, 0x0, 0x0, 0x4, 0x0}, + {".hash", SHT_HASH, SHF_ALLOC, 0x400240, 0x240, 0x24, 0x5, 0x0, 0x8, 0x4}, + {".gnu.hash", SHT_LOOS + 268435446, SHF_ALLOC, 0x400268, 0x268, 0x1c, 0x5, 0x0, 0x8, 0x0}, + {".dynsym", SHT_DYNSYM, SHF_ALLOC, 0x400288, 0x288, 0x60, 0x6, 0x1, 0x8, 0x18}, + {".dynstr", SHT_STRTAB, SHF_ALLOC, 0x4002e8, 0x2e8, 0x3d, 0x0, 0x0, 0x1, 0x0}, + {".gnu.version", SHT_HIOS, SHF_ALLOC, 0x400326, 0x326, 0x8, 0x5, 0x0, 0x2, 0x2}, + {".gnu.version_r", SHT_LOOS + 268435454, SHF_ALLOC, 0x400330, 0x330, 0x20, 0x6, 0x1, 0x8, 0x0}, + {".rela.dyn", SHT_RELA, SHF_ALLOC, 0x400350, 0x350, 0x18, 0x5, 0x0, 0x8, 0x18}, + {".rela.plt", SHT_RELA, SHF_ALLOC, 0x400368, 0x368, 0x30, 0x5, 0xc, 0x8, 0x18}, + {".init", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x400398, 0x398, 0x18, 0x0, 0x0, 0x4, 0x0}, + {".plt", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x4003b0, 0x3b0, 0x30, 0x0, 0x0, 0x4, 0x10}, + {".text", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x4003e0, 0x3e0, 0x1b4, 0x0, 0x0, 0x10, 0x0}, + {".fini", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x400594, 0x594, 0xe, 0x0, 0x0, 0x4, 0x0}, + {".rodata", SHT_PROGBITS, SHF_ALLOC, 0x4005a4, 0x5a4, 0x11, 0x0, 0x0, 0x4, 0x0}, + {".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, 0x4005b8, 0x5b8, 0x24, 0x0, 0x0, 0x4, 0x0}, + {".eh_frame", SHT_PROGBITS, SHF_ALLOC, 0x4005e0, 0x5e0, 0xa4, 0x0, 0x0, 0x8, 0x0}, + {".ctors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600688, 0x688, 0x10, 0x0, 0x0, 0x8, 0x0}, + {".dtors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600698, 0x698, 0x10, 0x0, 0x0, 0x8, 0x0}, + {".jcr", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x6006a8, 0x6a8, 0x8, 0x0, 0x0, 0x8, 0x0}, + {".dynamic", SHT_DYNAMIC, SHF_WRITE + SHF_ALLOC, 0x6006b0, 0x6b0, 0x1a0, 0x6, 0x0, 0x8, 0x10}, + {".got", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600850, 0x850, 0x8, 0x0, 0x0, 0x8, 0x8}, + {".got.plt", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600858, 0x858, 0x28, 0x0, 0x0, 0x8, 0x8}, + {".data", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600880, 0x880, 0x18, 0x0, 0x0, 0x8, 0x0}, + {".bss", SHT_NOBITS, SHF_WRITE + SHF_ALLOC, 0x600898, 0x898, 0x8, 0x0, 0x0, 0x4, 0x0}, + {".comment", SHT_PROGBITS, 0x0, 0x0, 0x898, 0x126, 0x0, 0x0, 0x1, 0x0}, + {".debug_aranges", SHT_PROGBITS, 0x0, 0x0, 0x9c0, 0x90, 0x0, 0x0, 0x10, 0x0}, + {".debug_pubnames", SHT_PROGBITS, 0x0, 0x0, 0xa50, 0x25, 0x0, 0x0, 0x1, 0x0}, + {".debug_info", SHT_PROGBITS, 0x0, 0x0, 0xa75, 0x1a7, 0x0, 0x0, 0x1, 0x0}, + {".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0xc1c, 0x6f, 0x0, 0x0, 0x1, 0x0}, + {".debug_line", SHT_PROGBITS, 0x0, 0x0, 0xc8b, 0x13f, 0x0, 0x0, 0x1, 0x0}, + {".debug_str", SHT_PROGBITS, SHF_MERGE + SHF_STRINGS, 0x0, 0xdca, 0xb1, 0x0, 0x0, 0x1, 0x1}, + {".debug_ranges", SHT_PROGBITS, 0x0, 0x0, 0xe80, 0x90, 0x0, 0x0, 0x10, 0x0}, + {".shstrtab", SHT_STRTAB, 0x0, 0x0, 0xf10, 0x149, 0x0, 0x0, 0x1, 0x0}, + {".symtab", SHT_SYMTAB, 0x0, 0x0, 0x19a0, 0x6f0, 0x24, 0x39, 0x8, 0x18}, + {".strtab", SHT_STRTAB, 0x0, 0x0, 0x2090, 0x1fc, 0x0, 0x0, 0x1, 0x0}, + }, + []ProgHeader{ + {PT_PHDR, PF_R + PF_X, 0x40, 0x400040, 0x400040, 0x1c0, 0x1c0, 0x8}, + {PT_INTERP, PF_R, 0x200, 0x400200, 0x400200, 0x1c, 0x1c, 1}, + {PT_LOAD, PF_R + PF_X, 0x0, 0x400000, 0x400000, 0x684, 0x684, 0x200000}, + {PT_LOAD, PF_R + PF_W, 0x688, 0x600688, 0x600688, 0x210, 0x218, 0x200000}, + {PT_DYNAMIC, PF_R + PF_W, 0x6b0, 0x6006b0, 0x6006b0, 0x1a0, 0x1a0, 0x8}, + {PT_NOTE, PF_R, 0x21c, 0x40021c, 0x40021c, 0x20, 0x20, 0x4}, + {PT_LOOS + 0x474E550, PF_R, 0x5b8, 0x4005b8, 0x4005b8, 0x24, 0x24, 0x4}, + {PT_LOOS + 0x474E551, PF_R + PF_W, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8}, + }, + []string{"libc.so.6"}, + }, + { + "testdata/hello-world-core.gz", + FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0x0, binary.LittleEndian, ET_CORE, EM_X86_64, 0x0}, + []SectionHeader{}, + []ProgHeader{ + {Type: PT_NOTE, Flags: 0x0, Off: 0x3f8, Vaddr: 0x0, Paddr: 0x0, Filesz: 0x8ac, Memsz: 0x0, Align: 0x0}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x1000, Vaddr: 0x400000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_R, Off: 0x1000, Vaddr: 0x401000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x2000, Vaddr: 0x402000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3000, Vaddr: 0x7f54078b8000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1b5000, Align: 0x1000}, + {Type: PT_LOAD, Flags: 0x0, Off: 0x3000, Vaddr: 0x7f5407a6d000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1ff000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_R, Off: 0x3000, Vaddr: 0x7f5407c6c000, Paddr: 0x0, Filesz: 0x4000, Memsz: 0x4000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x7000, Vaddr: 0x7f5407c70000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x9000, Vaddr: 0x7f5407c72000, Paddr: 0x0, Filesz: 0x5000, Memsz: 0x5000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0xe000, Vaddr: 0x7f5407c77000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x22000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0xe000, Vaddr: 0x7f5407e81000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x11000, Vaddr: 0x7f5407e96000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_R, Off: 0x14000, Vaddr: 0x7f5407e99000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x15000, Vaddr: 0x7f5407e9a000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x17000, Vaddr: 0x7fff79972000, Paddr: 0x0, Filesz: 0x23000, Memsz: 0x23000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3a000, Vaddr: 0x7fff799f8000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3b000, Vaddr: 0xffffffffff600000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + }, + nil, + }, +} + +func TestOpen(t *testing.T) { + for i := range fileTests { + tt := &fileTests[i] + + var f *File + var err error + if path.Ext(tt.file) == ".gz" { + var r io.ReaderAt + if r, err = decompress(tt.file); err == nil { + f, err = NewFile(r) + } + } else { + f, err = Open(tt.file) + } + if err != nil { + t.Errorf("cannot open file %s: %v", tt.file, err) + continue + } + defer f.Close() + if !reflect.DeepEqual(f.FileHeader, tt.hdr) { + t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr) + continue + } + for i, s := range f.Sections { + if i >= len(tt.sections) { + break + } + sh := &tt.sections[i] + if !reflect.DeepEqual(&s.SectionHeader, sh) { + t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, &s.SectionHeader, sh) + } + } + for i, p := range f.Progs { + if i >= len(tt.progs) { + break + } + ph := &tt.progs[i] + if !reflect.DeepEqual(&p.ProgHeader, ph) { + t.Errorf("open %s, program %d:\n\thave %#v\n\twant %#v\n", tt.file, i, &p.ProgHeader, ph) + } + } + tn := len(tt.sections) + fn := len(f.Sections) + if tn != fn { + t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn) + } + tn = len(tt.progs) + fn = len(f.Progs) + if tn != fn { + t.Errorf("open %s: len(Progs) = %d, want %d", tt.file, fn, tn) + } + tl := tt.needed + fl, err := f.ImportedLibraries() + if err != nil { + t.Error(err) + } + if !reflect.DeepEqual(tl, fl) { + t.Errorf("open %s: DT_NEEDED = %v, want %v", tt.file, tl, fl) + } + } +} + +// elf.NewFile requires io.ReaderAt, which compress/gzip cannot +// provide. Decompress the file to a bytes.Reader. +func decompress(gz string) (io.ReaderAt, error) { + in, err := os.Open(gz) + if err != nil { + return nil, err + } + defer in.Close() + r, err := gzip.NewReader(in) + if err != nil { + return nil, err + } + var out bytes.Buffer + _, err = io.Copy(&out, r) + return bytes.NewReader(out.Bytes()), err +} + +type relocationTestEntry struct { + entryNumber int + entry *dwarf.Entry +} + +type relocationTest struct { + file string + entries []relocationTestEntry +} + +var relocationTests = []relocationTest{ + { + "testdata/go-relocation-test-gcc441-x86-64.obj", + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, + }, + { + "testdata/go-relocation-test-gcc441-x86.obj", + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "t.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, + }, + { + "testdata/go-relocation-test-gcc424-x86-64.obj", + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, + }, + { + "testdata/go-relocation-test-gcc482-aarch64.obj", + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -g -fstack-protector"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: int64(0x24)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, + }, + { + "testdata/go-relocation-test-clang-x86.obj", + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "clang version google3-trunk (trunk r209387)"}, {Attr: dwarf.AttrLanguage, Val: int64(12)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-clang.c"}, {Attr: dwarf.AttrStmtList, Val: int64(0)}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}}}}, + }, + }, + { + "testdata/gcc-amd64-openbsd-debug-with-rela.obj", + []relocationTestEntry{ + {203, &dwarf.Entry{Offset: 0xc62, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_interval"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(236)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x0}}}}}, + {204, &dwarf.Entry{Offset: 0xc70, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_value"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(237)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x10}}}}}, + }, + }, +} + +func TestDWARFRelocations(t *testing.T) { + for i, test := range relocationTests { + f, err := Open(test.file) + if err != nil { + t.Error(err) + continue + } + dwarf, err := f.DWARF() + if err != nil { + t.Error(err) + continue + } + for _, testEntry := range test.entries { + reader := dwarf.Reader() + for j := 0; j < testEntry.entryNumber; j++ { + entry, err := reader.Next() + if entry == nil || err != nil { + t.Errorf("Failed to skip to entry %d: %v", testEntry.entryNumber, err) + continue + } + } + entry, err := reader.Next() + if err != nil { + t.Error(err) + continue + } + if !reflect.DeepEqual(testEntry.entry, entry) { + t.Errorf("#%d/%d: mismatch: got:%#v want:%#v", i, testEntry.entryNumber, entry, testEntry.entry) + continue + } + } + } +} + +func TestNoSectionOverlaps(t *testing.T) { + // Ensure 6l outputs sections without overlaps. + if runtime.GOOS != "linux" && runtime.GOOS != "freebsd" { + return // not ELF + } + _ = net.ResolveIPAddr // force dynamic linkage + f, err := Open(os.Args[0]) + if err != nil { + t.Error(err) + return + } + for i, si := range f.Sections { + sih := si.SectionHeader + if sih.Type == SHT_NOBITS { + continue + } + for j, sj := range f.Sections { + sjh := sj.SectionHeader + if i == j || sjh.Type == SHT_NOBITS || sih.Offset == sjh.Offset && sih.Size == 0 { + continue + } + if sih.Offset >= sjh.Offset && sih.Offset < sjh.Offset+sjh.Size { + t.Errorf("ld produced ELF with section %s within %s: 0x%x <= 0x%x..0x%x < 0x%x", + sih.Name, sjh.Name, sjh.Offset, sih.Offset, sih.Offset+sih.Size, sjh.Offset+sjh.Size) + } + } + } +} diff --git a/elf/symbols_test.go b/elf/symbols_test.go new file mode 100644 index 0000000..1b79520 --- /dev/null +++ b/elf/symbols_test.go @@ -0,0 +1,834 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package elf + +import ( + "io" + "path" + "reflect" + "testing" +) + +// TODO: remove duplicate code +func TestSymbols(t *testing.T) { + do := func(file string, ts []Symbol, getfunc func(*File) ([]Symbol, error)) { + var f *File + var err error + if path.Ext(file) == ".gz" { + var r io.ReaderAt + if r, err = decompress(file); err == nil { + f, err = NewFile(r) + } + } else { + f, err = Open(file) + } + if err != nil { + t.Errorf("TestSymbols: cannot open file %s: %v", file, err) + return + } + defer f.Close() + fs, err := getfunc(f) + if err != nil && err != ErrNoSymbols { + t.Error(err) + return + } else if err == ErrNoSymbols { + fs = []Symbol{} + } + if !reflect.DeepEqual(ts, fs) { + t.Errorf("%s: Symbols = %v, want %v", file, ts, fs) + } + } + for file, ts := range symbolsGolden { + do(file, ts, (*File).Symbols) + } + for file, ts := range dynamicSymbolsGolden { + do(file, ts, (*File).DynamicSymbols) + } +} + +// golden symbol table data generated by testdata/getgoldsym.c + +var symbolsGolden = map[string][]Symbol{ + "testdata/gcc-amd64-linux-exec": { + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x1, + Value: 0x400200, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x2, + Value: 0x40021C, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x3, + Value: 0x400240, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x4, + Value: 0x400268, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x5, + Value: 0x400288, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x6, + Value: 0x4002E8, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x7, + Value: 0x400326, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x8, + Value: 0x400330, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x9, + Value: 0x400350, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0xA, + Value: 0x400368, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0xB, + Value: 0x400398, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0xC, + Value: 0x4003B0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0xD, + Value: 0x4003E0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0xE, + Value: 0x400594, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0xF, + Value: 0x4005A4, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x10, + Value: 0x4005B8, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x11, + Value: 0x4005E0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x12, + Value: 0x600688, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x13, + Value: 0x600698, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x14, + Value: 0x6006A8, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x15, + Value: 0x6006B0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x16, + Value: 0x600850, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x17, + Value: 0x600858, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x18, + Value: 0x600880, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x19, + Value: 0x600898, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x1A, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x1B, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x1C, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x1D, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x1E, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x1F, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x20, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x21, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "init.c", + Info: 0x4, + Other: 0x0, + Section: 0xFFF1, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "initfini.c", + Info: 0x4, + Other: 0x0, + Section: 0xFFF1, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "call_gmon_start", + Info: 0x2, + Other: 0x0, + Section: 0xD, + Value: 0x40040C, + Size: 0x0, + }, + Symbol{ + Name: "crtstuff.c", + Info: 0x4, + Other: 0x0, + Section: 0xFFF1, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "__CTOR_LIST__", + Info: 0x1, + Other: 0x0, + Section: 0x12, + Value: 0x600688, + Size: 0x0, + }, + Symbol{ + Name: "__DTOR_LIST__", + Info: 0x1, + Other: 0x0, + Section: 0x13, + Value: 0x600698, + Size: 0x0, + }, + Symbol{ + Name: "__JCR_LIST__", + Info: 0x1, + Other: 0x0, + Section: 0x14, + Value: 0x6006A8, + Size: 0x0, + }, + Symbol{ + Name: "__do_global_dtors_aux", + Info: 0x2, + Other: 0x0, + Section: 0xD, + Value: 0x400430, + Size: 0x0, + }, + Symbol{ + Name: "completed.6183", + Info: 0x1, + Other: 0x0, + Section: 0x19, + Value: 0x600898, + Size: 0x1, + }, + Symbol{ + Name: "p.6181", + Info: 0x1, + Other: 0x0, + Section: 0x18, + Value: 0x600890, + Size: 0x0, + }, + Symbol{ + Name: "frame_dummy", + Info: 0x2, + Other: 0x0, + Section: 0xD, + Value: 0x400470, + Size: 0x0, + }, + Symbol{ + Name: "crtstuff.c", + Info: 0x4, + Other: 0x0, + Section: 0xFFF1, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "__CTOR_END__", + Info: 0x1, + Other: 0x0, + Section: 0x12, + Value: 0x600690, + Size: 0x0, + }, + Symbol{ + Name: "__DTOR_END__", + Info: 0x1, + Other: 0x0, + Section: 0x13, + Value: 0x6006A0, + Size: 0x0, + }, + Symbol{ + Name: "__FRAME_END__", + Info: 0x1, + Other: 0x0, + Section: 0x11, + Value: 0x400680, + Size: 0x0, + }, + Symbol{ + Name: "__JCR_END__", + Info: 0x1, + Other: 0x0, + Section: 0x14, + Value: 0x6006A8, + Size: 0x0, + }, + Symbol{ + Name: "__do_global_ctors_aux", + Info: 0x2, + Other: 0x0, + Section: 0xD, + Value: 0x400560, + Size: 0x0, + }, + Symbol{ + Name: "initfini.c", + Info: 0x4, + Other: 0x0, + Section: 0xFFF1, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "hello.c", + Info: 0x4, + Other: 0x0, + Section: 0xFFF1, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "_GLOBAL_OFFSET_TABLE_", + Info: 0x1, + Other: 0x2, + Section: 0x17, + Value: 0x600858, + Size: 0x0, + }, + Symbol{ + Name: "__init_array_end", + Info: 0x0, + Other: 0x2, + Section: 0x12, + Value: 0x600684, + Size: 0x0, + }, + Symbol{ + Name: "__init_array_start", + Info: 0x0, + Other: 0x2, + Section: 0x12, + Value: 0x600684, + Size: 0x0, + }, + Symbol{ + Name: "_DYNAMIC", + Info: 0x1, + Other: 0x2, + Section: 0x15, + Value: 0x6006B0, + Size: 0x0, + }, + Symbol{ + Name: "data_start", + Info: 0x20, + Other: 0x0, + Section: 0x18, + Value: 0x600880, + Size: 0x0, + }, + Symbol{ + Name: "__libc_csu_fini", + Info: 0x12, + Other: 0x0, + Section: 0xD, + Value: 0x4004C0, + Size: 0x2, + }, + Symbol{ + Name: "_start", + Info: 0x12, + Other: 0x0, + Section: 0xD, + Value: 0x4003E0, + Size: 0x0, + }, + Symbol{ + Name: "__gmon_start__", + Info: 0x20, + Other: 0x0, + Section: 0x0, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "_Jv_RegisterClasses", + Info: 0x20, + Other: 0x0, + Section: 0x0, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "puts@@GLIBC_2.2.5", + Info: 0x12, + Other: 0x0, + Section: 0x0, + Value: 0x0, + Size: 0x18C, + }, + Symbol{ + Name: "_fini", + Info: 0x12, + Other: 0x0, + Section: 0xE, + Value: 0x400594, + Size: 0x0, + }, + Symbol{ + Name: "__libc_start_main@@GLIBC_2.2.5", + Info: 0x12, + Other: 0x0, + Section: 0x0, + Value: 0x0, + Size: 0x1C2, + }, + Symbol{ + Name: "_IO_stdin_used", + Info: 0x11, + Other: 0x0, + Section: 0xF, + Value: 0x4005A4, + Size: 0x4, + }, + Symbol{ + Name: "__data_start", + Info: 0x10, + Other: 0x0, + Section: 0x18, + Value: 0x600880, + Size: 0x0, + }, + Symbol{ + Name: "__dso_handle", + Info: 0x11, + Other: 0x2, + Section: 0x18, + Value: 0x600888, + Size: 0x0, + }, + Symbol{ + Name: "__libc_csu_init", + Info: 0x12, + Other: 0x0, + Section: 0xD, + Value: 0x4004D0, + Size: 0x89, + }, + Symbol{ + Name: "__bss_start", + Info: 0x10, + Other: 0x0, + Section: 0xFFF1, + Value: 0x600898, + Size: 0x0, + }, + Symbol{ + Name: "_end", + Info: 0x10, + Other: 0x0, + Section: 0xFFF1, + Value: 0x6008A0, + Size: 0x0, + }, + Symbol{ + Name: "_edata", + Info: 0x10, + Other: 0x0, + Section: 0xFFF1, + Value: 0x600898, + Size: 0x0, + }, + Symbol{ + Name: "main", + Info: 0x12, + Other: 0x0, + Section: 0xD, + Value: 0x400498, + Size: 0x1B, + }, + Symbol{ + Name: "_init", + Info: 0x12, + Other: 0x0, + Section: 0xB, + Value: 0x400398, + Size: 0x0, + }, + }, + "testdata/go-relocation-test-clang-x86.obj": { + Symbol{ + Name: "go-relocation-test-clang.c", + Info: 0x4, + Other: 0x0, + Section: 0xFFF1, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: ".Linfo_string0", + Info: 0x0, + Other: 0x0, + Section: 0xC, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: ".Linfo_string1", + Info: 0x0, + Other: 0x0, + Section: 0xC, + Value: 0x2C, + Size: 0x0, + }, + Symbol{ + Name: ".Linfo_string2", + Info: 0x0, + Other: 0x0, + Section: 0xC, + Value: 0x47, + Size: 0x0, + }, + Symbol{ + Name: ".Linfo_string3", + Info: 0x0, + Other: 0x0, + Section: 0xC, + Value: 0x4C, + Size: 0x0, + }, + Symbol{ + Name: ".Linfo_string4", + Info: 0x0, + Other: 0x0, + Section: 0xC, + Value: 0x4E, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x1, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x2, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x3, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x4, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x6, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x7, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x8, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0xA, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0xC, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0xD, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0xE, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0xF, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "", + Info: 0x3, + Other: 0x0, + Section: 0x10, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "v", + Info: 0x11, + Other: 0x0, + Section: 0xFFF2, + Value: 0x4, + Size: 0x4, + }, + }, + "testdata/hello-world-core.gz": {}, +} + +var dynamicSymbolsGolden = map[string][]Symbol{ + "testdata/gcc-amd64-linux-exec": { + Symbol{ + Name: "__gmon_start__", + Info: 0x20, + Other: 0x0, + Section: 0x0, + Value: 0x0, + Size: 0x0, + }, + Symbol{ + Name: "puts", + Info: 0x12, + Other: 0x0, + Section: 0x0, + Value: 0x0, + Size: 0x18C, + }, + Symbol{ + Name: "__libc_start_main", + Info: 0x12, + Other: 0x0, + Section: 0x0, + Value: 0x0, + Size: 0x1C2, + }, + }, + "testdata/go-relocation-test-clang-x86.obj": {}, + "testdata/hello-world-core.gz": {}, +} diff --git a/elf/testdata/gcc-386-freebsd-exec b/elf/testdata/gcc-386-freebsd-exec new file mode 100755 index 0000000000000000000000000000000000000000..7af9c58ca73945561c470a5e7ad29f39d0405a6e GIT binary patch literal 5742 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 C>2l%# literal 0 HcmV?d00001 diff --git a/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj b/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj new file mode 100644 index 0000000000000000000000000000000000000000..f62b1ea1cada83530e45320cc58394933753dddb GIT binary patch literal 6544 zcmbtYU5p#m6+ZUNwP(kh#G6fNT9CSsR?tG$yPJ?K*(F&5AqfP;PeV|JG#cAud$RV7 zGc)#Pm!AX@TF4*J7NkD3YKw%bzED+2sC_6>+Xu=6s#K|J#RDqp6Hp0SN-Jnd&$;Jb z|4f{xj&^76`Ofb>_uO;Gb7J42+q0Pr=qm%ep-*WP;J{>myd;N9upSD1P>r;1W+L+y zYqD^qHIdnvDdqsOlbJ$cS7D)$K}*IuFf1YtPR`o^<_0L>5I}qx2fGV~1(3U-(-Z^wb5}FDg;NflX}h z1+iy5i;=<|MVyM9tCiV*_u;8qr?yYeP0vov&CKka**SaD6l6+`u;rDGhF-H2N0m}N zl#fcQTB_8;OA-n@e<9EN>7q98S?;W(= zL+=}~OdyASe_EE|ucw4LxWj2-h95`?b7)5fLnem|KiH4BokX;Bb=T-G-N33TcH^XE z^RkUtv6g$BtSk?>Q&3a6ioCiXC!TZ`k~3VUu3N22AR_Y!v^5Ybpd0&Z9$8Mbga$ea zs5HaMGD#d)^(q0sO3ohSfkUjMm^q&6+Zy(~nMK^4; zR^Fy8Nf{?`>#*5?y1-^tZ&P@&mw*AZE2-JkF2PJA?0OMSXvJ}(I{teu%98!r87+-=XQBIn6t5h4v28=M)`v0y(wwnqNsGH;75(0(p=&lGU~pdPX6Y zc1Ny^d9BJ=)$5{$d7Y~1nt!}aUd*qmYOb^R*K~2yvYOXxDsWW2m^bvHHa0zy+S=Rm zA(7KcV%b|IvZPk16;>%ib4Ia)rXNr_nX?LLa|ClL!l>!Dh`mjfb?iDdx8*mb(e0`$ z#a=yLBdHseR*W^;^a6r*D7*1WxhWm(?Cobcxp~F!#9k!~s+3UkCap6F?$SAn(NIM$ zj=7+ss>Lf_goOZF0#w0qfn!dP;2vt4Xb$ti?+6 zQc0JM*!3D!)<7!}3%{h!fr@l?iUabr&_|`XORfhpWj_`7$RY2`(S9XOiq zGFO$jSTV-%ZO!^4y`SmEDIR{4rmx$kc;sy&b<-4&ejvH3D~ZQ4*i~{BZSV1W175IW6OnB!~MX19HoXZ~mT^LT^~{t+$Ef?JA!7fC#y9#M2k$IG{}^&TPZC z(Hw6IPdxKDQF@PwXJ3+}+D_uRH;CjmBfkBX zB>G!PeCJ&$J+P6)cRwSj*55|r`AI7AflVY{xRwyV*KouS*O3wes@;emO-aK8?Lhqa zISKXlzIgFf0`!R`e!5Yv2OVIXtCzPBpqJ~F3zE|0FMdFzI#I;0Ue*+KY4Mxah~yJX zyxt@NkG87Qa^q#l?L3S{Vw1*i*M;h4SP%3Ph!X|5{)MaGfDXke{PQO2<#qbttQl-^Pf=3UM2;Vt(PBp2nhqL!}W& zjhlxw*22I8D}To|;}~VK=Cu5vgRjaf9-fo=J5Qrisl4V?NSu*9QYXtn80d&C;$(&7DH*8#| zLbA3}!g1O%Bx{-}luuePKi2pw${9%um$w3AJ{yw&rVhrAGGLNKBkV*lw#r@r{?A!yAZr}0f&E`5 z3}YGW&&YQcx2S26M;QezDH^-|w=#s-iM&b|?ESqMVy7B~ZLkk8LKTX!<4alWW0p<( zfs0bHiyQT57=eA{dpdfA{D7bb8OpM%-h-C?`1TE|I_xJnGF)=C2klcjR_U)%jbM}g z^b4q9lp9WhQMWLtGvr*Rs^L`JwoKnyRL#uP&oMYoAlj1HdCpl8HqeZ!%|yP( z7Hu>02Xs>`*P6T@BBPSQ5WVc$IVrovK%Axs`^G?Pv&$SK}dIh$scvW2dJ}_}5;pmr%CiHk$y8aZ>ez-83tQUW?=D z27T;VEO-27vNg@Y&m39^hq2f6DhVnOj4g{tPJgIIm^EAsgI3|Va38u?&j6=CHZn+$JU6_u%rmG;I+N&DltGGABYIbHN=(-juY%Q1QKR6`SC({0JKoWD{ z9pEE=^zLcdhme!TNI2TaDogz6-P5v1kTWX&<6mGE9Vf+4v8Ut5-96I(&z1kH*i7%9 z_D|DH*A5B4sq|NRnmkDB6V1DNSn%~Ln4VV(_?7_l9W@<4{;nA5UoX%WHq*PO{r4Cn z1}1!XaRiM1Mq4pP&V=F{l_A1MtsLRh?=HOz!1HFrW%RN#%rs=ihB1nKVjxakAGD4W z@b`;4zxw&b%i!F62U={e8LPo8`_+Ak)^2cCw=i2~a&Y|*UIxEc;bdnp3(w*KN*Cc& z3smQH{rWO^MG0tmmd0NWDICA)%j@G5uH{WCNW8QiEx!qmqCs58^S;6<|0*;ow@&pZ U!nG_h_6^|VLtlgW)MMiP4~j0k8UO$Q literal 0 HcmV?d00001 diff --git a/elf/testdata/go-relocation-test-clang-x86.obj b/elf/testdata/go-relocation-test-clang-x86.obj new file mode 100644 index 0000000000000000000000000000000000000000..e909cf4e6e19a63276f497b70a170492ade64156 GIT binary patch literal 1900 zcmb7F%W4!s6uq6E@imh%F;PTC11d(yw0S|I5zR(}7=j2QiV%Bd+R2ca?$BKcX5}Uy z;8JkyQgGwS&+s!`>DEO&r@CsoCXf&>+`8w~ee~_>s_GAqRv&ww=Zg_feDN%ST^`EG zyf%|EE~7%6PnUFPH4ibP_DqAIY+2XUL%Ag0sCTnAR`zQnCob1eVqW~(ld*@@dzGci z&>0c`1|}XTm-$;8W-&es>Pb7Pr%^X)hN_dqbrof*-t2~PJ7^+8(4uW` z5$Gy(2P$#_nzzk)cG!s(MiHGou@j{k3Qn|>q}`3?>MGriHzy`@lg>`xom*VEebmG6 zsNNQyrK|o=NeNm}W4pZ`HX3QPBY}$ED+!uOuNR@gppj(~;Dt@p)^Frb+c5QDw6GpOzZE`<(t@7WC?Bqz6j=s5 z2W4+mK9BJ>2T;1uDFP$bw+C)9U7w&!1V*fna~9L}6MB)ri1qP2xc$9Bf9!|-(c1oQ z0oxov`31d1V8r^agS+)wPCXpOe!W+~%;9mNltG^7P>=LcKN_niF}&oYBbGkJoW*n> z7I)sn+2=B@&IBW@LFEB<>+QM3@qGridkRn(_k+NQP5TO-UlO_FZCT_QoMWKlP_FX` z^b6Fj_YE7ngE=dUYbvVz#n#21LVDKg< zci)WM?S`ChSBT#~t@)fU*d&hldgm1rzHsLh;!Cw4K)wtg;P?wT;tOwyLVT<=*O6UO zN9E5x?$;x(|6rAu+d8hQPTZcC0XjEGX9wxbAl2KBca?{q5#PeY(($bEPstyNhe@6* Y4~<=;#Xgy$#U{~zEhOuFEQz)M0D2R@nE(I) literal 0 HcmV?d00001 diff --git a/elf/testdata/go-relocation-test-gcc424-x86-64.obj b/elf/testdata/go-relocation-test-gcc424-x86-64.obj new file mode 100644 index 0000000000000000000000000000000000000000..a7c6d6e562c1c70342d9d1cf41194b7ef3fdedf6 GIT binary patch literal 3088 zcmbVO&1(};5TCc3wy9}r+WG}mj8L%^vueqyN*gHJYD*EJAR^S=xJir6rX-tMMX?8w z3JRXYiy)pn>eZ9rMg0prdhrkN?4i!=zG=3TNBtPs_vZcPG_4+RIcERIfdNFg4YbfX1vOuZAPoYD5y#;U-wsPB{y8I z1^Li#g!xLTR2VBbB{&^cSEF2Vuv8h8dL&`Jwo@I;K?rae*A|T7sMN2%UL2#MU?~*r zMjNH+j!!|svzCRifRzE*LQ$aBvprqu=enrFQ9x_Jl0=dsdtdgkm7+rTc2Ycvx_6Z_ zC|l>C_`{I}Ac~cN+QyTn#k1W>v!2pM#ImgOJ}g+vaxBlZm-vKFQ7h{Q;M6uwx7iZT z)k3s3JvB9+8^Nm{{liUz)3{p=T@UB59?wgxh5mX7&Mh}|!SNamRDG{mS#&+G?yrG^ z4dQBOIVjh504vLZzXK@O-6|vg(xP6*39-+1_^vj+0KJhgLE+Zjph8%wR;~KjNKQ}- zeFu-2Z-j2?c3TPl6&S(*x;I(VqD~}nzE5q2W2jJk;`NP8GwdK{8Gi!pbi4l>Pd&nY zm!x9;LJ~CiP8u)@xlaQMb!1W`*EkyGnaKa+o;d1lJi&gPJMf{ik{pUBxPr6HNhW@o z=CvPB(svdOCjO;N9Kk%q--VClcM+30De{^Ii?m1x$1`*T6%+r4=0BqyiN6~kj^}+f z5F@JXFC6M5FaMH1KwlI8JHz=2lKc-4GoAmN2ItV86zTg6jS?gOVSOMzC+Cp9n`kiY z|FdELedsIq{{}Icleo@ney%@ZB+~a4VkZ9YhWn!rx8#3s$cYfFX#SJ5NQmVBfHsMd z`wJbP2#r+f+mERHc90{-e~qGAgN&!Oi7H3Uin5^t4xqjlA9;RqGx5LH@BdM>Cq?on zZAqQ{Qt)#tiLq`QD@OY;orG}H4hc8iJmK`EY8N~Df|zh!EMc-iH&GkjE0B0;19f=2 z*g&1vE;bCdgNWEL(1ssDWFS$u?ccTC{(O~Nzl(I8$*Nq{46=W&3!Hg8jZ+@_4zsLk zMdn!-IOo~WIOWkT79sRIMk4d@e>w8y-X*^r4Jgt-7ols1n+fjLZE2(}X}5^PM7@}J zV~E0$5Itg>Q=1x3iiNYtq}l}$P>=vuTnoUqxc){4KpjKHuEii zt66q~TCneE2Yw6?%cZ9X#}0fJ74I8+QJ-1 zQ$Fg7NbTJNwVPGbBSWcu-0msFXc2{-T+L)ludLCyE2sUm9=IMj?VuI$O_2hBHvs36 z8@S+j?KXH3d7{1j@9#7wpy`Oug%cK;>$pq`n_&h8 zinj$|zN+b>>hWjMKy&l2@icu@Uni+w&FLY{FP&3ck_-$gX)dm{hr(J1O` z#KJipL|LAZDaP+2*K?BT6Wdpq3)6k+MPjNC=_o?#&~q|`ORxU`F+C?mPBOSgN`iFX zEwt$vxqA#seMzDFR?%SUf6CB59jV^`OT_e?RBMmHc~TOj`_es|>d$k1iKp`$!KK&V zM~rfcABrKp<3eWyU@rBAzd_s-|Ct?T{}TU}<25(K~E; zN&1X5^Q?(2$*JrBeEXTiM7E6+OMRGjLb$0z!c8YnIK655$qwHDCY(2`nQY*fSRYPL zLO6}2f67DeVWCSbh`-?UT_O@(^2qmq^2jWcpFF4HPZ*KJk-x}) R!sVv)JUI$Nq6tk-=`Vm&no0lw literal 0 HcmV?d00001 diff --git a/elf/testdata/go-relocation-test-gcc441-x86.obj b/elf/testdata/go-relocation-test-gcc441-x86.obj new file mode 100644 index 0000000000000000000000000000000000000000..0d59fe303b6dad25698a1e7c0644d285460797a9 GIT binary patch literal 1884 zcma)7O;1x%5S{y8(Q2VkP@|$rNgzNX578zjfS}edjB#OPLt;o<>{420?)s0Sa@64Q;duHxOU*Fuif7=)%Dn@+ic_NYLA_c2v zXGYD)5fLwt)j>bKm|4oKjS;*C(4Uk{W(9dzvx?y; zQ?`tWf{F}Ua*=^pd!{PJEd7Q0sexJ}5(zBDSvPh6Y2SIUo-gJLK_OU>`KZ1Nq*z?c z&#ad_ji}=SbA^ue7G~o_5tgHv7Z1g}Oq=n+RO&r?Jq?T{DYne@Z2HDKZ^+s~d#@gq z$`Z7rmebp6Md9;Ef+wY@Btf~|h8dPSTbreFxfMQ>pcU@KNUc$ACjFgSBh>7jW+fTg z?UWm(dI+-K@=~kR*dpZkQC@;dvtAEzLqVe%g#iv^t{s&sPu;R`d$ZaCi`&wF&Ev@8 zwc!a_;TudIxn9HsaOUwkDa%fEXU&@i!h8N#-UZ~4PlbG}t)j6N0BSRaI~P706^$(d zIHn-<4{YczH1D4KVSnuF0fw%DDPJ)z(TGK3_uwb_3P;^~^12uBsc4LMm#n*-sCyhS-N!4~x`rIHaPl-_(byjRB;QT&jR505_(@Q{kFdGs zA_ip*PR^NjW{l%gtTk@~;H1rPljePn^EC9DfrO~L4~%yF36!^R3W+@SNBM%0+{dHD zxv)>&2W@?OFl8Tn-)O|5b(1w2I^ZJ#coJ$DgM2(E)=csp;9c?lRg_PC)k!=hKYuGu z-T(Ecue*0yeqTk#^%OMDW_#lIb;SJSNzv!a>51R{GjNzM*8&&BV#hekGb^iD_`v1TvlM5^xhCS$2sry67PUb5KIhOz+H)neMfx zCvHCc058FVxFGSOqKKlmfS`$oMLY#^mt6FJ@D`CJR=roX^XAQvgBEmG)vrFg`n}iv z{+ZR6T*m<+2Y!a0CMduIuO}}laS8UqBy3;b`eECH&94r^-LH%AH<~`dYZI?ex3B;9 z{jc7o-^slZw_53u;GHyU)kyydFP{V`sB!}}=af<3+`=vrT&JLzt2Lj&Q4Nj|^Zb!! zl7m}N8prvC3{4!Z42~|1sFZO&{}DBn%JJVI_stuuk-^` zkGYRfDJlZ$NtHx5>2e2P;Z@`=EYRKgr!)o*5-NB(P*2NQ+)Ara-b|Bn9(VF`y;?oK zxDZx#^ln`Vl_x)}yFLbYlTNdq#I=BCc=ojm!AfvETnrb2ay=-ob@HfsxxAjGd0frY z3|eVY5Bfr?jVOa1L}~4)nP^YSc{FdYLzLC4&}pPu-XG1iRoLU2*P)cI)4UevSJz`o zc?IC?%F4;$(1l7j$-75an@NGo^p)5-3F@^T9Rx^nu1nWUqP224lXJ9(5Wl=lUFZci6157|mN$mk6 z*%He#A2InNFrP(kV`RGXgm!pYGnhh7b!5HME+BdiFT#yuhhV6w`{0dyO`@GdIsa?? zJjPPaojrmwQA0DBU@lDh7yT3pc28v6V259r3$uMspkdhjpr|A6aRAMa-jURo`MrXe zos%LL7<`iygKXco&}L)g{xO5cj93zF-;0P1>;E2YJS2&y{&(YL_y0Cxc20y~i^11P zG0669p>0_Ghg@IgMD=N;cKvq|qny$wii*Z~L2L=YTDe$fO`mi`5%5Ad+(pBU2~@?7-%<9JED%w-ZWd+$)>F$TXO#UQ&^((S!N?hm-o zZ4SVXpxR4zeOeR4>fhw^qfE&Rvg;eBK9T%<`0G(0$ci^~;?&2mP6!`1VZzlME0fR3EA%j!=HLP z!#aIuh{XT0R(I@zGYqRN2>u@H=Pmp@*5x_G|03fJ%l|g(T?=1g{hEbKy$=|-=kuB6 lzsCBv7Jip?^F7L0LMB-!cK*{GXXCPd=y}amE$hej{}%-U_$L4W literal 0 HcmV?d00001 diff --git a/elf/testdata/hello-world-core.gz b/elf/testdata/hello-world-core.gz new file mode 100644 index 0000000000000000000000000000000000000000..806af6edbc23bd0db45a9b1adfae9418ca6a969a GIT binary patch literal 12678 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 Y-g)s?BF`+qef#Ve(D~239)f%S3*17p82|tP literal 0 HcmV?d00001 diff --git a/elf/testdata/hello.c b/elf/testdata/hello.c new file mode 100644 index 0000000..34d9ee7 --- /dev/null +++ b/elf/testdata/hello.c @@ -0,0 +1,7 @@ +#include + +void +main(int argc, char *argv[]) +{ + printf("hello, world\n"); +} diff --git a/goobj/read.go b/goobj/read.go new file mode 100644 index 0000000..79a83e5 --- /dev/null +++ b/goobj/read.go @@ -0,0 +1,666 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package goobj implements reading of Go object files and archives. +// +// TODO(rsc): Decide where this package should live. (golang.org/issue/6932) +// TODO(rsc): Decide the appropriate integer types for various fields. +// TODO(rsc): Write tests. (File format still up in the air a little.) +package goobj + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "strconv" + "strings" +) + +// A SymKind describes the kind of memory represented by a symbol. +type SymKind int + +// This list is taken from include/link.h. + +// Defined SymKind values. +// TODO(rsc): Give idiomatic Go names. +// TODO(rsc): Reduce the number of symbol types in the object files. +const ( + _ SymKind = iota + + // readonly, executable + STEXT + SELFRXSECT + + // readonly, non-executable + STYPE + SSTRING + SGOSTRING + SGOFUNC + SRODATA + SFUNCTAB + STYPELINK + SSYMTAB // TODO: move to unmapped section + SPCLNTAB + SELFROSECT + + // writable, non-executable + SMACHOPLT + SELFSECT + SMACHO // Mach-O __nl_symbol_ptr + SMACHOGOT + SNOPTRDATA + SINITARR + SDATA + SWINDOWS + SBSS + SNOPTRBSS + STLSBSS + + // not mapped + SXREF + SMACHOSYMSTR + SMACHOSYMTAB + SMACHOINDIRECTPLT + SMACHOINDIRECTGOT + SFILE + SFILEPATH + SCONST + SDYNIMPORT + SHOSTOBJ +) + +var symKindStrings = []string{ + SBSS: "SBSS", + SCONST: "SCONST", + SDATA: "SDATA", + SDYNIMPORT: "SDYNIMPORT", + SELFROSECT: "SELFROSECT", + SELFRXSECT: "SELFRXSECT", + SELFSECT: "SELFSECT", + SFILE: "SFILE", + SFILEPATH: "SFILEPATH", + SFUNCTAB: "SFUNCTAB", + SGOFUNC: "SGOFUNC", + SGOSTRING: "SGOSTRING", + SHOSTOBJ: "SHOSTOBJ", + SINITARR: "SINITARR", + SMACHO: "SMACHO", + SMACHOGOT: "SMACHOGOT", + SMACHOINDIRECTGOT: "SMACHOINDIRECTGOT", + SMACHOINDIRECTPLT: "SMACHOINDIRECTPLT", + SMACHOPLT: "SMACHOPLT", + SMACHOSYMSTR: "SMACHOSYMSTR", + SMACHOSYMTAB: "SMACHOSYMTAB", + SNOPTRBSS: "SNOPTRBSS", + SNOPTRDATA: "SNOPTRDATA", + SPCLNTAB: "SPCLNTAB", + SRODATA: "SRODATA", + SSTRING: "SSTRING", + SSYMTAB: "SSYMTAB", + STEXT: "STEXT", + STLSBSS: "STLSBSS", + STYPE: "STYPE", + STYPELINK: "STYPELINK", + SWINDOWS: "SWINDOWS", + SXREF: "SXREF", +} + +func (k SymKind) String() string { + if k < 0 || int(k) >= len(symKindStrings) { + return fmt.Sprintf("SymKind(%d)", k) + } + return symKindStrings[k] +} + +// A Sym is a named symbol in an object file. +type Sym struct { + SymID // symbol identifier (name and version) + Kind SymKind // kind of symbol + DupOK bool // are duplicate definitions okay? + Size int // size of corresponding data + Type SymID // symbol for Go type information + Data Data // memory image of symbol + Reloc []Reloc // relocations to apply to Data + Func *Func // additional data for functions +} + +// A SymID - the combination of Name and Version - uniquely identifies +// a symbol within a package. +type SymID struct { + // Name is the name of a symbol. + Name string + + // Version is zero for symbols with global visibility. + // Symbols with only file visibility (such as file-level static + // declarations in C) have a non-zero version distinguishing + // a symbol in one file from a symbol of the same name + // in another file + Version int +} + +func (s SymID) String() string { + if s.Version == 0 { + return s.Name + } + return fmt.Sprintf("%s<%d>", s.Name, s.Version) +} + +// A Data is a reference to data stored in an object file. +// It records the offset and size of the data, so that a client can +// read the data only if necessary. +type Data struct { + Offset int64 + Size int64 +} + +// A Reloc describes a relocation applied to a memory image to refer +// to an address within a particular symbol. +type Reloc struct { + // The bytes at [Offset, Offset+Size) within the memory image + // should be updated to refer to the address Add bytes after the start + // of the symbol Sym. + Offset int + Size int + Sym SymID + Add int + + // The Type records the form of address expected in the bytes + // described by the previous fields: absolute, PC-relative, and so on. + // TODO(rsc): The interpretation of Type is not exposed by this package. + Type int +} + +// A Var describes a variable in a function stack frame: a declared +// local variable, an input argument, or an output result. +type Var struct { + // The combination of Name, Kind, and Offset uniquely + // identifies a variable in a function stack frame. + // Using fewer of these - in particular, using only Name - does not. + Name string // Name of variable. + Kind int // TODO(rsc): Define meaning. + Offset int // Frame offset. TODO(rsc): Define meaning. + + Type SymID // Go type for variable. +} + +// Func contains additional per-symbol information specific to functions. +type Func struct { + Args int // size in bytes of argument frame: inputs and outputs + Frame int // size in bytes of local variable frame + Leaf bool // function omits save of link register (ARM) + NoSplit bool // function omits stack split prologue + Var []Var // detail about local variables + PCSP Data // PC → SP offset map + PCFile Data // PC → file number map (index into File) + PCLine Data // PC → line number map + PCData []Data // PC → runtime support data map + FuncData []FuncData // non-PC-specific runtime support data + File []string // paths indexed by PCFile +} + +// TODO: Add PCData []byte and PCDataIter (similar to liblink). + +// A FuncData is a single function-specific data value. +type FuncData struct { + Sym SymID // symbol holding data + Offset int64 // offset into symbol for funcdata pointer +} + +// A Package is a parsed Go object file or archive defining a Go package. +type Package struct { + ImportPath string // import path denoting this package + Imports []string // packages imported by this package + Syms []*Sym // symbols defined by this package + MaxVersion int // maximum Version in any SymID in Syms +} + +var ( + archiveHeader = []byte("!\n") + archiveMagic = []byte("`\n") + goobjHeader = []byte("go objec") // truncated to size of archiveHeader + + errCorruptArchive = errors.New("corrupt archive") + errTruncatedArchive = errors.New("truncated archive") + errNotArchive = errors.New("unrecognized archive format") + + errCorruptObject = errors.New("corrupt object file") + errTruncatedObject = errors.New("truncated object file") + errNotObject = errors.New("unrecognized object file format") +) + +// An objReader is an object file reader. +type objReader struct { + p *Package + b *bufio.Reader + f io.ReadSeeker + err error + offset int64 + limit int64 + tmp [256]byte + pkg string + pkgprefix string +} + +// importPathToPrefix returns the prefix that will be used in the +// final symbol table for the given import path. +// We escape '%', '"', all control characters and non-ASCII bytes, +// and any '.' after the final slash. +// +// See ../../../cmd/ld/lib.c:/^pathtoprefix and +// ../../../cmd/gc/subr.c:/^pathtoprefix. +func importPathToPrefix(s string) string { + // find index of last slash, if any, or else -1. + // used for determining whether an index is after the last slash. + slash := strings.LastIndex(s, "/") + + // check for chars that need escaping + n := 0 + for r := 0; r < len(s); r++ { + if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F { + n++ + } + } + + // quick exit + if n == 0 { + return s + } + + // escape + const hex = "0123456789abcdef" + p := make([]byte, 0, len(s)+2*n) + for r := 0; r < len(s); r++ { + if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F { + p = append(p, '%', hex[c>>4], hex[c&0xF]) + } else { + p = append(p, c) + } + } + + return string(p) +} + +// init initializes r to read package p from f. +func (r *objReader) init(f io.ReadSeeker, p *Package) { + r.f = f + r.p = p + r.offset, _ = f.Seek(0, 1) + r.limit, _ = f.Seek(0, 2) + f.Seek(r.offset, 0) + r.b = bufio.NewReader(f) + r.pkgprefix = importPathToPrefix(p.ImportPath) + "." +} + +// error records that an error occurred. +// It returns only the first error, so that an error +// caused by an earlier error does not discard information +// about the earlier error. +func (r *objReader) error(err error) error { + if r.err == nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + r.err = err + } + // panic("corrupt") // useful for debugging + return r.err +} + +// readByte reads and returns a byte from the input file. +// On I/O error or EOF, it records the error but returns byte 0. +// A sequence of 0 bytes will eventually terminate any +// parsing state in the object file. In particular, it ends the +// reading of a varint. +func (r *objReader) readByte() byte { + if r.err != nil { + return 0 + } + if r.offset >= r.limit { + r.error(io.ErrUnexpectedEOF) + return 0 + } + b, err := r.b.ReadByte() + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + r.error(err) + b = 0 + } else { + r.offset++ + } + return b +} + +// read reads exactly len(b) bytes from the input file. +// If an error occurs, read returns the error but also +// records it, so it is safe for callers to ignore the result +// as long as delaying the report is not a problem. +func (r *objReader) readFull(b []byte) error { + if r.err != nil { + return r.err + } + if r.offset+int64(len(b)) > r.limit { + return r.error(io.ErrUnexpectedEOF) + } + n, err := io.ReadFull(r.b, b) + r.offset += int64(n) + if err != nil { + return r.error(err) + } + return nil +} + +// readInt reads a zigzag varint from the input file. +func (r *objReader) readInt() int { + var u uint64 + + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + r.error(errCorruptObject) + return 0 + } + c := r.readByte() + u |= uint64(c&0x7F) << shift + if c&0x80 == 0 { + break + } + } + + v := int64(u>>1) ^ (int64(u) << 63 >> 63) + if int64(int(v)) != v { + r.error(errCorruptObject) // TODO + return 0 + } + return int(v) +} + +// readString reads a length-delimited string from the input file. +func (r *objReader) readString() string { + n := r.readInt() + buf := make([]byte, n) + r.readFull(buf) + return string(buf) +} + +// readSymID reads a SymID from the input file. +func (r *objReader) readSymID() SymID { + name, vers := r.readString(), r.readInt() + + // In a symbol name in an object file, "". denotes the + // prefix for the package in which the object file has been found. + // Expand it. + name = strings.Replace(name, `"".`, r.pkgprefix, -1) + + // An individual object file only records version 0 (extern) or 1 (static). + // To make static symbols unique across all files being read, we + // replace version 1 with the version corresponding to the current + // file number. The number is incremented on each call to parseObject. + if vers != 0 { + vers = r.p.MaxVersion + } + + return SymID{name, vers} +} + +// readData reads a data reference from the input file. +func (r *objReader) readData() Data { + n := r.readInt() + d := Data{Offset: r.offset, Size: int64(n)} + r.skip(int64(n)) + return d +} + +// skip skips n bytes in the input. +func (r *objReader) skip(n int64) { + if n < 0 { + r.error(fmt.Errorf("debug/goobj: internal error: misuse of skip")) + } + if n < int64(len(r.tmp)) { + // Since the data is so small, a just reading from the buffered + // reader is better than flushing the buffer and seeking. + r.readFull(r.tmp[:n]) + } else if n <= int64(r.b.Buffered()) { + // Even though the data is not small, it has already been read. + // Advance the buffer instead of seeking. + for n > int64(len(r.tmp)) { + r.readFull(r.tmp[:]) + n -= int64(len(r.tmp)) + } + r.readFull(r.tmp[:n]) + } else { + // Seek, giving up buffered data. + _, err := r.f.Seek(r.offset+n, 0) + if err != nil { + r.error(err) + } + r.offset += n + r.b.Reset(r.f) + } +} + +// Parse parses an object file or archive from r, +// assuming that its import path is pkgpath. +func Parse(r io.ReadSeeker, pkgpath string) (*Package, error) { + if pkgpath == "" { + pkgpath = `""` + } + p := new(Package) + p.ImportPath = pkgpath + + var rd objReader + rd.init(r, p) + err := rd.readFull(rd.tmp[:8]) + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return nil, err + } + + switch { + default: + return nil, errNotObject + + case bytes.Equal(rd.tmp[:8], archiveHeader): + if err := rd.parseArchive(); err != nil { + return nil, err + } + case bytes.Equal(rd.tmp[:8], goobjHeader): + if err := rd.parseObject(goobjHeader); err != nil { + return nil, err + } + } + + return p, nil +} + +// trimSpace removes trailing spaces from b and returns the corresponding string. +// This effectively parses the form used in archive headers. +func trimSpace(b []byte) string { + return string(bytes.TrimRight(b, " ")) +} + +// parseArchive parses a Unix archive of Go object files. +// TODO(rsc): Need to skip non-Go object files. +// TODO(rsc): Maybe record table of contents in r.p so that +// linker can avoid having code to parse archives too. +func (r *objReader) parseArchive() error { + for r.offset < r.limit { + if err := r.readFull(r.tmp[:60]); err != nil { + return err + } + data := r.tmp[:60] + + // Each file is preceded by this text header (slice indices in first column): + // 0:16 name + // 16:28 date + // 28:34 uid + // 34:40 gid + // 40:48 mode + // 48:58 size + // 58:60 magic - `\n + // We only care about name, size, and magic. + // The fields are space-padded on the right. + // The size is in decimal. + // The file data - size bytes - follows the header. + // Headers are 2-byte aligned, so if size is odd, an extra padding + // byte sits between the file data and the next header. + // The file data that follows is padded to an even number of bytes: + // if size is odd, an extra padding byte is inserted betw the next header. + if len(data) < 60 { + return errTruncatedArchive + } + if !bytes.Equal(data[58:60], archiveMagic) { + return errCorruptArchive + } + name := trimSpace(data[0:16]) + size, err := strconv.ParseInt(trimSpace(data[48:58]), 10, 64) + if err != nil { + return errCorruptArchive + } + data = data[60:] + fsize := size + size&1 + if fsize < 0 || fsize < size { + return errCorruptArchive + } + switch name { + case "__.SYMDEF", "__.GOSYMDEF", "__.PKGDEF": + r.skip(size) + default: + oldLimit := r.limit + r.limit = r.offset + size + if err := r.parseObject(nil); err != nil { + return fmt.Errorf("parsing archive member %q: %v", name, err) + } + r.skip(r.limit - r.offset) + r.limit = oldLimit + } + if size&1 != 0 { + r.skip(1) + } + } + return nil +} + +// parseObject parses a single Go object file. +// The prefix is the bytes already read from the file, +// typically in order to detect that this is an object file. +// The object file consists of a textual header ending in "\n!\n" +// and then the part we want to parse begins. +// The format of that part is defined in a comment at the top +// of src/liblink/objfile.c. +func (r *objReader) parseObject(prefix []byte) error { + // TODO(rsc): Maybe use prefix and the initial input to + // record the header line from the file, which would + // give the architecture and other version information. + + r.p.MaxVersion++ + var c1, c2, c3 byte + for { + c1, c2, c3 = c2, c3, r.readByte() + if c3 == 0 { // NUL or EOF, either is bad + return errCorruptObject + } + if c1 == '\n' && c2 == '!' && c3 == '\n' { + break + } + } + + r.readFull(r.tmp[:8]) + if !bytes.Equal(r.tmp[:8], []byte("\x00\x00go13ld")) { + return r.error(errCorruptObject) + } + + b := r.readByte() + if b != 1 { + return r.error(errCorruptObject) + } + + // Direct package dependencies. + for { + s := r.readString() + if s == "" { + break + } + r.p.Imports = append(r.p.Imports, s) + } + + // Symbols. + for { + if b := r.readByte(); b != 0xfe { + if b != 0xff { + return r.error(errCorruptObject) + } + break + } + + typ := r.readInt() + s := &Sym{SymID: r.readSymID()} + r.p.Syms = append(r.p.Syms, s) + s.Kind = SymKind(typ) + flags := r.readInt() + s.DupOK = flags&1 != 0 + s.Size = r.readInt() + s.Type = r.readSymID() + s.Data = r.readData() + s.Reloc = make([]Reloc, r.readInt()) + for i := range s.Reloc { + rel := &s.Reloc[i] + rel.Offset = r.readInt() + rel.Size = r.readInt() + rel.Type = r.readInt() + rel.Add = r.readInt() + r.readInt() // Xadd - ignored + rel.Sym = r.readSymID() + r.readSymID() // Xsym - ignored + } + + if s.Kind == STEXT { + f := new(Func) + s.Func = f + f.Args = r.readInt() + f.Frame = r.readInt() + flags := r.readInt() + f.Leaf = flags&1 != 0 + f.NoSplit = r.readInt() != 0 + f.Var = make([]Var, r.readInt()) + for i := range f.Var { + v := &f.Var[i] + v.Name = r.readSymID().Name + v.Offset = r.readInt() + v.Kind = r.readInt() + v.Type = r.readSymID() + } + + f.PCSP = r.readData() + f.PCFile = r.readData() + f.PCLine = r.readData() + f.PCData = make([]Data, r.readInt()) + for i := range f.PCData { + f.PCData[i] = r.readData() + } + f.FuncData = make([]FuncData, r.readInt()) + for i := range f.FuncData { + f.FuncData[i].Sym = r.readSymID() + } + for i := range f.FuncData { + f.FuncData[i].Offset = int64(r.readInt()) // TODO + } + f.File = make([]string, r.readInt()) + for i := range f.File { + f.File[i] = r.readSymID().Name + } + } + } + + r.readFull(r.tmp[:7]) + if !bytes.Equal(r.tmp[:7], []byte("\xffgo13ld")) { + return r.error(errCorruptObject) + } + + return nil +} diff --git a/goobj/read_test.go b/goobj/read_test.go new file mode 100644 index 0000000..dee1405 --- /dev/null +++ b/goobj/read_test.go @@ -0,0 +1,28 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goobj + +import "testing" + +var importPathToPrefixTests = []struct { + in string + out string +}{ + {"runtime", "runtime"}, + {"sync/atomic", "sync/atomic"}, + {"code.google.com/p/go.tools/godoc", "code.google.com/p/go.tools/godoc"}, + {"foo.bar/baz.quux", "foo.bar/baz%2equux"}, + {"", ""}, + {"%foo%bar", "%25foo%25bar"}, + {"\x01\x00\x7F☺", "%01%00%7f%e2%98%ba"}, +} + +func TestImportPathToPrefix(t *testing.T) { + for _, tt := range importPathToPrefixTests { + if out := importPathToPrefix(tt.in); out != tt.out { + t.Errorf("importPathToPrefix(%q) = %q, want %q", tt.in, out, tt.out) + } + } +} diff --git a/gosym/pclinetest.asm b/gosym/pclinetest.asm new file mode 100644 index 0000000..b9ee9c0 --- /dev/null +++ b/gosym/pclinetest.asm @@ -0,0 +1,58 @@ +TEXT linefrompc(SB),4,$0 // Each byte stores its line delta +BYTE $2; +BYTE $1; +BYTE $1; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; +BYTE $1; +BYTE $1; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +#include "pclinetest.h" +BYTE $2; +#include "pclinetest.h" +BYTE $2; +BYTE $255; + +TEXT pcfromline(SB),4,$0 // Each record stores its line delta, then n, then n more bytes +BYTE $32; BYTE $0; +BYTE $1; BYTE $1; BYTE $0; +BYTE $1; BYTE $0; + +BYTE $2; BYTE $4; BYTE $0; BYTE $0; BYTE $0; BYTE $0; + + +#include "pclinetest.h" +BYTE $4; BYTE $0; + + +BYTE $3; BYTE $3; BYTE $0; BYTE $0; BYTE $0; +#include "pclinetest.h" + + +BYTE $4; BYTE $3; BYTE $0; BYTE $0; BYTE $0; +BYTE $255; + +// Keep the linker happy +TEXT main·main(SB),4,$0 + RET + +TEXT main·init(SB),4,$0 + // Prevent GC of our test symbols + CALL linefrompc(SB) + CALL pcfromline(SB) + RET diff --git a/gosym/pclinetest.h b/gosym/pclinetest.h new file mode 100644 index 0000000..156c0b8 --- /dev/null +++ b/gosym/pclinetest.h @@ -0,0 +1,9 @@ +// +build ignore + +// Empty include file to generate z symbols + + + + + +// EOF diff --git a/gosym/pclntab.go b/gosym/pclntab.go new file mode 100644 index 0000000..6620aef --- /dev/null +++ b/gosym/pclntab.go @@ -0,0 +1,453 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * Line tables + */ + +package gosym + +import ( + "encoding/binary" + "sync" +) + +// A LineTable is a data structure mapping program counters to line numbers. +// +// In Go 1.1 and earlier, each function (represented by a Func) had its own LineTable, +// and the line number corresponded to a numbering of all source lines in the +// program, across all files. That absolute line number would then have to be +// converted separately to a file name and line number within the file. +// +// In Go 1.2, the format of the data changed so that there is a single LineTable +// for the entire program, shared by all Funcs, and there are no absolute line +// numbers, just line numbers within specific files. +// +// For the most part, LineTable's methods should be treated as an internal +// detail of the package; callers should use the methods on Table instead. +type LineTable struct { + Data []byte + PC uint64 + Line int + + // Go 1.2 state + mu sync.Mutex + go12 int // is this in Go 1.2 format? -1 no, 0 unknown, 1 yes + binary binary.ByteOrder + quantum uint32 + ptrsize uint32 + functab []byte + nfunctab uint32 + filetab []byte + nfiletab uint32 + fileMap map[string]uint32 +} + +// NOTE(rsc): This is wrong for GOARCH=arm, which uses a quantum of 4, +// but we have no idea whether we're using arm or not. This only +// matters in the old (pre-Go 1.2) symbol table format, so it's not worth +// fixing. +const oldQuantum = 1 + +func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64, line int) { + // The PC/line table can be thought of as a sequence of + // * + // batches. Each update batch results in a (pc, line) pair, + // where line applies to every PC from pc up to but not + // including the pc of the next pair. + // + // Here we process each update individually, which simplifies + // the code, but makes the corner cases more confusing. + b, pc, line = t.Data, t.PC, t.Line + for pc <= targetPC && line != targetLine && len(b) > 0 { + code := b[0] + b = b[1:] + switch { + case code == 0: + if len(b) < 4 { + b = b[0:0] + break + } + val := binary.BigEndian.Uint32(b) + b = b[4:] + line += int(val) + case code <= 64: + line += int(code) + case code <= 128: + line -= int(code - 64) + default: + pc += oldQuantum * uint64(code-128) + continue + } + pc += oldQuantum + } + return b, pc, line +} + +func (t *LineTable) slice(pc uint64) *LineTable { + data, pc, line := t.parse(pc, -1) + return &LineTable{Data: data, PC: pc, Line: line} +} + +// PCToLine returns the line number for the given program counter. +// Callers should use Table's PCToLine method instead. +func (t *LineTable) PCToLine(pc uint64) int { + if t.isGo12() { + return t.go12PCToLine(pc) + } + _, _, line := t.parse(pc, -1) + return line +} + +// LineToPC returns the program counter for the given line number, +// considering only program counters before maxpc. +// Callers should use Table's LineToPC method instead. +func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 { + if t.isGo12() { + return 0 + } + _, pc, line1 := t.parse(maxpc, line) + if line1 != line { + return 0 + } + // Subtract quantum from PC to account for post-line increment + return pc - oldQuantum +} + +// NewLineTable returns a new PC/line table +// corresponding to the encoded data. +// Text must be the start address of the +// corresponding text segment. +func NewLineTable(data []byte, text uint64) *LineTable { + return &LineTable{Data: data, PC: text, Line: 0} +} + +// Go 1.2 symbol table format. +// See golang.org/s/go12symtab. +// +// A general note about the methods here: rather than try to avoid +// index out of bounds errors, we trust Go to detect them, and then +// we recover from the panics and treat them as indicative of a malformed +// or incomplete table. +// +// The methods called by symtab.go, which begin with "go12" prefixes, +// are expected to have that recovery logic. + +// isGo12 reports whether this is a Go 1.2 (or later) symbol table. +func (t *LineTable) isGo12() bool { + t.go12Init() + return t.go12 == 1 +} + +const go12magic = 0xfffffffb + +// uintptr returns the pointer-sized value encoded at b. +// The pointer size is dictated by the table being read. +func (t *LineTable) uintptr(b []byte) uint64 { + if t.ptrsize == 4 { + return uint64(t.binary.Uint32(b)) + } + return t.binary.Uint64(b) +} + +// go12init initializes the Go 1.2 metadata if t is a Go 1.2 symbol table. +func (t *LineTable) go12Init() { + t.mu.Lock() + defer t.mu.Unlock() + if t.go12 != 0 { + return + } + + defer func() { + // If we panic parsing, assume it's not a Go 1.2 symbol table. + recover() + }() + + // Check header: 4-byte magic, two zeros, pc quantum, pointer size. + t.go12 = -1 // not Go 1.2 until proven otherwise + if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 || + (t.Data[6] != 1 && t.Data[6] != 4) || // pc quantum + (t.Data[7] != 4 && t.Data[7] != 8) { // pointer size + return + } + + switch uint32(go12magic) { + case binary.LittleEndian.Uint32(t.Data): + t.binary = binary.LittleEndian + case binary.BigEndian.Uint32(t.Data): + t.binary = binary.BigEndian + default: + return + } + + t.quantum = uint32(t.Data[6]) + t.ptrsize = uint32(t.Data[7]) + + t.nfunctab = uint32(t.uintptr(t.Data[8:])) + t.functab = t.Data[8+t.ptrsize:] + functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize + fileoff := t.binary.Uint32(t.functab[functabsize:]) + t.functab = t.functab[:functabsize] + t.filetab = t.Data[fileoff:] + t.nfiletab = t.binary.Uint32(t.filetab) + t.filetab = t.filetab[:t.nfiletab*4] + + t.go12 = 1 // so far so good +} + +// go12Funcs returns a slice of Funcs derived from the Go 1.2 pcln table. +func (t *LineTable) go12Funcs() []Func { + // Assume it is malformed and return nil on error. + defer func() { + recover() + }() + + n := len(t.functab) / int(t.ptrsize) / 2 + funcs := make([]Func, n) + for i := range funcs { + f := &funcs[i] + f.Entry = uint64(t.uintptr(t.functab[2*i*int(t.ptrsize):])) + f.End = uint64(t.uintptr(t.functab[(2*i+2)*int(t.ptrsize):])) + info := t.Data[t.uintptr(t.functab[(2*i+1)*int(t.ptrsize):]):] + f.LineTable = t + f.FrameSize = int(t.binary.Uint32(info[t.ptrsize+2*4:])) + f.Sym = &Sym{ + Value: f.Entry, + Type: 'T', + Name: t.string(t.binary.Uint32(info[t.ptrsize:])), + GoType: 0, + Func: f, + } + } + return funcs +} + +// findFunc returns the func corresponding to the given program counter. +func (t *LineTable) findFunc(pc uint64) []byte { + if pc < t.uintptr(t.functab) || pc >= t.uintptr(t.functab[len(t.functab)-int(t.ptrsize):]) { + return nil + } + + // The function table is a list of 2*nfunctab+1 uintptrs, + // alternating program counters and offsets to func structures. + f := t.functab + nf := t.nfunctab + for nf > 0 { + m := nf / 2 + fm := f[2*t.ptrsize*m:] + if t.uintptr(fm) <= pc && pc < t.uintptr(fm[2*t.ptrsize:]) { + return t.Data[t.uintptr(fm[t.ptrsize:]):] + } else if pc < t.uintptr(fm) { + nf = m + } else { + f = f[(m+1)*2*t.ptrsize:] + nf -= m + 1 + } + } + return nil +} + +// readvarint reads, removes, and returns a varint from *pp. +func (t *LineTable) readvarint(pp *[]byte) uint32 { + var v, shift uint32 + p := *pp + for shift = 0; ; shift += 7 { + b := p[0] + p = p[1:] + v |= (uint32(b) & 0x7F) << shift + if b&0x80 == 0 { + break + } + } + *pp = p + return v +} + +// string returns a Go string found at off. +func (t *LineTable) string(off uint32) string { + for i := off; ; i++ { + if t.Data[i] == 0 { + return string(t.Data[off:i]) + } + } +} + +// step advances to the next pc, value pair in the encoded table. +func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool { + uvdelta := t.readvarint(p) + if uvdelta == 0 && !first { + return false + } + if uvdelta&1 != 0 { + uvdelta = ^(uvdelta >> 1) + } else { + uvdelta >>= 1 + } + vdelta := int32(uvdelta) + pcdelta := t.readvarint(p) * t.quantum + *pc += uint64(pcdelta) + *val += vdelta + return true +} + +// pcvalue reports the value associated with the target pc. +// off is the offset to the beginning of the pc-value table, +// and entry is the start PC for the corresponding function. +func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 { + if off == 0 { + return -1 + } + p := t.Data[off:] + + val := int32(-1) + pc := entry + for t.step(&p, &pc, &val, pc == entry) { + if targetpc < pc { + return val + } + } + return -1 +} + +// findFileLine scans one function in the binary looking for a +// program counter in the given file on the given line. +// It does so by running the pc-value tables mapping program counter +// to file number. Since most functions come from a single file, these +// are usually short and quick to scan. If a file match is found, then the +// code goes to the expense of looking for a simultaneous line number match. +func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32) uint64 { + if filetab == 0 || linetab == 0 { + return 0 + } + + fp := t.Data[filetab:] + fl := t.Data[linetab:] + fileVal := int32(-1) + filePC := entry + lineVal := int32(-1) + linePC := entry + fileStartPC := filePC + for t.step(&fp, &filePC, &fileVal, filePC == entry) { + if fileVal == filenum && fileStartPC < filePC { + // fileVal is in effect starting at fileStartPC up to + // but not including filePC, and it's the file we want. + // Run the PC table looking for a matching line number + // or until we reach filePC. + lineStartPC := linePC + for linePC < filePC && t.step(&fl, &linePC, &lineVal, linePC == entry) { + // lineVal is in effect until linePC, and lineStartPC < filePC. + if lineVal == line { + if fileStartPC <= lineStartPC { + return lineStartPC + } + if fileStartPC < linePC { + return fileStartPC + } + } + lineStartPC = linePC + } + } + fileStartPC = filePC + } + return 0 +} + +// go12PCToLine maps program counter to line number for the Go 1.2 pcln table. +func (t *LineTable) go12PCToLine(pc uint64) (line int) { + defer func() { + if recover() != nil { + line = -1 + } + }() + + f := t.findFunc(pc) + if f == nil { + return -1 + } + entry := t.uintptr(f) + linetab := t.binary.Uint32(f[t.ptrsize+5*4:]) + return int(t.pcvalue(linetab, entry, pc)) +} + +// go12PCToFile maps program counter to file name for the Go 1.2 pcln table. +func (t *LineTable) go12PCToFile(pc uint64) (file string) { + defer func() { + if recover() != nil { + file = "" + } + }() + + f := t.findFunc(pc) + if f == nil { + return "" + } + entry := t.uintptr(f) + filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) + fno := t.pcvalue(filetab, entry, pc) + if fno <= 0 { + return "" + } + return t.string(t.binary.Uint32(t.filetab[4*fno:])) +} + +// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table. +func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) { + defer func() { + if recover() != nil { + pc = 0 + } + }() + + t.initFileMap() + filenum := t.fileMap[file] + if filenum == 0 { + return 0 + } + + // Scan all functions. + // If this turns out to be a bottleneck, we could build a map[int32][]int32 + // mapping file number to a list of functions with code from that file. + for i := uint32(0); i < t.nfunctab; i++ { + f := t.Data[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):] + entry := t.uintptr(f) + filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) + linetab := t.binary.Uint32(f[t.ptrsize+5*4:]) + pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line)) + if pc != 0 { + return pc + } + } + return 0 +} + +// initFileMap initializes the map from file name to file number. +func (t *LineTable) initFileMap() { + t.mu.Lock() + defer t.mu.Unlock() + + if t.fileMap != nil { + return + } + m := make(map[string]uint32) + + for i := uint32(1); i < t.nfiletab; i++ { + s := t.string(t.binary.Uint32(t.filetab[4*i:])) + m[s] = i + } + t.fileMap = m +} + +// go12MapFiles adds to m a key for every file in the Go 1.2 LineTable. +// Every key maps to obj. That's not a very interesting map, but it provides +// a way for callers to obtain the list of files in the program. +func (t *LineTable) go12MapFiles(m map[string]*Obj, obj *Obj) { + defer func() { + recover() + }() + + t.initFileMap() + for file := range t.fileMap { + m[file] = obj + } +} diff --git a/gosym/pclntab_test.go b/gosym/pclntab_test.go new file mode 100644 index 0000000..35502e8 --- /dev/null +++ b/gosym/pclntab_test.go @@ -0,0 +1,274 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gosym + +import ( + "debug/elf" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +var ( + pclineTempDir string + pclinetestBinary string +) + +func dotest(self bool) bool { + // For now, only works on amd64 platforms. + if runtime.GOARCH != "amd64" { + return false + } + // Self test reads test binary; only works on Linux. + if self && runtime.GOOS != "linux" { + return false + } + // Command below expects "sh", so Unix. + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + return false + } + if pclinetestBinary != "" { + return true + } + var err error + pclineTempDir, err = ioutil.TempDir("", "pclinetest") + if err != nil { + panic(err) + } + if strings.Contains(pclineTempDir, " ") { + panic("unexpected space in tempdir") + } + // This command builds pclinetest from pclinetest.asm; + // the resulting binary looks like it was built from pclinetest.s, + // but we have renamed it to keep it away from the go tool. + pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest") + command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -H linux -E main -o %s %s.6", + pclinetestBinary, pclinetestBinary, pclinetestBinary) + cmd := exec.Command("sh", "-c", command) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + panic(err) + } + return true +} + +func endtest() { + if pclineTempDir != "" { + os.RemoveAll(pclineTempDir) + pclineTempDir = "" + pclinetestBinary = "" + } +} + +func getTable(t *testing.T) *Table { + f, tab := crack(os.Args[0], t) + f.Close() + return tab +} + +func crack(file string, t *testing.T) (*elf.File, *Table) { + // Open self + f, err := elf.Open(file) + if err != nil { + t.Fatal(err) + } + return parse(file, f, t) +} + +func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) { + symdat, err := f.Section(".gosymtab").Data() + if err != nil { + f.Close() + t.Fatalf("reading %s gosymtab: %v", file, err) + } + pclndat, err := f.Section(".gopclntab").Data() + if err != nil { + f.Close() + t.Fatalf("reading %s gopclntab: %v", file, err) + } + + pcln := NewLineTable(pclndat, f.Section(".text").Addr) + tab, err := NewTable(symdat, pcln) + if err != nil { + f.Close() + t.Fatalf("parsing %s gosymtab: %v", file, err) + } + + return f, tab +} + +var goarch = os.Getenv("O") + +func TestLineFromAline(t *testing.T) { + if !dotest(true) { + return + } + defer endtest() + + tab := getTable(t) + if tab.go12line != nil { + // aline's don't exist in the Go 1.2 table. + t.Skip("not relevant to Go 1.2 symbol table") + } + + // Find the sym package + pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj + if pkg == nil { + t.Fatalf("nil pkg") + } + + // Walk every absolute line and ensure that we hit every + // source line monotonically + lastline := make(map[string]int) + final := -1 + for i := 0; i < 10000; i++ { + path, line := pkg.lineFromAline(i) + // Check for end of object + if path == "" { + if final == -1 { + final = i - 1 + } + continue + } else if final != -1 { + t.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final, i, path, line) + } + // It's okay to see files multiple times (e.g., sys.a) + if line == 1 { + lastline[path] = 1 + continue + } + // Check that the is the next line in path + ll, ok := lastline[path] + if !ok { + t.Errorf("file %s starts on line %d", path, line) + } else if line != ll+1 { + t.Fatalf("expected next line of file %s to be %d, got %d", path, ll+1, line) + } + lastline[path] = line + } + if final == -1 { + t.Errorf("never reached end of object") + } +} + +func TestLineAline(t *testing.T) { + if !dotest(true) { + return + } + defer endtest() + + tab := getTable(t) + if tab.go12line != nil { + // aline's don't exist in the Go 1.2 table. + t.Skip("not relevant to Go 1.2 symbol table") + } + + for _, o := range tab.Files { + // A source file can appear multiple times in a + // object. alineFromLine will always return alines in + // the first file, so track which lines we've seen. + found := make(map[string]int) + for i := 0; i < 1000; i++ { + path, line := o.lineFromAline(i) + if path == "" { + break + } + + // cgo files are full of 'Z' symbols, which we don't handle + if len(path) > 4 && path[len(path)-4:] == ".cgo" { + continue + } + + if minline, ok := found[path]; path != "" && ok { + if minline >= line { + // We've already covered this file + continue + } + } + found[path] = line + + a, err := o.alineFromLine(path, line) + if err != nil { + t.Errorf("absolute line %d in object %s maps to %s:%d, but mapping that back gives error %s", i, o.Paths[0].Name, path, line, err) + } else if a != i { + t.Errorf("absolute line %d in object %s maps to %s:%d, which maps back to absolute line %d\n", i, o.Paths[0].Name, path, line, a) + } + } + } +} + +func TestPCLine(t *testing.T) { + if !dotest(false) { + return + } + defer endtest() + + f, tab := crack(pclinetestBinary, t) + text := f.Section(".text") + textdat, err := text.Data() + if err != nil { + t.Fatalf("reading .text: %v", err) + } + + // Test PCToLine + sym := tab.LookupFunc("linefrompc") + wantLine := 0 + for pc := sym.Entry; pc < sym.End; pc++ { + off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g + if textdat[off] == 255 { + break + } + wantLine += int(textdat[off]) + t.Logf("off is %d %#x (max %d)", off, textdat[off], sym.End-pc) + file, line, fn := tab.PCToLine(pc) + if fn == nil { + t.Errorf("failed to get line of PC %#x", pc) + } else if !strings.HasSuffix(file, "pclinetest.asm") || line != wantLine || fn != sym { + t.Errorf("PCToLine(%#x) = %s:%d (%s), want %s:%d (%s)", pc, file, line, fn.Name, "pclinetest.asm", wantLine, sym.Name) + } + } + + // Test LineToPC + sym = tab.LookupFunc("pcfromline") + lookupline := -1 + wantLine = 0 + off := uint64(0) // TODO(rsc): should not need off; bug in 8g + for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) { + file, line, fn := tab.PCToLine(pc) + off = pc - text.Addr + if textdat[off] == 255 { + break + } + wantLine += int(textdat[off]) + if line != wantLine { + t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line) + off = pc + 1 - text.Addr + continue + } + if lookupline == -1 { + lookupline = line + } + for ; lookupline <= line; lookupline++ { + pc2, fn2, err := tab.LineToPC(file, lookupline) + if lookupline != line { + // Should be nothing on this line + if err == nil { + t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name) + } + } else if err != nil { + t.Errorf("failed to get PC of line %d: %s", lookupline, err) + } else if pc != pc2 { + t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name) + } + } + off = pc + 1 - text.Addr + } +} diff --git a/gosym/symtab.go b/gosym/symtab.go new file mode 100644 index 0000000..ee18499 --- /dev/null +++ b/gosym/symtab.go @@ -0,0 +1,710 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gosym implements access to the Go symbol +// and line number tables embedded in Go binaries generated +// by the gc compilers. +package gosym + +// The table format is a variant of the format used in Plan 9's a.out +// format, documented at http://plan9.bell-labs.com/magic/man2html/6/a.out. +// The best reference for the differences between the Plan 9 format +// and the Go format is the runtime source, specifically ../../runtime/symtab.c. + +import ( + "bytes" + "encoding/binary" + "fmt" + "strconv" + "strings" +) + +/* + * Symbols + */ + +// A Sym represents a single symbol table entry. +type Sym struct { + Value uint64 + Type byte + Name string + GoType uint64 + // If this symbol if a function symbol, the corresponding Func + Func *Func +} + +// Static reports whether this symbol is static (not visible outside its file). +func (s *Sym) Static() bool { return s.Type >= 'a' } + +// PackageName returns the package part of the symbol name, +// or the empty string if there is none. +func (s *Sym) PackageName() string { + if i := strings.Index(s.Name, "."); i != -1 { + return s.Name[0:i] + } + return "" +} + +// ReceiverName returns the receiver type name of this symbol, +// or the empty string if there is none. +func (s *Sym) ReceiverName() string { + l := strings.Index(s.Name, ".") + r := strings.LastIndex(s.Name, ".") + if l == -1 || r == -1 || l == r { + return "" + } + return s.Name[l+1 : r] +} + +// BaseName returns the symbol name without the package or receiver name. +func (s *Sym) BaseName() string { + if i := strings.LastIndex(s.Name, "."); i != -1 { + return s.Name[i+1:] + } + return s.Name +} + +// A Func collects information about a single function. +type Func struct { + Entry uint64 + *Sym + End uint64 + Params []*Sym + Locals []*Sym + FrameSize int + LineTable *LineTable + Obj *Obj +} + +// An Obj represents a collection of functions in a symbol table. +// +// The exact method of division of a binary into separate Objs is an internal detail +// of the symbol table format. +// +// In early versions of Go each source file became a different Obj. +// +// In Go 1 and Go 1.1, each package produced one Obj for all Go sources +// and one Obj per C source file. +// +// In Go 1.2, there is a single Obj for the entire program. +type Obj struct { + // Funcs is a list of functions in the Obj. + Funcs []Func + + // In Go 1.1 and earlier, Paths is a list of symbols corresponding + // to the source file names that produced the Obj. + // In Go 1.2, Paths is nil. + // Use the keys of Table.Files to obtain a list of source files. + Paths []Sym // meta +} + +/* + * Symbol tables + */ + +// Table represents a Go symbol table. It stores all of the +// symbols decoded from the program and provides methods to translate +// between symbols, names, and addresses. +type Table struct { + Syms []Sym + Funcs []Func + Files map[string]*Obj // nil for Go 1.2 and later binaries + Objs []Obj // nil for Go 1.2 and later binaries + + go12line *LineTable // Go 1.2 line number table +} + +type sym struct { + value uint64 + gotype uint64 + typ byte + name []byte +} + +var ( + littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00} + bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00} + oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00} +) + +func walksymtab(data []byte, fn func(sym) error) error { + if len(data) == 0 { // missing symtab is okay + return nil + } + var order binary.ByteOrder = binary.BigEndian + newTable := false + switch { + case bytes.HasPrefix(data, oldLittleEndianSymtab): + // Same as Go 1.0, but little endian. + // Format was used during interim development between Go 1.0 and Go 1.1. + // Should not be widespread, but easy to support. + data = data[6:] + order = binary.LittleEndian + case bytes.HasPrefix(data, bigEndianSymtab): + newTable = true + case bytes.HasPrefix(data, littleEndianSymtab): + newTable = true + order = binary.LittleEndian + } + var ptrsz int + if newTable { + if len(data) < 8 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + ptrsz = int(data[7]) + if ptrsz != 4 && ptrsz != 8 { + return &DecodingError{7, "invalid pointer size", ptrsz} + } + data = data[8:] + } + var s sym + p := data + for len(p) >= 4 { + var typ byte + if newTable { + // Symbol type, value, Go type. + typ = p[0] & 0x3F + wideValue := p[0]&0x40 != 0 + goType := p[0]&0x80 != 0 + if typ < 26 { + typ += 'A' + } else { + typ += 'a' - 26 + } + s.typ = typ + p = p[1:] + if wideValue { + if len(p) < ptrsz { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // fixed-width value + if ptrsz == 8 { + s.value = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.value = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + } else { + // varint value + s.value = 0 + shift := uint(0) + for len(p) > 0 && p[0]&0x80 != 0 { + s.value |= uint64(p[0]&0x7F) << shift + shift += 7 + p = p[1:] + } + if len(p) == 0 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + s.value |= uint64(p[0]) << shift + p = p[1:] + } + if goType { + if len(p) < ptrsz { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // fixed-width go type + if ptrsz == 8 { + s.gotype = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.gotype = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + } + } else { + // Value, symbol type. + s.value = uint64(order.Uint32(p[0:4])) + if len(p) < 5 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + typ = p[4] + if typ&0x80 == 0 { + return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ} + } + typ &^= 0x80 + s.typ = typ + p = p[5:] + } + + // Name. + var i int + var nnul int + for i = 0; i < len(p); i++ { + if p[i] == 0 { + nnul = 1 + break + } + } + switch typ { + case 'z', 'Z': + p = p[i+nnul:] + for i = 0; i+2 <= len(p); i += 2 { + if p[i] == 0 && p[i+1] == 0 { + nnul = 2 + break + } + } + } + if len(p) < i+nnul { + return &DecodingError{len(data), "unexpected EOF", nil} + } + s.name = p[0:i] + i += nnul + p = p[i:] + + if !newTable { + if len(p) < 4 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // Go type. + s.gotype = uint64(order.Uint32(p[:4])) + p = p[4:] + } + fn(s) + } + return nil +} + +// NewTable decodes the Go symbol table in data, +// returning an in-memory representation. +func NewTable(symtab []byte, pcln *LineTable) (*Table, error) { + var n int + err := walksymtab(symtab, func(s sym) error { + n++ + return nil + }) + if err != nil { + return nil, err + } + + var t Table + if pcln.isGo12() { + t.go12line = pcln + } + fname := make(map[uint16]string) + t.Syms = make([]Sym, 0, n) + nf := 0 + nz := 0 + lasttyp := uint8(0) + err = walksymtab(symtab, func(s sym) error { + n := len(t.Syms) + t.Syms = t.Syms[0 : n+1] + ts := &t.Syms[n] + ts.Type = s.typ + ts.Value = uint64(s.value) + ts.GoType = uint64(s.gotype) + switch s.typ { + default: + // rewrite name to use . instead of · (c2 b7) + w := 0 + b := s.name + for i := 0; i < len(b); i++ { + if b[i] == 0xc2 && i+1 < len(b) && b[i+1] == 0xb7 { + i++ + b[i] = '.' + } + b[w] = b[i] + w++ + } + ts.Name = string(s.name[0:w]) + case 'z', 'Z': + if lasttyp != 'z' && lasttyp != 'Z' { + nz++ + } + for i := 0; i < len(s.name); i += 2 { + eltIdx := binary.BigEndian.Uint16(s.name[i : i+2]) + elt, ok := fname[eltIdx] + if !ok { + return &DecodingError{-1, "bad filename code", eltIdx} + } + if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' { + ts.Name += "/" + } + ts.Name += elt + } + } + switch s.typ { + case 'T', 't', 'L', 'l': + nf++ + case 'f': + fname[uint16(s.value)] = ts.Name + } + lasttyp = s.typ + return nil + }) + if err != nil { + return nil, err + } + + t.Funcs = make([]Func, 0, nf) + t.Files = make(map[string]*Obj) + + var obj *Obj + if t.go12line != nil { + // Put all functions into one Obj. + t.Objs = make([]Obj, 1) + obj = &t.Objs[0] + t.go12line.go12MapFiles(t.Files, obj) + } else { + t.Objs = make([]Obj, 0, nz) + } + + // Count text symbols and attach frame sizes, parameters, and + // locals to them. Also, find object file boundaries. + lastf := 0 + for i := 0; i < len(t.Syms); i++ { + sym := &t.Syms[i] + switch sym.Type { + case 'Z', 'z': // path symbol + if t.go12line != nil { + // Go 1.2 binaries have the file information elsewhere. Ignore. + break + } + // Finish the current object + if obj != nil { + obj.Funcs = t.Funcs[lastf:] + } + lastf = len(t.Funcs) + + // Start new object + n := len(t.Objs) + t.Objs = t.Objs[0 : n+1] + obj = &t.Objs[n] + + // Count & copy path symbols + var end int + for end = i + 1; end < len(t.Syms); end++ { + if c := t.Syms[end].Type; c != 'Z' && c != 'z' { + break + } + } + obj.Paths = t.Syms[i:end] + i = end - 1 // loop will i++ + + // Record file names + depth := 0 + for j := range obj.Paths { + s := &obj.Paths[j] + if s.Name == "" { + depth-- + } else { + if depth == 0 { + t.Files[s.Name] = obj + } + depth++ + } + } + + case 'T', 't', 'L', 'l': // text symbol + if n := len(t.Funcs); n > 0 { + t.Funcs[n-1].End = sym.Value + } + if sym.Name == "runtime.etext" || sym.Name == "etext" { + continue + } + + // Count parameter and local (auto) syms + var np, na int + var end int + countloop: + for end = i + 1; end < len(t.Syms); end++ { + switch t.Syms[end].Type { + case 'T', 't', 'L', 'l', 'Z', 'z': + break countloop + case 'p': + np++ + case 'a': + na++ + } + } + + // Fill in the function symbol + n := len(t.Funcs) + t.Funcs = t.Funcs[0 : n+1] + fn := &t.Funcs[n] + sym.Func = fn + fn.Params = make([]*Sym, 0, np) + fn.Locals = make([]*Sym, 0, na) + fn.Sym = sym + fn.Entry = sym.Value + fn.Obj = obj + if t.go12line != nil { + // All functions share the same line table. + // It knows how to narrow down to a specific + // function quickly. + fn.LineTable = t.go12line + } else if pcln != nil { + fn.LineTable = pcln.slice(fn.Entry) + pcln = fn.LineTable + } + for j := i; j < end; j++ { + s := &t.Syms[j] + switch s.Type { + case 'm': + fn.FrameSize = int(s.Value) + case 'p': + n := len(fn.Params) + fn.Params = fn.Params[0 : n+1] + fn.Params[n] = s + case 'a': + n := len(fn.Locals) + fn.Locals = fn.Locals[0 : n+1] + fn.Locals[n] = s + } + } + i = end - 1 // loop will i++ + } + } + + if t.go12line != nil && nf == 0 { + t.Funcs = t.go12line.go12Funcs() + } + if obj != nil { + obj.Funcs = t.Funcs[lastf:] + } + return &t, nil +} + +// PCToFunc returns the function containing the program counter pc, +// or nil if there is no such function. +func (t *Table) PCToFunc(pc uint64) *Func { + funcs := t.Funcs + for len(funcs) > 0 { + m := len(funcs) / 2 + fn := &funcs[m] + switch { + case pc < fn.Entry: + funcs = funcs[0:m] + case fn.Entry <= pc && pc < fn.End: + return fn + default: + funcs = funcs[m+1:] + } + } + return nil +} + +// PCToLine looks up line number information for a program counter. +// If there is no information, it returns fn == nil. +func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) { + if fn = t.PCToFunc(pc); fn == nil { + return + } + if t.go12line != nil { + file = t.go12line.go12PCToFile(pc) + line = t.go12line.go12PCToLine(pc) + } else { + file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc)) + } + return +} + +// LineToPC looks up the first program counter on the given line in +// the named file. It returns UnknownPathError or UnknownLineError if +// there is an error looking up this line. +func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) { + obj, ok := t.Files[file] + if !ok { + return 0, nil, UnknownFileError(file) + } + + if t.go12line != nil { + pc := t.go12line.go12LineToPC(file, line) + if pc == 0 { + return 0, nil, &UnknownLineError{file, line} + } + return pc, t.PCToFunc(pc), nil + } + + abs, err := obj.alineFromLine(file, line) + if err != nil { + return + } + for i := range obj.Funcs { + f := &obj.Funcs[i] + pc := f.LineTable.LineToPC(abs, f.End) + if pc != 0 { + return pc, f, nil + } + } + return 0, nil, &UnknownLineError{file, line} +} + +// LookupSym returns the text, data, or bss symbol with the given name, +// or nil if no such symbol is found. +func (t *Table) LookupSym(name string) *Sym { + // TODO(austin) Maybe make a map + for i := range t.Syms { + s := &t.Syms[i] + switch s.Type { + case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b': + if s.Name == name { + return s + } + } + } + return nil +} + +// LookupFunc returns the text, data, or bss symbol with the given name, +// or nil if no such symbol is found. +func (t *Table) LookupFunc(name string) *Func { + for i := range t.Funcs { + f := &t.Funcs[i] + if f.Sym.Name == name { + return f + } + } + return nil +} + +// SymByAddr returns the text, data, or bss symbol starting at the given address. +func (t *Table) SymByAddr(addr uint64) *Sym { + for i := range t.Syms { + s := &t.Syms[i] + switch s.Type { + case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b': + if s.Value == addr { + return s + } + } + } + return nil +} + +/* + * Object files + */ + +// This is legacy code for Go 1.1 and earlier, which used the +// Plan 9 format for pc-line tables. This code was never quite +// correct. It's probably very close, and it's usually correct, but +// we never quite found all the corner cases. +// +// Go 1.2 and later use a simpler format, documented at golang.org/s/go12symtab. + +func (o *Obj) lineFromAline(aline int) (string, int) { + type stackEnt struct { + path string + start int + offset int + prev *stackEnt + } + + noPath := &stackEnt{"", 0, 0, nil} + tos := noPath + +pathloop: + for _, s := range o.Paths { + val := int(s.Value) + switch { + case val > aline: + break pathloop + + case val == 1: + // Start a new stack + tos = &stackEnt{s.Name, val, 0, noPath} + + case s.Name == "": + // Pop + if tos == noPath { + return "", 0 + } + tos.prev.offset += val - tos.start + tos = tos.prev + + default: + // Push + tos = &stackEnt{s.Name, val, 0, tos} + } + } + + if tos == noPath { + return "", 0 + } + return tos.path, aline - tos.start - tos.offset + 1 +} + +func (o *Obj) alineFromLine(path string, line int) (int, error) { + if line < 1 { + return 0, &UnknownLineError{path, line} + } + + for i, s := range o.Paths { + // Find this path + if s.Name != path { + continue + } + + // Find this line at this stack level + depth := 0 + var incstart int + line += int(s.Value) + pathloop: + for _, s := range o.Paths[i:] { + val := int(s.Value) + switch { + case depth == 1 && val >= line: + return line - 1, nil + + case s.Name == "": + depth-- + if depth == 0 { + break pathloop + } else if depth == 1 { + line += val - incstart + } + + default: + if depth == 1 { + incstart = val + } + depth++ + } + } + return 0, &UnknownLineError{path, line} + } + return 0, UnknownFileError(path) +} + +/* + * Errors + */ + +// UnknownFileError represents a failure to find the specific file in +// the symbol table. +type UnknownFileError string + +func (e UnknownFileError) Error() string { return "unknown file: " + string(e) } + +// UnknownLineError represents a failure to map a line to a program +// counter, either because the line is beyond the bounds of the file +// or because there is no code on the given line. +type UnknownLineError struct { + File string + Line int +} + +func (e *UnknownLineError) Error() string { + return "no code at " + e.File + ":" + strconv.Itoa(e.Line) +} + +// DecodingError represents an error during the decoding of +// the symbol table. +type DecodingError struct { + off int + msg string + val interface{} +} + +func (e *DecodingError) Error() string { + msg := e.msg + if e.val != nil { + msg += fmt.Sprintf(" '%v'", e.val) + } + msg += fmt.Sprintf(" at byte %#x", e.off) + return msg +} diff --git a/macho/fat.go b/macho/fat.go new file mode 100644 index 0000000..93b8315 --- /dev/null +++ b/macho/fat.go @@ -0,0 +1,146 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package macho + +import ( + "encoding/binary" + "fmt" + "io" + "os" +) + +// A FatFile is a Mach-O universal binary that contains at least one architecture. +type FatFile struct { + Magic uint32 + Arches []FatArch + closer io.Closer +} + +// A FatArchHeader represents a fat header for a specific image architecture. +type FatArchHeader struct { + Cpu Cpu + SubCpu uint32 + Offset uint32 + Size uint32 + Align uint32 +} + +const fatArchHeaderSize = 5 * 4 + +// A FatArch is a Mach-O File inside a FatFile. +type FatArch struct { + FatArchHeader + *File +} + +// ErrNotFat is returned from NewFatFile or OpenFat when the file is not a +// universal binary but may be a thin binary, based on its magic number. +var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil} + +// NewFatFile creates a new FatFile for accessing all the Mach-O images in a +// universal binary. The Mach-O binary is expected to start at position 0 in +// the ReaderAt. +func NewFatFile(r io.ReaderAt) (*FatFile, error) { + var ff FatFile + sr := io.NewSectionReader(r, 0, 1<<63-1) + + // Read the fat_header struct, which is always in big endian. + // Start with the magic number. + err := binary.Read(sr, binary.BigEndian, &ff.Magic) + if err != nil { + return nil, &FormatError{0, "error reading magic number", nil} + } else if ff.Magic != MagicFat { + // See if this is a Mach-O file via its magic number. The magic + // must be converted to little endian first though. + var buf [4]byte + binary.BigEndian.PutUint32(buf[:], ff.Magic) + leMagic := binary.LittleEndian.Uint32(buf[:]) + if leMagic == Magic32 || leMagic == Magic64 { + return nil, ErrNotFat + } else { + return nil, &FormatError{0, "invalid magic number", nil} + } + } + offset := int64(4) + + // Read the number of FatArchHeaders that come after the fat_header. + var narch uint32 + err = binary.Read(sr, binary.BigEndian, &narch) + if err != nil { + return nil, &FormatError{offset, "invalid fat_header", nil} + } + offset += 4 + + if narch < 1 { + return nil, &FormatError{offset, "file contains no images", nil} + } + + // Combine the Cpu and SubCpu (both uint32) into a uint64 to make sure + // there are not duplicate architectures. + seenArches := make(map[uint64]bool, narch) + // Make sure that all images are for the same MH_ type. + var machoType Type + + // Following the fat_header comes narch fat_arch structs that index + // Mach-O images further in the file. + ff.Arches = make([]FatArch, narch) + for i := uint32(0); i < narch; i++ { + fa := &ff.Arches[i] + err = binary.Read(sr, binary.BigEndian, &fa.FatArchHeader) + if err != nil { + return nil, &FormatError{offset, "invalid fat_arch header", nil} + } + offset += fatArchHeaderSize + + fr := io.NewSectionReader(r, int64(fa.Offset), int64(fa.Size)) + fa.File, err = NewFile(fr) + if err != nil { + return nil, err + } + + // Make sure the architecture for this image is not duplicate. + seenArch := (uint64(fa.Cpu) << 32) | uint64(fa.SubCpu) + if o, k := seenArches[seenArch]; o || k { + return nil, &FormatError{offset, fmt.Sprintf("duplicate architecture cpu=%v, subcpu=%#x", fa.Cpu, fa.SubCpu), nil} + } + seenArches[seenArch] = true + + // Make sure the Mach-O type matches that of the first image. + if i == 0 { + machoType = fa.Type + } else { + if fa.Type != machoType { + return nil, &FormatError{offset, fmt.Sprintf("Mach-O type for architecture #%d (type=%#x) does not match first (type=%#x)", i, fa.Type, machoType), nil} + } + } + } + + return &ff, nil +} + +// OpenFat opens the named file using os.Open and prepares it for use as a Mach-O +// universal binary. +func OpenFat(name string) (ff *FatFile, err error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + ff, err = NewFatFile(f) + if err != nil { + f.Close() + return nil, err + } + ff.closer = f + return +} + +func (ff *FatFile) Close() error { + var err error + if ff.closer != nil { + err = ff.closer.Close() + ff.closer = nil + } + return err +} diff --git a/macho/file.go b/macho/file.go new file mode 100644 index 0000000..eefb744 --- /dev/null +++ b/macho/file.go @@ -0,0 +1,524 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package macho implements access to Mach-O object files. +package macho + +// High level access to low level data structures. + +import ( + "bytes" + "debug/dwarf" + "encoding/binary" + "fmt" + "io" + "os" +) + +// A File represents an open Mach-O file. +type File struct { + FileHeader + ByteOrder binary.ByteOrder + Loads []Load + Sections []*Section + + Symtab *Symtab + Dysymtab *Dysymtab + + closer io.Closer +} + +// A Load represents any Mach-O load command. +type Load interface { + Raw() []byte +} + +// A LoadBytes is the uninterpreted bytes of a Mach-O load command. +type LoadBytes []byte + +func (b LoadBytes) Raw() []byte { return b } + +// A SegmentHeader is the header for a Mach-O 32-bit or 64-bit load segment command. +type SegmentHeader struct { + Cmd LoadCmd + Len uint32 + Name string + Addr uint64 + Memsz uint64 + Offset uint64 + Filesz uint64 + Maxprot uint32 + Prot uint32 + Nsect uint32 + Flag uint32 +} + +// A Segment represents a Mach-O 32-bit or 64-bit load segment command. +type Segment struct { + LoadBytes + SegmentHeader + + // Embed ReaderAt for ReadAt method. + // Do not embed SectionReader directly + // to avoid having Read and Seek. + // If a client wants Read and Seek it must use + // Open() to avoid fighting over the seek offset + // with other clients. + io.ReaderAt + sr *io.SectionReader +} + +// Data reads and returns the contents of the segment. +func (s *Segment) Data() ([]byte, error) { + dat := make([]byte, s.sr.Size()) + n, err := s.sr.ReadAt(dat, 0) + if n == len(dat) { + err = nil + } + return dat[0:n], err +} + +// Open returns a new ReadSeeker reading the segment. +func (s *Segment) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } + +type SectionHeader struct { + Name string + Seg string + Addr uint64 + Size uint64 + Offset uint32 + Align uint32 + Reloff uint32 + Nreloc uint32 + Flags uint32 +} + +type Section struct { + SectionHeader + + // Embed ReaderAt for ReadAt method. + // Do not embed SectionReader directly + // to avoid having Read and Seek. + // If a client wants Read and Seek it must use + // Open() to avoid fighting over the seek offset + // with other clients. + io.ReaderAt + sr *io.SectionReader +} + +// Data reads and returns the contents of the Mach-O section. +func (s *Section) Data() ([]byte, error) { + dat := make([]byte, s.sr.Size()) + n, err := s.sr.ReadAt(dat, 0) + if n == len(dat) { + err = nil + } + return dat[0:n], err +} + +// Open returns a new ReadSeeker reading the Mach-O section. +func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } + +// A Dylib represents a Mach-O load dynamic library command. +type Dylib struct { + LoadBytes + Name string + Time uint32 + CurrentVersion uint32 + CompatVersion uint32 +} + +// A Symtab represents a Mach-O symbol table command. +type Symtab struct { + LoadBytes + SymtabCmd + Syms []Symbol +} + +// A Dysymtab represents a Mach-O dynamic symbol table command. +type Dysymtab struct { + LoadBytes + DysymtabCmd + IndirectSyms []uint32 // indices into Symtab.Syms +} + +/* + * Mach-O reader + */ + +// FormatError is returned by some operations if the data does +// not have the correct format for an object file. +type FormatError struct { + off int64 + msg string + val interface{} +} + +func (e *FormatError) Error() string { + msg := e.msg + if e.val != nil { + msg += fmt.Sprintf(" '%v'", e.val) + } + msg += fmt.Sprintf(" in record at byte %#x", e.off) + return msg +} + +// Open opens the named file using os.Open and prepares it for use as a Mach-O binary. +func Open(name string) (*File, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + ff, err := NewFile(f) + if err != nil { + f.Close() + return nil, err + } + ff.closer = f + return ff, nil +} + +// Close closes the File. +// If the File was created using NewFile directly instead of Open, +// Close has no effect. +func (f *File) Close() error { + var err error + if f.closer != nil { + err = f.closer.Close() + f.closer = nil + } + return err +} + +// NewFile creates a new File for accessing a Mach-O binary in an underlying reader. +// The Mach-O binary is expected to start at position 0 in the ReaderAt. +func NewFile(r io.ReaderAt) (*File, error) { + f := new(File) + sr := io.NewSectionReader(r, 0, 1<<63-1) + + // Read and decode Mach magic to determine byte order, size. + // Magic32 and Magic64 differ only in the bottom bit. + var ident [4]byte + if _, err := r.ReadAt(ident[0:], 0); err != nil { + return nil, err + } + be := binary.BigEndian.Uint32(ident[0:]) + le := binary.LittleEndian.Uint32(ident[0:]) + switch Magic32 &^ 1 { + case be &^ 1: + f.ByteOrder = binary.BigEndian + f.Magic = be + case le &^ 1: + f.ByteOrder = binary.LittleEndian + f.Magic = le + default: + return nil, &FormatError{0, "invalid magic number", nil} + } + + // Read entire file header. + if err := binary.Read(sr, f.ByteOrder, &f.FileHeader); err != nil { + return nil, err + } + + // Then load commands. + offset := int64(fileHeaderSize32) + if f.Magic == Magic64 { + offset = fileHeaderSize64 + } + dat := make([]byte, f.Cmdsz) + if _, err := r.ReadAt(dat, offset); err != nil { + return nil, err + } + f.Loads = make([]Load, f.Ncmd) + bo := f.ByteOrder + for i := range f.Loads { + // Each load command begins with uint32 command and length. + if len(dat) < 8 { + return nil, &FormatError{offset, "command block too small", nil} + } + cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8]) + if siz < 8 || siz > uint32(len(dat)) { + return nil, &FormatError{offset, "invalid command block size", nil} + } + var cmddat []byte + cmddat, dat = dat[0:siz], dat[siz:] + offset += int64(siz) + var s *Segment + switch cmd { + default: + f.Loads[i] = LoadBytes(cmddat) + + case LoadCmdDylib: + var hdr DylibCmd + b := bytes.NewReader(cmddat) + if err := binary.Read(b, bo, &hdr); err != nil { + return nil, err + } + l := new(Dylib) + if hdr.Name >= uint32(len(cmddat)) { + return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name} + } + l.Name = cstring(cmddat[hdr.Name:]) + l.Time = hdr.Time + l.CurrentVersion = hdr.CurrentVersion + l.CompatVersion = hdr.CompatVersion + l.LoadBytes = LoadBytes(cmddat) + f.Loads[i] = l + + case LoadCmdSymtab: + var hdr SymtabCmd + b := bytes.NewReader(cmddat) + if err := binary.Read(b, bo, &hdr); err != nil { + return nil, err + } + strtab := make([]byte, hdr.Strsize) + if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil { + return nil, err + } + var symsz int + if f.Magic == Magic64 { + symsz = 16 + } else { + symsz = 12 + } + symdat := make([]byte, int(hdr.Nsyms)*symsz) + if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil { + return nil, err + } + st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset) + if err != nil { + return nil, err + } + f.Loads[i] = st + f.Symtab = st + + case LoadCmdDysymtab: + var hdr DysymtabCmd + b := bytes.NewReader(cmddat) + if err := binary.Read(b, bo, &hdr); err != nil { + return nil, err + } + dat := make([]byte, hdr.Nindirectsyms*4) + if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil { + return nil, err + } + x := make([]uint32, hdr.Nindirectsyms) + if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil { + return nil, err + } + st := new(Dysymtab) + st.LoadBytes = LoadBytes(cmddat) + st.DysymtabCmd = hdr + st.IndirectSyms = x + f.Loads[i] = st + f.Dysymtab = st + + case LoadCmdSegment: + var seg32 Segment32 + b := bytes.NewReader(cmddat) + if err := binary.Read(b, bo, &seg32); err != nil { + return nil, err + } + s = new(Segment) + s.LoadBytes = cmddat + s.Cmd = cmd + s.Len = siz + s.Name = cstring(seg32.Name[0:]) + s.Addr = uint64(seg32.Addr) + s.Memsz = uint64(seg32.Memsz) + s.Offset = uint64(seg32.Offset) + s.Filesz = uint64(seg32.Filesz) + s.Maxprot = seg32.Maxprot + s.Prot = seg32.Prot + s.Nsect = seg32.Nsect + s.Flag = seg32.Flag + f.Loads[i] = s + for i := 0; i < int(s.Nsect); i++ { + var sh32 Section32 + if err := binary.Read(b, bo, &sh32); err != nil { + return nil, err + } + sh := new(Section) + sh.Name = cstring(sh32.Name[0:]) + sh.Seg = cstring(sh32.Seg[0:]) + sh.Addr = uint64(sh32.Addr) + sh.Size = uint64(sh32.Size) + sh.Offset = sh32.Offset + sh.Align = sh32.Align + sh.Reloff = sh32.Reloff + sh.Nreloc = sh32.Nreloc + sh.Flags = sh32.Flags + f.pushSection(sh, r) + } + + case LoadCmdSegment64: + var seg64 Segment64 + b := bytes.NewReader(cmddat) + if err := binary.Read(b, bo, &seg64); err != nil { + return nil, err + } + s = new(Segment) + s.LoadBytes = cmddat + s.Cmd = cmd + s.Len = siz + s.Name = cstring(seg64.Name[0:]) + s.Addr = seg64.Addr + s.Memsz = seg64.Memsz + s.Offset = seg64.Offset + s.Filesz = seg64.Filesz + s.Maxprot = seg64.Maxprot + s.Prot = seg64.Prot + s.Nsect = seg64.Nsect + s.Flag = seg64.Flag + f.Loads[i] = s + for i := 0; i < int(s.Nsect); i++ { + var sh64 Section64 + if err := binary.Read(b, bo, &sh64); err != nil { + return nil, err + } + sh := new(Section) + sh.Name = cstring(sh64.Name[0:]) + sh.Seg = cstring(sh64.Seg[0:]) + sh.Addr = sh64.Addr + sh.Size = sh64.Size + sh.Offset = sh64.Offset + sh.Align = sh64.Align + sh.Reloff = sh64.Reloff + sh.Nreloc = sh64.Nreloc + sh.Flags = sh64.Flags + f.pushSection(sh, r) + } + } + if s != nil { + s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz)) + s.ReaderAt = s.sr + } + } + return f, nil +} + +func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) { + bo := f.ByteOrder + symtab := make([]Symbol, hdr.Nsyms) + b := bytes.NewReader(symdat) + for i := range symtab { + var n Nlist64 + if f.Magic == Magic64 { + if err := binary.Read(b, bo, &n); err != nil { + return nil, err + } + } else { + var n32 Nlist32 + if err := binary.Read(b, bo, &n32); err != nil { + return nil, err + } + n.Name = n32.Name + n.Type = n32.Type + n.Sect = n32.Sect + n.Desc = n32.Desc + n.Value = uint64(n32.Value) + } + sym := &symtab[i] + if n.Name >= uint32(len(strtab)) { + return nil, &FormatError{offset, "invalid name in symbol table", n.Name} + } + sym.Name = cstring(strtab[n.Name:]) + sym.Type = n.Type + sym.Sect = n.Sect + sym.Desc = n.Desc + sym.Value = n.Value + } + st := new(Symtab) + st.LoadBytes = LoadBytes(cmddat) + st.Syms = symtab + return st, nil +} + +func (f *File) pushSection(sh *Section, r io.ReaderAt) { + f.Sections = append(f.Sections, sh) + sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size)) + sh.ReaderAt = sh.sr +} + +func cstring(b []byte) string { + var i int + for i = 0; i < len(b) && b[i] != 0; i++ { + } + return string(b[0:i]) +} + +// Segment returns the first Segment with the given name, or nil if no such segment exists. +func (f *File) Segment(name string) *Segment { + for _, l := range f.Loads { + if s, ok := l.(*Segment); ok && s.Name == name { + return s + } + } + return nil +} + +// Section returns the first section with the given name, or nil if no such +// section exists. +func (f *File) Section(name string) *Section { + for _, s := range f.Sections { + if s.Name == name { + return s + } + } + return nil +} + +// DWARF returns the DWARF debug information for the Mach-O file. +func (f *File) DWARF() (*dwarf.Data, error) { + // There are many other DWARF sections, but these + // are the required ones, and the debug/dwarf package + // does not use the others, so don't bother loading them. + var names = [...]string{"abbrev", "info", "str"} + var dat [len(names)][]byte + for i, name := range names { + name = "__debug_" + name + s := f.Section(name) + if s == nil { + continue + } + b, err := s.Data() + if err != nil && uint64(len(b)) < s.Size { + return nil, err + } + dat[i] = b + } + + abbrev, info, str := dat[0], dat[1], dat[2] + return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str) +} + +// ImportedSymbols returns the names of all symbols +// referred to by the binary f that are expected to be +// satisfied by other libraries at dynamic load time. +func (f *File) ImportedSymbols() ([]string, error) { + if f.Dysymtab == nil || f.Symtab == nil { + return nil, &FormatError{0, "missing symbol table", nil} + } + + st := f.Symtab + dt := f.Dysymtab + var all []string + for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] { + all = append(all, s.Name) + } + return all, nil +} + +// ImportedLibraries returns the paths of all libraries +// referred to by the binary f that are expected to be +// linked with the binary at dynamic link time. +func (f *File) ImportedLibraries() ([]string, error) { + var all []string + for _, l := range f.Loads { + if lib, ok := l.(*Dylib); ok { + all = append(all, lib.Name) + } + } + return all, nil +} diff --git a/macho/file_test.go b/macho/file_test.go new file mode 100644 index 0000000..4797780 --- /dev/null +++ b/macho/file_test.go @@ -0,0 +1,210 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package macho + +import ( + "reflect" + "testing" +) + +type fileTest struct { + file string + hdr FileHeader + segments []*SegmentHeader + sections []*SectionHeader +} + +var fileTests = []fileTest{ + { + "testdata/gcc-386-darwin-exec", + FileHeader{0xfeedface, Cpu386, 0x3, 0x2, 0xc, 0x3c0, 0x85}, + []*SegmentHeader{ + {LoadCmdSegment, 0x38, "__PAGEZERO", 0x0, 0x1000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {LoadCmdSegment, 0xc0, "__TEXT", 0x1000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x2, 0x0}, + {LoadCmdSegment, 0xc0, "__DATA", 0x2000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x2, 0x0}, + {LoadCmdSegment, 0x7c, "__IMPORT", 0x3000, 0x1000, 0x2000, 0x1000, 0x7, 0x7, 0x1, 0x0}, + {LoadCmdSegment, 0x38, "__LINKEDIT", 0x4000, 0x1000, 0x3000, 0x12c, 0x7, 0x1, 0x0, 0x0}, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + }, + []*SectionHeader{ + {"__text", "__TEXT", 0x1f68, 0x88, 0xf68, 0x2, 0x0, 0x0, 0x80000400}, + {"__cstring", "__TEXT", 0x1ff0, 0xd, 0xff0, 0x0, 0x0, 0x0, 0x2}, + {"__data", "__DATA", 0x2000, 0x14, 0x1000, 0x2, 0x0, 0x0, 0x0}, + {"__dyld", "__DATA", 0x2014, 0x1c, 0x1014, 0x2, 0x0, 0x0, 0x0}, + {"__jump_table", "__IMPORT", 0x3000, 0xa, 0x2000, 0x6, 0x0, 0x0, 0x4000008}, + }, + }, + { + "testdata/gcc-amd64-darwin-exec", + FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0x2, 0xb, 0x568, 0x85}, + []*SegmentHeader{ + {LoadCmdSegment64, 0x48, "__PAGEZERO", 0x0, 0x100000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x5, 0x0}, + {LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x3, 0x0}, + {LoadCmdSegment64, 0x48, "__LINKEDIT", 0x100002000, 0x1000, 0x2000, 0x140, 0x7, 0x1, 0x0, 0x0}, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + }, + []*SectionHeader{ + {"__text", "__TEXT", 0x100000f14, 0x6d, 0xf14, 0x2, 0x0, 0x0, 0x80000400}, + {"__symbol_stub1", "__TEXT", 0x100000f81, 0xc, 0xf81, 0x0, 0x0, 0x0, 0x80000408}, + {"__stub_helper", "__TEXT", 0x100000f90, 0x18, 0xf90, 0x2, 0x0, 0x0, 0x0}, + {"__cstring", "__TEXT", 0x100000fa8, 0xd, 0xfa8, 0x0, 0x0, 0x0, 0x2}, + {"__eh_frame", "__TEXT", 0x100000fb8, 0x48, 0xfb8, 0x3, 0x0, 0x0, 0x6000000b}, + {"__data", "__DATA", 0x100001000, 0x1c, 0x1000, 0x3, 0x0, 0x0, 0x0}, + {"__dyld", "__DATA", 0x100001020, 0x38, 0x1020, 0x3, 0x0, 0x0, 0x0}, + {"__la_symbol_ptr", "__DATA", 0x100001058, 0x10, 0x1058, 0x2, 0x0, 0x0, 0x7}, + }, + }, + { + "testdata/gcc-amd64-darwin-exec-debug", + FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0xa, 0x4, 0x5a0, 0}, + []*SegmentHeader{ + nil, + {LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x0, 0x7, 0x5, 0x5, 0x0}, + {LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x0, 0x0, 0x7, 0x3, 0x3, 0x0}, + {LoadCmdSegment64, 0x278, "__DWARF", 0x100002000, 0x1000, 0x1000, 0x1bc, 0x7, 0x3, 0x7, 0x0}, + }, + []*SectionHeader{ + {"__text", "__TEXT", 0x100000f14, 0x0, 0x0, 0x2, 0x0, 0x0, 0x80000400}, + {"__symbol_stub1", "__TEXT", 0x100000f81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000408}, + {"__stub_helper", "__TEXT", 0x100000f90, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0}, + {"__cstring", "__TEXT", 0x100000fa8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, + {"__eh_frame", "__TEXT", 0x100000fb8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x6000000b}, + {"__data", "__DATA", 0x100001000, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0}, + {"__dyld", "__DATA", 0x100001020, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0}, + {"__la_symbol_ptr", "__DATA", 0x100001058, 0x0, 0x0, 0x2, 0x0, 0x0, 0x7}, + {"__debug_abbrev", "__DWARF", 0x100002000, 0x36, 0x1000, 0x0, 0x0, 0x0, 0x0}, + {"__debug_aranges", "__DWARF", 0x100002036, 0x30, 0x1036, 0x0, 0x0, 0x0, 0x0}, + {"__debug_frame", "__DWARF", 0x100002066, 0x40, 0x1066, 0x0, 0x0, 0x0, 0x0}, + {"__debug_info", "__DWARF", 0x1000020a6, 0x54, 0x10a6, 0x0, 0x0, 0x0, 0x0}, + {"__debug_line", "__DWARF", 0x1000020fa, 0x47, 0x10fa, 0x0, 0x0, 0x0, 0x0}, + {"__debug_pubnames", "__DWARF", 0x100002141, 0x1b, 0x1141, 0x0, 0x0, 0x0, 0x0}, + {"__debug_str", "__DWARF", 0x10000215c, 0x60, 0x115c, 0x0, 0x0, 0x0, 0x0}, + }, + }, +} + +func TestOpen(t *testing.T) { + for i := range fileTests { + tt := &fileTests[i] + + f, err := Open(tt.file) + if err != nil { + t.Error(err) + continue + } + if !reflect.DeepEqual(f.FileHeader, tt.hdr) { + t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr) + continue + } + for i, l := range f.Loads { + if i >= len(tt.segments) { + break + } + sh := tt.segments[i] + s, ok := l.(*Segment) + if sh == nil { + if ok { + t.Errorf("open %s, section %d: skipping %#v\n", tt.file, i, &s.SegmentHeader) + } + continue + } + if !ok { + t.Errorf("open %s, section %d: not *Segment\n", tt.file, i) + continue + } + have := &s.SegmentHeader + want := sh + if !reflect.DeepEqual(have, want) { + t.Errorf("open %s, segment %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want) + } + } + tn := len(tt.segments) + fn := len(f.Loads) + if tn != fn { + t.Errorf("open %s: len(Loads) = %d, want %d", tt.file, fn, tn) + } + + for i, sh := range f.Sections { + if i >= len(tt.sections) { + break + } + have := &sh.SectionHeader + want := tt.sections[i] + if !reflect.DeepEqual(have, want) { + t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want) + } + } + tn = len(tt.sections) + fn = len(f.Sections) + if tn != fn { + t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn) + } + + } +} + +func TestOpenFailure(t *testing.T) { + filename := "file.go" // not a Mach-O file + _, err := Open(filename) // don't crash + if err == nil { + t.Errorf("open %s: succeeded unexpectedly", filename) + } +} + +func TestOpenFat(t *testing.T) { + ff, err := OpenFat("testdata/fat-gcc-386-amd64-darwin-exec") + if err != nil { + t.Fatal(err) + } + + if ff.Magic != MagicFat { + t.Errorf("OpenFat: got magic number %#x, want %#x", ff.Magic, MagicFat) + } + if len(ff.Arches) != 2 { + t.Errorf("OpenFat: got %d architectures, want 2", len(ff.Arches)) + } + + for i := range ff.Arches { + arch := &ff.Arches[i] + ftArch := &fileTests[i] + + if arch.Cpu != ftArch.hdr.Cpu || arch.SubCpu != ftArch.hdr.SubCpu { + t.Errorf("OpenFat: architecture #%d got cpu=%#x subtype=%#x, expected cpu=%#x, subtype=%#x", i, arch.Cpu, arch.SubCpu, ftArch.hdr.Cpu, ftArch.hdr.SubCpu) + } + + if !reflect.DeepEqual(arch.FileHeader, ftArch.hdr) { + t.Errorf("OpenFat header:\n\tgot %#v\n\twant %#v\n", arch.FileHeader, ftArch.hdr) + } + } +} + +func TestOpenFatFailure(t *testing.T) { + filename := "file.go" // not a Mach-O file + if _, err := OpenFat(filename); err == nil { + t.Errorf("OpenFat %s: succeeded unexpectedly", filename) + } + + filename = "testdata/gcc-386-darwin-exec" // not a fat Mach-O + ff, err := OpenFat(filename) + if err != ErrNotFat { + t.Errorf("OpenFat %s: got %v, want ErrNotFat", filename, err) + } + if ff != nil { + t.Errorf("OpenFat %s: got %v, want nil", filename, ff) + } +} diff --git a/macho/macho.go b/macho/macho.go new file mode 100644 index 0000000..d9678c8 --- /dev/null +++ b/macho/macho.go @@ -0,0 +1,316 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Mach-O header data structures +// http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html + +package macho + +import "strconv" + +// A FileHeader represents a Mach-O file header. +type FileHeader struct { + Magic uint32 + Cpu Cpu + SubCpu uint32 + Type Type + Ncmd uint32 + Cmdsz uint32 + Flags uint32 +} + +const ( + fileHeaderSize32 = 7 * 4 + fileHeaderSize64 = 8 * 4 +) + +const ( + Magic32 uint32 = 0xfeedface + Magic64 uint32 = 0xfeedfacf + MagicFat uint32 = 0xcafebabe +) + +// A Type is the Mach-O file type, e.g. an object file, executable, or dynamic library. +type Type uint32 + +const ( + TypeObj Type = 1 + TypeExec Type = 2 + TypeDylib Type = 6 + TypeBundle Type = 8 +) + +// A Cpu is a Mach-O cpu type. +type Cpu uint32 + +const cpuArch64 = 0x01000000 + +const ( + Cpu386 Cpu = 7 + CpuAmd64 Cpu = Cpu386 | cpuArch64 + CpuArm Cpu = 12 + CpuPpc Cpu = 18 + CpuPpc64 Cpu = CpuPpc | cpuArch64 +) + +var cpuStrings = []intName{ + {uint32(Cpu386), "Cpu386"}, + {uint32(CpuAmd64), "CpuAmd64"}, + {uint32(CpuArm), "CpuArm"}, + {uint32(CpuPpc), "CpuPpc"}, + {uint32(CpuPpc64), "CpuPpc64"}, +} + +func (i Cpu) String() string { return stringName(uint32(i), cpuStrings, false) } +func (i Cpu) GoString() string { return stringName(uint32(i), cpuStrings, true) } + +// A LoadCmd is a Mach-O load command. +type LoadCmd uint32 + +const ( + LoadCmdSegment LoadCmd = 1 + LoadCmdSymtab LoadCmd = 2 + LoadCmdThread LoadCmd = 4 + LoadCmdUnixThread LoadCmd = 5 // thread+stack + LoadCmdDysymtab LoadCmd = 11 + LoadCmdDylib LoadCmd = 12 + LoadCmdDylinker LoadCmd = 15 + LoadCmdSegment64 LoadCmd = 25 +) + +var cmdStrings = []intName{ + {uint32(LoadCmdSegment), "LoadCmdSegment"}, + {uint32(LoadCmdThread), "LoadCmdThread"}, + {uint32(LoadCmdUnixThread), "LoadCmdUnixThread"}, + {uint32(LoadCmdDylib), "LoadCmdDylib"}, + {uint32(LoadCmdSegment64), "LoadCmdSegment64"}, +} + +func (i LoadCmd) String() string { return stringName(uint32(i), cmdStrings, false) } +func (i LoadCmd) GoString() string { return stringName(uint32(i), cmdStrings, true) } + +// A Segment64 is a 64-bit Mach-O segment load command. +type Segment64 struct { + Cmd LoadCmd + Len uint32 + Name [16]byte + Addr uint64 + Memsz uint64 + Offset uint64 + Filesz uint64 + Maxprot uint32 + Prot uint32 + Nsect uint32 + Flag uint32 +} + +// A Segment32 is a 32-bit Mach-O segment load command. +type Segment32 struct { + Cmd LoadCmd + Len uint32 + Name [16]byte + Addr uint32 + Memsz uint32 + Offset uint32 + Filesz uint32 + Maxprot uint32 + Prot uint32 + Nsect uint32 + Flag uint32 +} + +// A DylibCmd is a Mach-O load dynamic library command. +type DylibCmd struct { + Cmd LoadCmd + Len uint32 + Name uint32 + Time uint32 + CurrentVersion uint32 + CompatVersion uint32 +} + +// A Section32 is a 32-bit Mach-O section header. +type Section32 struct { + Name [16]byte + Seg [16]byte + Addr uint32 + Size uint32 + Offset uint32 + Align uint32 + Reloff uint32 + Nreloc uint32 + Flags uint32 + Reserve1 uint32 + Reserve2 uint32 +} + +// A Section32 is a 64-bit Mach-O section header. +type Section64 struct { + Name [16]byte + Seg [16]byte + Addr uint64 + Size uint64 + Offset uint32 + Align uint32 + Reloff uint32 + Nreloc uint32 + Flags uint32 + Reserve1 uint32 + Reserve2 uint32 + Reserve3 uint32 +} + +// A SymtabCmd is a Mach-O symbol table command. +type SymtabCmd struct { + Cmd LoadCmd + Len uint32 + Symoff uint32 + Nsyms uint32 + Stroff uint32 + Strsize uint32 +} + +// A DysymtabCmd is a Mach-O dynamic symbol table command. +type DysymtabCmd struct { + Cmd LoadCmd + Len uint32 + Ilocalsym uint32 + Nlocalsym uint32 + Iextdefsym uint32 + Nextdefsym uint32 + Iundefsym uint32 + Nundefsym uint32 + Tocoffset uint32 + Ntoc uint32 + Modtaboff uint32 + Nmodtab uint32 + Extrefsymoff uint32 + Nextrefsyms uint32 + Indirectsymoff uint32 + Nindirectsyms uint32 + Extreloff uint32 + Nextrel uint32 + Locreloff uint32 + Nlocrel uint32 +} + +// An Nlist32 is a Mach-O 32-bit symbol table entry. +type Nlist32 struct { + Name uint32 + Type uint8 + Sect uint8 + Desc uint16 + Value uint32 +} + +// An Nlist64 is a Mach-O 64-bit symbol table entry. +type Nlist64 struct { + Name uint32 + Type uint8 + Sect uint8 + Desc uint16 + Value uint64 +} + +// A Symbol is a Mach-O 32-bit or 64-bit symbol table entry. +type Symbol struct { + Name string + Type uint8 + Sect uint8 + Desc uint16 + Value uint64 +} + +// A Thread is a Mach-O thread state command. +type Thread struct { + Cmd LoadCmd + Len uint32 + Type uint32 + Data []uint32 +} + +// Regs386 is the Mach-O 386 register structure. +type Regs386 struct { + AX uint32 + BX uint32 + CX uint32 + DX uint32 + DI uint32 + SI uint32 + BP uint32 + SP uint32 + SS uint32 + FLAGS uint32 + IP uint32 + CS uint32 + DS uint32 + ES uint32 + FS uint32 + GS uint32 +} + +// RegsAMD64 is the Mach-O AMD64 register structure. +type RegsAMD64 struct { + AX uint64 + BX uint64 + CX uint64 + DX uint64 + DI uint64 + SI uint64 + BP uint64 + SP uint64 + R8 uint64 + R9 uint64 + R10 uint64 + R11 uint64 + R12 uint64 + R13 uint64 + R14 uint64 + R15 uint64 + IP uint64 + FLAGS uint64 + CS uint64 + FS uint64 + GS uint64 +} + +type intName struct { + i uint32 + s string +} + +func stringName(i uint32, names []intName, goSyntax bool) string { + for _, n := range names { + if n.i == i { + if goSyntax { + return "macho." + n.s + } + return n.s + } + } + return strconv.FormatUint(uint64(i), 10) +} + +func flagName(i uint32, names []intName, goSyntax bool) string { + s := "" + for _, n := range names { + if n.i&i == n.i { + if len(s) > 0 { + s += "+" + } + if goSyntax { + s += "macho." + } + s += n.s + i -= n.i + } + } + if len(s) == 0 { + return "0x" + strconv.FormatUint(uint64(i), 16) + } + if i != 0 { + s += "+0x" + strconv.FormatUint(uint64(i), 16) + } + return s +} diff --git a/macho/testdata/fat-gcc-386-amd64-darwin-exec b/macho/testdata/fat-gcc-386-amd64-darwin-exec new file mode 100644 index 0000000000000000000000000000000000000000..7efd19300b2899328664e0e0314fac011722724b GIT binary patch literal 28992 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 YIe}f$IZ?D?rX`S+#wLgJDVnj z(nx4wDS_bGyP>BXJmg}Vw1?7D5j}X879vs!A|7&Z`uokkm&{Cq9&>pgy#4;X_x*VD zdo#;?^Zm8+@5@dhtPUX-4+$ZTV9dNB#1GJvcQ9-WDaSLPWxvW^`jXM8SW$Ohk2nvp zP|C^d)MRu?ke`aigz~XBf$ao?E2Q+Cd5>0G;&{h;a50#wsCoLlu3brp#a$bQl#PW- zzE+kEubDsl|NY%icwH)qepr$U@30C24?uFpDc2nrvH1R06ke}dPd~E1FnEw(Xn1a^ zI<4Yyyj6vFRL#>5OB`EDXGUIka}}q(Dvr0V@Ob7F`nWfC1^36bT?YowbtpL3;ml+v zG=uYtoU1FPcH$E&efr?2HrxsxEG1zrPcn1)Drry#w+La zty1?~oNIJ{Qwk3%5c*;6c^`-Uz2|ClWbBLV@Q8jU(lOrPQ$7jE6oNR)4C2HL?xl45 z+1-F%M2yezI0l7lIGFpUvZ3~swX_h4eP+55!<682FxYIkgXL0wFhudYh;ttoPOZD$ z*|`U&e}39p+kF2DpWSr~VxGY`Xwm-o^TAR7Z`2S7t9 zk6B7QFJaXXjpj`(G(4v=@M$D5$TmNQ0mFb{z%XDKFbo(53QWx9&Yj*h`y9d+EGrzM!@B0XEtCW9vhx=b4Go<>R-veLpkd z_x*}U&q?9;{Q~1ML6)`&)@yNd(eL0eY!#*dlTyM9pCq1-=)0@8wF*# z)}Q{S=C-SvV_XcezP%)@`IMFF>P)WS6}2BR)<`#zLwTEvC<(s5cBg(klwg^2RM)GV zdZnZ`r2oATNu{}#NWHq=Q97wK>vqoTI`@{UCz)r&Fkl!k3>XFs1BL;^fMLKeU>GnA z7zPXjhJk}&fW55~DA>nUoz6ZU{i5;*;P>ETfv)#IQhrL+`5!A^*ZrTv@1O(WcYv!H z%-7!s*vIa{#H_++pPs(%%QfIz!fLnNU@ydc_AlSU&^j&Hglsq6^QCGLopRlLZ?}Nu z^=7po%eC6q&AO0dQyF)Wab6o6;!qzO$335^7(8-p4F62z?(LIDXH6{xyF2o+jH#neumA8_o*c2K%2 zLV|5?0IP~4u;31C;SelAA`9S+J>xizqvQY{X=Xg{=bPs@ww0G}!=Has2w@SSqeX;J z0{W*U46H4~d>;Cx-0(;8`McDgmmfdeEZs6Mfb2U)h>)EJdppdSO&~@nKQ7}d*b7lS z-+~ZF8#r7$$t#|L;6`fqN(inK=qR`(#6Ik*rcP|9udYq<-bV0({%OWrU_#&lh_p4m zr&%Bi-|uk*Z!Eqb-1V>9j@2<*K7TxapCfodM3_y8)+S9$>1v);IKD3tJUDB=We_^> z7jQw(b=^JJ`?Z}~Km+GF>s%?9vk7S^jxzEmWD0L&;`;_f{w44Z^@hKMa~S7IJid|1 z44$s=N2TXj6TH0$UNHCcIS#IA-Uc9mxOQt#9*&}rLV08F#aB0(2vH8r8J%f^`_$Ba zizupUX>X?Us-F4O_l&E3o~0Nq&7O#_DtYm(GKVUQkNbyb&f||<_qa;|{S z;yl#7!V1b{?t%y^lQO!O6?3;UcQTi=+e_49gZ@i!4Z2tTq+RGMR%M=x+5%)n=>MQ$ zga`R-Dt*kVpA}{w_D@iKF8^Lg(Awzq!ebcOeweCo35M&?xnr=b7ZS-`P>J{jwG?rb z9e##3K`S8oyB*`-zZPNi&H* literal 0 HcmV?d00001 diff --git a/macho/testdata/hello.c b/macho/testdata/hello.c new file mode 100644 index 0000000..a689d36 --- /dev/null +++ b/macho/testdata/hello.c @@ -0,0 +1,8 @@ +#include + +int +main(void) +{ + printf("hello, world\n"); + return 0; +} diff --git a/pe/file.go b/pe/file.go new file mode 100644 index 0000000..ce6f140 --- /dev/null +++ b/pe/file.go @@ -0,0 +1,390 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package pe implements access to PE (Microsoft Windows Portable Executable) files. +package pe + +import ( + "debug/dwarf" + "encoding/binary" + "errors" + "fmt" + "io" + "os" + "strconv" + "unsafe" +) + +// A File represents an open PE file. +type File struct { + FileHeader + OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64 + Sections []*Section + Symbols []*Symbol + + closer io.Closer +} + +type SectionHeader struct { + Name string + VirtualSize uint32 + VirtualAddress uint32 + Size uint32 + Offset uint32 + PointerToRelocations uint32 + PointerToLineNumbers uint32 + NumberOfRelocations uint16 + NumberOfLineNumbers uint16 + Characteristics uint32 +} + +type Section struct { + SectionHeader + + // Embed ReaderAt for ReadAt method. + // Do not embed SectionReader directly + // to avoid having Read and Seek. + // If a client wants Read and Seek it must use + // Open() to avoid fighting over the seek offset + // with other clients. + io.ReaderAt + sr *io.SectionReader +} + +type Symbol struct { + Name string + Value uint32 + SectionNumber int16 + Type uint16 + StorageClass uint8 +} + +type ImportDirectory struct { + OriginalFirstThunk uint32 + TimeDateStamp uint32 + ForwarderChain uint32 + Name uint32 + FirstThunk uint32 + + dll string +} + +// Data reads and returns the contents of the PE section. +func (s *Section) Data() ([]byte, error) { + dat := make([]byte, s.sr.Size()) + n, err := s.sr.ReadAt(dat, 0) + if n == len(dat) { + err = nil + } + return dat[0:n], err +} + +// Open returns a new ReadSeeker reading the PE section. +func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } + +type FormatError struct { + off int64 + msg string + val interface{} +} + +func (e *FormatError) Error() string { + msg := e.msg + if e.val != nil { + msg += fmt.Sprintf(" '%v'", e.val) + } + msg += fmt.Sprintf(" in record at byte %#x", e.off) + return msg +} + +// Open opens the named file using os.Open and prepares it for use as a PE binary. +func Open(name string) (*File, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + ff, err := NewFile(f) + if err != nil { + f.Close() + return nil, err + } + ff.closer = f + return ff, nil +} + +// Close closes the File. +// If the File was created using NewFile directly instead of Open, +// Close has no effect. +func (f *File) Close() error { + var err error + if f.closer != nil { + err = f.closer.Close() + f.closer = nil + } + return err +} + +// NewFile creates a new File for accessing a PE binary in an underlying reader. +func NewFile(r io.ReaderAt) (*File, error) { + f := new(File) + sr := io.NewSectionReader(r, 0, 1<<63-1) + + var dosheader [96]byte + if _, err := r.ReadAt(dosheader[0:], 0); err != nil { + return nil, err + } + var base int64 + if dosheader[0] == 'M' && dosheader[1] == 'Z' { + signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:])) + var sign [4]byte + r.ReadAt(sign[:], signoff) + if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) { + return nil, errors.New("Invalid PE File Format.") + } + base = signoff + 4 + } else { + base = int64(0) + } + sr.Seek(base, os.SEEK_SET) + if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { + return nil, err + } + if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 { + return nil, errors.New("Invalid PE File Format.") + } + + var ss []byte + if f.FileHeader.NumberOfSymbols > 0 { + // Get COFF string table, which is located at the end of the COFF symbol table. + sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET) + var l uint32 + if err := binary.Read(sr, binary.LittleEndian, &l); err != nil { + return nil, err + } + ss = make([]byte, l) + if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil { + return nil, err + } + + // Process COFF symbol table. + sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET) + aux := uint8(0) + for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ { + cs := new(COFFSymbol) + if err := binary.Read(sr, binary.LittleEndian, cs); err != nil { + return nil, err + } + if aux > 0 { + aux-- + continue + } + var name string + if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 { + si := int(binary.LittleEndian.Uint32(cs.Name[4:])) + name, _ = getString(ss, si) + } else { + name = cstring(cs.Name[:]) + } + aux = cs.NumberOfAuxSymbols + s := &Symbol{ + Name: name, + Value: cs.Value, + SectionNumber: cs.SectionNumber, + Type: cs.Type, + StorageClass: cs.StorageClass, + } + f.Symbols = append(f.Symbols, s) + } + } + + // Read optional header. + sr.Seek(base, os.SEEK_SET) + if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { + return nil, err + } + var oh32 OptionalHeader32 + var oh64 OptionalHeader64 + switch uintptr(f.FileHeader.SizeOfOptionalHeader) { + case unsafe.Sizeof(oh32): + if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil { + return nil, err + } + if oh32.Magic != 0x10b { // PE32 + return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic) + } + f.OptionalHeader = &oh32 + case unsafe.Sizeof(oh64): + if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil { + return nil, err + } + if oh64.Magic != 0x20b { // PE32+ + return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic) + } + f.OptionalHeader = &oh64 + } + + // Process sections. + f.Sections = make([]*Section, f.FileHeader.NumberOfSections) + for i := 0; i < int(f.FileHeader.NumberOfSections); i++ { + sh := new(SectionHeader32) + if err := binary.Read(sr, binary.LittleEndian, sh); err != nil { + return nil, err + } + var name string + if sh.Name[0] == '\x2F' { + si, _ := strconv.Atoi(cstring(sh.Name[1:])) + name, _ = getString(ss, si) + } else { + name = cstring(sh.Name[0:]) + } + s := new(Section) + s.SectionHeader = SectionHeader{ + Name: name, + VirtualSize: sh.VirtualSize, + VirtualAddress: sh.VirtualAddress, + Size: sh.SizeOfRawData, + Offset: sh.PointerToRawData, + PointerToRelocations: sh.PointerToRelocations, + PointerToLineNumbers: sh.PointerToLineNumbers, + NumberOfRelocations: sh.NumberOfRelocations, + NumberOfLineNumbers: sh.NumberOfLineNumbers, + Characteristics: sh.Characteristics, + } + s.sr = io.NewSectionReader(r, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size)) + s.ReaderAt = s.sr + f.Sections[i] = s + } + return f, nil +} + +func cstring(b []byte) string { + var i int + for i = 0; i < len(b) && b[i] != 0; i++ { + } + return string(b[0:i]) +} + +// getString extracts a string from symbol string table. +func getString(section []byte, start int) (string, bool) { + if start < 0 || start >= len(section) { + return "", false + } + + for end := start; end < len(section); end++ { + if section[end] == 0 { + return string(section[start:end]), true + } + } + return "", false +} + +// Section returns the first section with the given name, or nil if no such +// section exists. +func (f *File) Section(name string) *Section { + for _, s := range f.Sections { + if s.Name == name { + return s + } + } + return nil +} + +func (f *File) DWARF() (*dwarf.Data, error) { + // There are many other DWARF sections, but these + // are the required ones, and the debug/dwarf package + // does not use the others, so don't bother loading them. + var names = [...]string{"abbrev", "info", "str"} + var dat [len(names)][]byte + for i, name := range names { + name = ".debug_" + name + s := f.Section(name) + if s == nil { + continue + } + b, err := s.Data() + if err != nil && uint32(len(b)) < s.Size { + return nil, err + } + dat[i] = b + } + + abbrev, info, str := dat[0], dat[1], dat[2] + return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str) +} + +// ImportedSymbols returns the names of all symbols +// referred to by the binary f that are expected to be +// satisfied by other libraries at dynamic load time. +// It does not return weak symbols. +func (f *File) ImportedSymbols() ([]string, error) { + pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 + ds := f.Section(".idata") + if ds == nil { + // not dynamic, so no libraries + return nil, nil + } + d, err := ds.Data() + if err != nil { + return nil, err + } + var ida []ImportDirectory + for len(d) > 0 { + var dt ImportDirectory + dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4]) + dt.Name = binary.LittleEndian.Uint32(d[12:16]) + dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20]) + d = d[20:] + if dt.OriginalFirstThunk == 0 { + break + } + ida = append(ida, dt) + } + names, _ := ds.Data() + var all []string + for _, dt := range ida { + dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress)) + d, _ = ds.Data() + // seek to OriginalFirstThunk + d = d[dt.OriginalFirstThunk-ds.VirtualAddress:] + for len(d) > 0 { + if pe64 { // 64bit + va := binary.LittleEndian.Uint64(d[0:8]) + d = d[8:] + if va == 0 { + break + } + if va&0x8000000000000000 > 0 { // is Ordinal + // TODO add dynimport ordinal support. + } else { + fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2)) + all = append(all, fn+":"+dt.dll) + } + } else { // 32bit + va := binary.LittleEndian.Uint32(d[0:4]) + d = d[4:] + if va == 0 { + break + } + if va&0x80000000 > 0 { // is Ordinal + // TODO add dynimport ordinal support. + //ord := va&0x0000FFFF + } else { + fn, _ := getString(names, int(va-ds.VirtualAddress+2)) + all = append(all, fn+":"+dt.dll) + } + } + } + } + + return all, nil +} + +// ImportedLibraries returns the names of all libraries +// referred to by the binary f that are expected to be +// linked with the binary at dynamic link time. +func (f *File) ImportedLibraries() ([]string, error) { + // TODO + // cgo -dynimport don't use this for windows PE, so just return. + return nil, nil +} diff --git a/pe/file_test.go b/pe/file_test.go new file mode 100644 index 0000000..0d73969 --- /dev/null +++ b/pe/file_test.go @@ -0,0 +1,243 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pe + +import ( + "reflect" + "testing" +) + +type fileTest struct { + file string + hdr FileHeader + opthdr interface{} + sections []*SectionHeader + symbols []*Symbol +} + +var fileTests = []fileTest{ + { + "testdata/gcc-386-mingw-obj", + FileHeader{0x014c, 0x000c, 0x0, 0x64a, 0x1e, 0x0, 0x104}, + nil, + []*SectionHeader{ + {".text", 0, 0, 36, 500, 1440, 0, 3, 0, 0x60300020}, + {".data", 0, 0, 0, 0, 0, 0, 0, 0, 3224371264}, + {".bss", 0, 0, 0, 0, 0, 0, 0, 0, 3224371328}, + {".debug_abbrev", 0, 0, 137, 536, 0, 0, 0, 0, 0x42100000}, + {".debug_info", 0, 0, 418, 673, 1470, 0, 7, 0, 1108344832}, + {".debug_line", 0, 0, 128, 1091, 1540, 0, 1, 0, 1108344832}, + {".rdata", 0, 0, 16, 1219, 0, 0, 0, 0, 1076887616}, + {".debug_frame", 0, 0, 52, 1235, 1550, 0, 2, 0, 1110441984}, + {".debug_loc", 0, 0, 56, 1287, 0, 0, 0, 0, 1108344832}, + {".debug_pubnames", 0, 0, 27, 1343, 1570, 0, 1, 0, 1108344832}, + {".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832}, + {".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832}, + }, + []*Symbol{ + {".file", 0x0, -2, 0x0, 0x67}, + {"_main", 0x0, 1, 0x20, 0x2}, + {".text", 0x0, 1, 0x0, 0x3}, + {".data", 0x0, 2, 0x0, 0x3}, + {".bss", 0x0, 3, 0x0, 0x3}, + {".debug_abbrev", 0x0, 4, 0x0, 0x3}, + {".debug_info", 0x0, 5, 0x0, 0x3}, + {".debug_line", 0x0, 6, 0x0, 0x3}, + {".rdata", 0x0, 7, 0x0, 0x3}, + {".debug_frame", 0x0, 8, 0x0, 0x3}, + {".debug_loc", 0x0, 9, 0x0, 0x3}, + {".debug_pubnames", 0x0, 10, 0x0, 0x3}, + {".debug_pubtypes", 0x0, 11, 0x0, 0x3}, + {".debug_aranges", 0x0, 12, 0x0, 0x3}, + {"___main", 0x0, 0, 0x20, 0x2}, + {"_puts", 0x0, 0, 0x20, 0x2}, + }, + }, + { + "testdata/gcc-386-mingw-exec", + FileHeader{0x014c, 0x000f, 0x4c6a1b60, 0x3c00, 0x282, 0xe0, 0x107}, + &OptionalHeader32{ + 0x10b, 0x2, 0x38, 0xe00, 0x1a00, 0x200, 0x1160, 0x1000, 0x2000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x10000, 0x400, 0x14abb, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10, + [16]DataDirectory{ + {0x0, 0x0}, + {0x5000, 0x3c8}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x7000, 0x18}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + }, + }, + []*SectionHeader{ + {".text", 0xcd8, 0x1000, 0xe00, 0x400, 0x0, 0x0, 0x0, 0x0, 0x60500060}, + {".data", 0x10, 0x2000, 0x200, 0x1200, 0x0, 0x0, 0x0, 0x0, 0xc0300040}, + {".rdata", 0x120, 0x3000, 0x200, 0x1400, 0x0, 0x0, 0x0, 0x0, 0x40300040}, + {".bss", 0xdc, 0x4000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0400080}, + {".idata", 0x3c8, 0x5000, 0x400, 0x1600, 0x0, 0x0, 0x0, 0x0, 0xc0300040}, + {".CRT", 0x18, 0x6000, 0x200, 0x1a00, 0x0, 0x0, 0x0, 0x0, 0xc0300040}, + {".tls", 0x20, 0x7000, 0x200, 0x1c00, 0x0, 0x0, 0x0, 0x0, 0xc0300040}, + {".debug_aranges", 0x20, 0x8000, 0x200, 0x1e00, 0x0, 0x0, 0x0, 0x0, 0x42100000}, + {".debug_pubnames", 0x51, 0x9000, 0x200, 0x2000, 0x0, 0x0, 0x0, 0x0, 0x42100000}, + {".debug_pubtypes", 0x91, 0xa000, 0x200, 0x2200, 0x0, 0x0, 0x0, 0x0, 0x42100000}, + {".debug_info", 0xe22, 0xb000, 0x1000, 0x2400, 0x0, 0x0, 0x0, 0x0, 0x42100000}, + {".debug_abbrev", 0x157, 0xc000, 0x200, 0x3400, 0x0, 0x0, 0x0, 0x0, 0x42100000}, + {".debug_line", 0x144, 0xd000, 0x200, 0x3600, 0x0, 0x0, 0x0, 0x0, 0x42100000}, + {".debug_frame", 0x34, 0xe000, 0x200, 0x3800, 0x0, 0x0, 0x0, 0x0, 0x42300000}, + {".debug_loc", 0x38, 0xf000, 0x200, 0x3a00, 0x0, 0x0, 0x0, 0x0, 0x42100000}, + }, + []*Symbol{}, + }, + { + "testdata/gcc-amd64-mingw-obj", + FileHeader{0x8664, 0x6, 0x0, 0x198, 0x12, 0x0, 0x4}, + nil, + []*SectionHeader{ + {".text", 0x0, 0x0, 0x30, 0x104, 0x15c, 0x0, 0x3, 0x0, 0x60500020}, + {".data", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0500040}, + {".bss", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0500080}, + {".rdata", 0x0, 0x0, 0x10, 0x134, 0x0, 0x0, 0x0, 0x0, 0x40500040}, + {".xdata", 0x0, 0x0, 0xc, 0x144, 0x0, 0x0, 0x0, 0x0, 0x40300040}, + {".pdata", 0x0, 0x0, 0xc, 0x150, 0x17a, 0x0, 0x3, 0x0, 0x40300040}, + }, + []*Symbol{ + {".file", 0x0, -2, 0x0, 0x67}, + {"main", 0x0, 1, 0x20, 0x2}, + {".text", 0x0, 1, 0x0, 0x3}, + {".data", 0x0, 2, 0x0, 0x3}, + {".bss", 0x0, 3, 0x0, 0x3}, + {".rdata", 0x0, 4, 0x0, 0x3}, + {".xdata", 0x0, 5, 0x0, 0x3}, + {".pdata", 0x0, 6, 0x0, 0x3}, + {"__main", 0x0, 0, 0x20, 0x2}, + {"puts", 0x0, 0, 0x20, 0x2}, + }, + }, + { + "testdata/gcc-amd64-mingw-exec", + FileHeader{0x8664, 0x11, 0x53e4364f, 0x39600, 0x6fc, 0xf0, 0x27}, + &OptionalHeader64{ + 0x20b, 0x2, 0x16, 0x6a00, 0x2400, 0x1600, 0x14e0, 0x1000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x0, 0x0, 0x5, 0x2, 0x0, 0x45000, 0x600, 0x46f19, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10, + [16]DataDirectory{ + {0x0, 0x0}, + {0xe000, 0x990}, + {0x0, 0x0}, + {0xa000, 0x498}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x10000, 0x28}, + {0x0, 0x0}, + {0x0, 0x0}, + {0xe254, 0x218}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + }}, + []*SectionHeader{ + {".text", 0x6860, 0x1000, 0x6a00, 0x600, 0x0, 0x0, 0x0, 0x0, 0x60500020}, + {".data", 0xe0, 0x8000, 0x200, 0x7000, 0x0, 0x0, 0x0, 0x0, 0xc0500040}, + {".rdata", 0x6b0, 0x9000, 0x800, 0x7200, 0x0, 0x0, 0x0, 0x0, 0x40600040}, + {".pdata", 0x498, 0xa000, 0x600, 0x7a00, 0x0, 0x0, 0x0, 0x0, 0x40300040}, + {".xdata", 0x488, 0xb000, 0x600, 0x8000, 0x0, 0x0, 0x0, 0x0, 0x40300040}, + {".bss", 0x1410, 0xc000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0600080}, + {".idata", 0x990, 0xe000, 0xa00, 0x8600, 0x0, 0x0, 0x0, 0x0, 0xc0300040}, + {".CRT", 0x68, 0xf000, 0x200, 0x9000, 0x0, 0x0, 0x0, 0x0, 0xc0400040}, + {".tls", 0x48, 0x10000, 0x200, 0x9200, 0x0, 0x0, 0x0, 0x0, 0xc0600040}, + {".debug_aranges", 0x600, 0x11000, 0x600, 0x9400, 0x0, 0x0, 0x0, 0x0, 0x42500040}, + {".debug_info", 0x1316e, 0x12000, 0x13200, 0x9a00, 0x0, 0x0, 0x0, 0x0, 0x42100040}, + {".debug_abbrev", 0x2ccb, 0x26000, 0x2e00, 0x1cc00, 0x0, 0x0, 0x0, 0x0, 0x42100040}, + {".debug_line", 0x3c4d, 0x29000, 0x3e00, 0x1fa00, 0x0, 0x0, 0x0, 0x0, 0x42100040}, + {".debug_frame", 0x18b8, 0x2d000, 0x1a00, 0x23800, 0x0, 0x0, 0x0, 0x0, 0x42400040}, + {".debug_str", 0x396, 0x2f000, 0x400, 0x25200, 0x0, 0x0, 0x0, 0x0, 0x42100040}, + {".debug_loc", 0x13240, 0x30000, 0x13400, 0x25600, 0x0, 0x0, 0x0, 0x0, 0x42100040}, + {".debug_ranges", 0xa70, 0x44000, 0xc00, 0x38a00, 0x0, 0x0, 0x0, 0x0, 0x42100040}, + }, + []*Symbol{}, + }, +} + +func isOptHdrEq(a, b interface{}) bool { + switch va := a.(type) { + case *OptionalHeader32: + vb, ok := b.(*OptionalHeader32) + if !ok { + return false + } + return *vb == *va + case *OptionalHeader64: + vb, ok := b.(*OptionalHeader64) + if !ok { + return false + } + return *vb == *va + case nil: + return b == nil + } + return false +} + +func TestOpen(t *testing.T) { + for i := range fileTests { + tt := &fileTests[i] + + f, err := Open(tt.file) + if err != nil { + t.Error(err) + continue + } + if !reflect.DeepEqual(f.FileHeader, tt.hdr) { + t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr) + continue + } + if !isOptHdrEq(tt.opthdr, f.OptionalHeader) { + t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.OptionalHeader, tt.opthdr) + continue + } + + for i, sh := range f.Sections { + if i >= len(tt.sections) { + break + } + have := &sh.SectionHeader + want := tt.sections[i] + if !reflect.DeepEqual(have, want) { + t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want) + } + } + tn := len(tt.sections) + fn := len(f.Sections) + if tn != fn { + t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn) + } + for i, have := range f.Symbols { + if i >= len(tt.symbols) { + break + } + want := tt.symbols[i] + if !reflect.DeepEqual(have, want) { + t.Errorf("open %s, symbol %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want) + } + } + } +} + +func TestOpenFailure(t *testing.T) { + filename := "file.go" // not a PE file + _, err := Open(filename) // don't crash + if err == nil { + t.Errorf("open %s: succeeded unexpectedly", filename) + } +} diff --git a/pe/pe.go b/pe/pe.go new file mode 100644 index 0000000..8e90b1b --- /dev/null +++ b/pe/pe.go @@ -0,0 +1,134 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pe + +type FileHeader struct { + Machine uint16 + NumberOfSections uint16 + TimeDateStamp uint32 + PointerToSymbolTable uint32 + NumberOfSymbols uint32 + SizeOfOptionalHeader uint16 + Characteristics uint16 +} + +type DataDirectory struct { + VirtualAddress uint32 + Size uint32 +} + +type OptionalHeader32 struct { + Magic uint16 + MajorLinkerVersion uint8 + MinorLinkerVersion uint8 + SizeOfCode uint32 + SizeOfInitializedData uint32 + SizeOfUninitializedData uint32 + AddressOfEntryPoint uint32 + BaseOfCode uint32 + BaseOfData uint32 + ImageBase uint32 + SectionAlignment uint32 + FileAlignment uint32 + MajorOperatingSystemVersion uint16 + MinorOperatingSystemVersion uint16 + MajorImageVersion uint16 + MinorImageVersion uint16 + MajorSubsystemVersion uint16 + MinorSubsystemVersion uint16 + Win32VersionValue uint32 + SizeOfImage uint32 + SizeOfHeaders uint32 + CheckSum uint32 + Subsystem uint16 + DllCharacteristics uint16 + SizeOfStackReserve uint32 + SizeOfStackCommit uint32 + SizeOfHeapReserve uint32 + SizeOfHeapCommit uint32 + LoaderFlags uint32 + NumberOfRvaAndSizes uint32 + DataDirectory [16]DataDirectory +} + +type OptionalHeader64 struct { + Magic uint16 + MajorLinkerVersion uint8 + MinorLinkerVersion uint8 + SizeOfCode uint32 + SizeOfInitializedData uint32 + SizeOfUninitializedData uint32 + AddressOfEntryPoint uint32 + BaseOfCode uint32 + ImageBase uint64 + SectionAlignment uint32 + FileAlignment uint32 + MajorOperatingSystemVersion uint16 + MinorOperatingSystemVersion uint16 + MajorImageVersion uint16 + MinorImageVersion uint16 + MajorSubsystemVersion uint16 + MinorSubsystemVersion uint16 + Win32VersionValue uint32 + SizeOfImage uint32 + SizeOfHeaders uint32 + CheckSum uint32 + Subsystem uint16 + DllCharacteristics uint16 + SizeOfStackReserve uint64 + SizeOfStackCommit uint64 + SizeOfHeapReserve uint64 + SizeOfHeapCommit uint64 + LoaderFlags uint32 + NumberOfRvaAndSizes uint32 + DataDirectory [16]DataDirectory +} + +type SectionHeader32 struct { + Name [8]uint8 + VirtualSize uint32 + VirtualAddress uint32 + SizeOfRawData uint32 + PointerToRawData uint32 + PointerToRelocations uint32 + PointerToLineNumbers uint32 + NumberOfRelocations uint16 + NumberOfLineNumbers uint16 + Characteristics uint32 +} + +const COFFSymbolSize = 18 + +type COFFSymbol struct { + Name [8]uint8 + Value uint32 + SectionNumber int16 + Type uint16 + StorageClass uint8 + NumberOfAuxSymbols uint8 +} + +const ( + IMAGE_FILE_MACHINE_UNKNOWN = 0x0 + IMAGE_FILE_MACHINE_AM33 = 0x1d3 + IMAGE_FILE_MACHINE_AMD64 = 0x8664 + IMAGE_FILE_MACHINE_ARM = 0x1c0 + IMAGE_FILE_MACHINE_EBC = 0xebc + IMAGE_FILE_MACHINE_I386 = 0x14c + IMAGE_FILE_MACHINE_IA64 = 0x200 + IMAGE_FILE_MACHINE_M32R = 0x9041 + IMAGE_FILE_MACHINE_MIPS16 = 0x266 + IMAGE_FILE_MACHINE_MIPSFPU = 0x366 + IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466 + IMAGE_FILE_MACHINE_POWERPC = 0x1f0 + IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1 + IMAGE_FILE_MACHINE_R4000 = 0x166 + IMAGE_FILE_MACHINE_SH3 = 0x1a2 + IMAGE_FILE_MACHINE_SH3DSP = 0x1a3 + IMAGE_FILE_MACHINE_SH4 = 0x1a6 + IMAGE_FILE_MACHINE_SH5 = 0x1a8 + IMAGE_FILE_MACHINE_THUMB = 0x1c2 + IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169 +) diff --git a/pe/testdata/gcc-386-mingw-exec b/pe/testdata/gcc-386-mingw-exec new file mode 100644 index 0000000000000000000000000000000000000000..4b808d043200a8cc10fb5038fe51b70ad6630765 GIT binary patch literal 29941 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�itgmm?lVs%pdaM;AL;DPAarS~VMVcJb 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

Js3LxhqvBHD_y@_@%5}RSk@riaW5}Rr1`G7eW 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�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�`-uI)TM&%!z}O%aVQN1HgSFHVof?q=1*p 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 HX`rfq)vQIg literal 0 HcmV?d00001 diff --git a/pe/testdata/hello.c b/pe/testdata/hello.c new file mode 100644 index 0000000..a689d36 --- /dev/null +++ b/pe/testdata/hello.c @@ -0,0 +1,8 @@ +#include + +int +main(void) +{ + printf("hello, world\n"); + return 0; +} diff --git a/plan9obj/file.go b/plan9obj/file.go new file mode 100644 index 0000000..b11ed86 --- /dev/null +++ b/plan9obj/file.go @@ -0,0 +1,328 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package plan9obj implements access to Plan 9 a.out object files. +package plan9obj + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "os" +) + +// A FileHeader represents a Plan 9 a.out file header. +type FileHeader struct { + Magic uint32 + Bss uint32 + Entry uint64 + PtrSize int + LoadAddress uint64 + HdrSize uint64 +} + +// A File represents an open Plan 9 a.out file. +type File struct { + FileHeader + Sections []*Section + closer io.Closer +} + +// A SectionHeader represents a single Plan 9 a.out section header. +// This structure doesn't exist on-disk, but eases navigation +// through the object file. +type SectionHeader struct { + Name string + Size uint32 + Offset uint32 +} + +// A Section represents a single section in a Plan 9 a.out file. +type Section struct { + SectionHeader + + // Embed ReaderAt for ReadAt method. + // Do not embed SectionReader directly + // to avoid having Read and Seek. + // If a client wants Read and Seek it must use + // Open() to avoid fighting over the seek offset + // with other clients. + io.ReaderAt + sr *io.SectionReader +} + +// Data reads and returns the contents of the Plan 9 a.out section. +func (s *Section) Data() ([]byte, error) { + dat := make([]byte, s.sr.Size()) + n, err := s.sr.ReadAt(dat, 0) + if n == len(dat) { + err = nil + } + return dat[0:n], err +} + +// Open returns a new ReadSeeker reading the Plan 9 a.out section. +func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } + +// A Symbol represents an entry in a Plan 9 a.out symbol table section. +type Sym struct { + Value uint64 + Type rune + Name string +} + +/* + * Plan 9 a.out reader + */ + +// formatError is returned by some operations if the data does +// not have the correct format for an object file. +type formatError struct { + off int + msg string + val interface{} +} + +func (e *formatError) Error() string { + msg := e.msg + if e.val != nil { + msg += fmt.Sprintf(" '%v'", e.val) + } + msg += fmt.Sprintf(" in record at byte %#x", e.off) + return msg +} + +// Open opens the named file using os.Open and prepares it for use as a Plan 9 a.out binary. +func Open(name string) (*File, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + ff, err := NewFile(f) + if err != nil { + f.Close() + return nil, err + } + ff.closer = f + return ff, nil +} + +// Close closes the File. +// If the File was created using NewFile directly instead of Open, +// Close has no effect. +func (f *File) Close() error { + var err error + if f.closer != nil { + err = f.closer.Close() + f.closer = nil + } + return err +} + +func parseMagic(magic []byte) (uint32, error) { + m := binary.BigEndian.Uint32(magic) + switch m { + case Magic386, MagicAMD64, MagicARM: + return m, nil + } + return 0, &formatError{0, "bad magic number", magic} +} + +// NewFile creates a new File for accessing a Plan 9 binary in an underlying reader. +// The Plan 9 binary is expected to start at position 0 in the ReaderAt. +func NewFile(r io.ReaderAt) (*File, error) { + sr := io.NewSectionReader(r, 0, 1<<63-1) + // Read and decode Plan 9 magic + var magic [4]byte + if _, err := r.ReadAt(magic[:], 0); err != nil { + return nil, err + } + _, err := parseMagic(magic[:]) + if err != nil { + return nil, err + } + + ph := new(prog) + if err := binary.Read(sr, binary.BigEndian, ph); err != nil { + return nil, err + } + + f := &File{FileHeader: FileHeader{ + Magic: ph.Magic, + Bss: ph.Bss, + Entry: uint64(ph.Entry), + PtrSize: 4, + LoadAddress: 0x1000, + HdrSize: 4 * 8, + }} + + if ph.Magic&Magic64 != 0 { + if err := binary.Read(sr, binary.BigEndian, &f.Entry); err != nil { + return nil, err + } + f.PtrSize = 8 + f.LoadAddress = 0x200000 + f.HdrSize += 8 + } + + var sects = []struct { + name string + size uint32 + }{ + {"text", ph.Text}, + {"data", ph.Data}, + {"syms", ph.Syms}, + {"spsz", ph.Spsz}, + {"pcsz", ph.Pcsz}, + } + + f.Sections = make([]*Section, 5) + + off := uint32(f.HdrSize) + + for i, sect := range sects { + s := new(Section) + s.SectionHeader = SectionHeader{ + Name: sect.name, + Size: sect.size, + Offset: off, + } + off += sect.size + s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Size)) + s.ReaderAt = s.sr + f.Sections[i] = s + } + + return f, nil +} + +func walksymtab(data []byte, ptrsz int, fn func(sym) error) error { + var order binary.ByteOrder = binary.BigEndian + var s sym + p := data + for len(p) >= 4 { + // Symbol type, value. + if len(p) < ptrsz { + return &formatError{len(data), "unexpected EOF", nil} + } + // fixed-width value + if ptrsz == 8 { + s.value = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.value = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + + var typ byte + typ = p[0] & 0x7F + s.typ = typ + p = p[1:] + + // Name. + var i int + var nnul int + for i = 0; i < len(p); i++ { + if p[i] == 0 { + nnul = 1 + break + } + } + switch typ { + case 'z', 'Z': + p = p[i+nnul:] + for i = 0; i+2 <= len(p); i += 2 { + if p[i] == 0 && p[i+1] == 0 { + nnul = 2 + break + } + } + } + if len(p) < i+nnul { + return &formatError{len(data), "unexpected EOF", nil} + } + s.name = p[0:i] + i += nnul + p = p[i:] + + fn(s) + } + return nil +} + +// NewTable decodes the Go symbol table in data, +// returning an in-memory representation. +func newTable(symtab []byte, ptrsz int) ([]Sym, error) { + var n int + err := walksymtab(symtab, ptrsz, func(s sym) error { + n++ + return nil + }) + if err != nil { + return nil, err + } + + fname := make(map[uint16]string) + syms := make([]Sym, 0, n) + err = walksymtab(symtab, ptrsz, func(s sym) error { + n := len(syms) + syms = syms[0 : n+1] + ts := &syms[n] + ts.Type = rune(s.typ) + ts.Value = s.value + switch s.typ { + default: + ts.Name = string(s.name[:]) + case 'z', 'Z': + for i := 0; i < len(s.name); i += 2 { + eltIdx := binary.BigEndian.Uint16(s.name[i : i+2]) + elt, ok := fname[eltIdx] + if !ok { + return &formatError{-1, "bad filename code", eltIdx} + } + if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' { + ts.Name += "/" + } + ts.Name += elt + } + } + switch s.typ { + case 'f': + fname[uint16(s.value)] = ts.Name + } + return nil + }) + if err != nil { + return nil, err + } + + return syms, nil +} + +// Symbols returns the symbol table for f. +func (f *File) Symbols() ([]Sym, error) { + symtabSection := f.Section("syms") + if symtabSection == nil { + return nil, errors.New("no symbol section") + } + + symtab, err := symtabSection.Data() + if err != nil { + return nil, errors.New("cannot load symbol section") + } + + return newTable(symtab, f.PtrSize) +} + +// Section returns a section with the given name, or nil if no such +// section exists. +func (f *File) Section(name string) *Section { + for _, s := range f.Sections { + if s.Name == name { + return s + } + } + return nil +} diff --git a/plan9obj/file_test.go b/plan9obj/file_test.go new file mode 100644 index 0000000..cfd7a61 --- /dev/null +++ b/plan9obj/file_test.go @@ -0,0 +1,81 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package plan9obj + +import ( + "reflect" + "testing" +) + +type fileTest struct { + file string + hdr FileHeader + sections []*SectionHeader +} + +var fileTests = []fileTest{ + { + "testdata/386-plan9-exec", + FileHeader{Magic386, 0x324, 0x14, 4, 0x1000, 32}, + []*SectionHeader{ + {"text", 0x4c5f, 0x20}, + {"data", 0x94c, 0x4c7f}, + {"syms", 0x2c2b, 0x55cb}, + {"spsz", 0x0, 0x81f6}, + {"pcsz", 0xf7a, 0x81f6}, + }, + }, + { + "testdata/amd64-plan9-exec", + FileHeader{MagicAMD64, 0x618, 0x13, 8, 0x200000, 40}, + []*SectionHeader{ + {"text", 0x4213, 0x28}, + {"data", 0xa80, 0x423b}, + {"syms", 0x2c8c, 0x4cbb}, + {"spsz", 0x0, 0x7947}, + {"pcsz", 0xca0, 0x7947}, + }, + }, +} + +func TestOpen(t *testing.T) { + for i := range fileTests { + tt := &fileTests[i] + + f, err := Open(tt.file) + if err != nil { + t.Error(err) + continue + } + if !reflect.DeepEqual(f.FileHeader, tt.hdr) { + t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr) + continue + } + + for i, sh := range f.Sections { + if i >= len(tt.sections) { + break + } + have := &sh.SectionHeader + want := tt.sections[i] + if !reflect.DeepEqual(have, want) { + t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want) + } + } + tn := len(tt.sections) + fn := len(f.Sections) + if tn != fn { + t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn) + } + } +} + +func TestOpenFailure(t *testing.T) { + filename := "file.go" // not a Plan 9 a.out file + _, err := Open(filename) // don't crash + if err == nil { + t.Errorf("open %s: succeeded unexpectedly", filename) + } +} diff --git a/plan9obj/plan9obj.go b/plan9obj/plan9obj.go new file mode 100644 index 0000000..af98585 --- /dev/null +++ b/plan9obj/plan9obj.go @@ -0,0 +1,36 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * Plan 9 a.out constants and data structures + */ + +package plan9obj + +// Plan 9 Program header. +type prog struct { + Magic uint32 /* magic number */ + Text uint32 /* size of text segment */ + Data uint32 /* size of initialized data */ + Bss uint32 /* size of uninitialized data */ + Syms uint32 /* size of symbol table */ + Entry uint32 /* entry point */ + Spsz uint32 /* size of pc/sp offset table */ + Pcsz uint32 /* size of pc/line number table */ +} + +// Plan 9 symbol table entries. +type sym struct { + value uint64 + typ byte + name []byte +} + +const ( + Magic64 = 0x8000 // 64-bit expanded header + + Magic386 = (4*11+0)*11 + 7 + MagicAMD64 = (4*26+0)*26 + 7 + Magic64 + MagicARM = (4*20+0)*20 + 7 +) diff --git a/plan9obj/testdata/386-plan9-exec b/plan9obj/testdata/386-plan9-exec new file mode 100755 index 0000000000000000000000000000000000000000..748e83f8e6acc310edc5081a27d989c63f0eb2de GIT binary patch literal 37232 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

Y)F+f*6M$||H$)p@hh?u|@d*UGPA5sgqKdoGS> zNk$;W59OausOQapJnz!UO>6<;zc&%I_cHlktfl8pMNnIa1%?V4Oako=%VaNQhyE2^ zQ2Yp%x*xj$i>r~E6yLQv_(eOS6l`+?ze}P;3LZZupKksAz#*dc6SA17Ih^g3zI2g= zt_hyaR!i%7vfxi1LRH9SwBj{dMMO)BdBGa^6AnKO?Kn7;R1PDh^~sb!Gg7~j#00Fu z#lx`);Rlnn3X>Sg^Bw8hZpW0Pt^VtHL}p@8H)4syPp6Ik>mGu3yF>6_kC6PA2J~0f z=P*Q59veRws*!UJ+|VCCv%i`r$QFgIFszifhHyVuM(p>56C&T~dv- z8nySOx^!#OoQ9gPdSE}0>Iz$%zD9JVJIiXqWO1WTChB_?(J69LVH?Huq9Se--MY2Z z^pRMvll3(^r{1Y^ib}XsbcteEjR+wKAsq6FvJP}4RuUg(&wA9l#0{crL2Z~5v^VHB zgx`I^S{?CmrLRkM&RREMt=O3AoKb^1RgZt7TR+7a<_>pGaz{F6t%eNuimA?wRAqRL ze(lIwsZRVe3{aou3U`IS0A(K*AJ^5Vy5|tvdU16`9VDv3KUJ)Q+O=ZM+|?1aIx=dA8?KVJZX=@~s z_a#t!BF(9LEwg@Ft*BgFD>VY*nJ{{83|k$hh|fY+r?_6XA@!xy8mSpAs<<}L0_{Xs z#JxE+(mlF*ol|SAmB_Xm`!pCxDkr?O4mMKZeYIj;RQ;eAv+4|u;?v^&VqG}=7IuDc zu!5(eyGQqs&P{!0csR^W&|;&$R&)XLkxaMf(mT`eDLQjpI_Kmyx>}u*?n#i??Yo?xMA$aFl=(1tmdYB$#3+^>^iZ&uQIc4ppw=& zwa%nu)`cm{&@F&oZ&l{3AFRx7M7eR0azo<)Wd@b+?yuzDZB(YPa-@=dcZ8COvIGh` z1w%y0BNu>si>v#%Cc*6t&TM>!J4pq}nWjJ<`Qk@(u7$Nx&N=d6=Z*3p=M35GoG%Y? zrmdM$+s~QFyjbs?%$^1^ESZvi;eZ&WU4Nv3l@|KJ$FuI0v>M$jG?MpY_=@O5jG`L+ z^-7H98h8NkoT5JD%_1Yp&|pFfz@1`0UJ+FJhnny@xUMYTgJwWs5EFB!m24%8kWf@J z5HfCAFVk0zZmwJuz_ za&_1vAcI;6OP{iSH3U(`zv*5BhRQHhan0NfFz$;+WfD9af8_c(iVm4|I%oe{ePh^b z8MR<2hXwE5#a=35&QYC{u|n ztblu+SVw-G_TozB&#CJxN#Q?>>R4xWErusbS1V<_v^oxce*?@~e$#rp@ zNG0vDd0P2GC0~QQSjl)syj!opf+X8&?Wz9CJ3Iy5Ljj1xhrZWHnC>D}OoHqgq40m< z8^RyKzvm)C{xR)p;n5Tj$vgFOHi}^niq44D{m3`axjGp!9i|W)FTfins0T4YDdHNb z0YjiRy{@lw>SO(sd9I6U!(cX;xPG9@h@rFf4`jRg*JnKo*LWxu-I#>|)3+w9PTU~X zVt%Ayt%&IjMprgX(U@$ro#t8$RTml%ks!bZ@!m9-e%-9qV281WP+pG!h&IS91J0>; zO;s}M2iHgnV4(vn!(iaPwHdYei`Nt?X=Y!Yp@w5plh#B~SgwWFxb^k0A)?fT*L6<= z!pSB>43SBw4|A@Nr`Ai)z@KU+Jb4>z-Uq&k2#Nqvtc=8Gi85U0oJKf~#GwT!WE3Te+Fi6(Lpm(;88}LAh?-Z3^O2_v$raD*T*G0FQ${O12US z^G$J?R);m9_KbR|HiAv$us|Imzya6!V!Gf@5_Nh*1lbPOqp=CJ zMsmE&S1x;;f+Ggc6FQY@O6x(Pdp)u`m3Q)5K!IbL%Rn62KtoL}a`82K>NIkzwXjPgoQxs^4>W6- zLqKNo;_^G*)Py0beLli{M(D>e7=R)uSWuJ4cCH%@z-<(SZa7YzrTq zG@@LYN<`g!XNJ>&$d>Cg)d7kEHBvmCGsB(bynu}RIBIIr>)}`T4y<8;SrP9+-LoP- zT$muK2WxbqE0Wxt1}OSW36n8hsoHCf^D<;ROaj7u0T7IlQ+yoL%f0BYd+<)7m{PG{ zPS?VeGTs~DQ7>Z>rriFy+c3cbuND{-jNr(uL?IaU2-f7K#B&|{brsb}s_uEQF4YYR z_ad+~iZ0zffW<$lHk^VvbzBa+_n#99)3Mf5R7`Ni{450*7}MJ4qL zMGsNWIKD;bM3w=Iq7w`$KSj>ck47MjFr^lB0e%~S*6Sp+f`L3BG?>hH=`cbuVD$Rc S=qDPNM3&i=eclVzoc|w+l~vRL literal 0 HcmV?d00001 diff --git a/plan9obj/testdata/amd64-plan9-exec b/plan9obj/testdata/amd64-plan9-exec new file mode 100755 index 0000000000000000000000000000000000000000..3e257dd8ffc00ca4a19d9552a10d782134387e8b GIT binary patch literal 34279 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 literal 0 HcmV?d00001 diff --git a/plan9obj/testdata/hello.c b/plan9obj/testdata/hello.c new file mode 100644 index 0000000..c0d633e --- /dev/null +++ b/plan9obj/testdata/hello.c @@ -0,0 +1,8 @@ +#include +#include + +void +main(void) +{ + print("hello, world\n"); +}