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.
This commit is contained in:
Russ Cox
2014-09-08 00:08:51 -04:00
commit b8cc805bc6
57 changed files with 10854 additions and 0 deletions
+181
View File
@@ -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
}
+454
View File
@@ -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
)
+401
View File
@@ -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
}
+87
View File
@@ -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)
}
+85
View File
@@ -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 <complex.h>
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;
}
Vendored Executable
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+685
View File
@@ -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
}
+122
View File
@@ -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)
}
}
}
+166
View File
@@ -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
}
+90
View File
@@ -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
}
+1773
View File
File diff suppressed because it is too large Load Diff
+49
View File
@@ -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)
}
}
}
+945
View File
@@ -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
}
+345
View File
@@ -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)
}
}
}
}
+834
View File
@@ -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": {},
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
#include <stdio.h>
void
main(int argc, char *argv[])
{
printf("hello, world\n");
}
+666
View File
@@ -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("!<arch>\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
}
+28
View File
@@ -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)
}
}
}
+58
View File
@@ -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
+9
View File
@@ -0,0 +1,9 @@
// +build ignore
// Empty include file to generate z symbols
// EOF
+453
View File
@@ -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
// <pc update>* <line update>
// 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
}
}
+274
View File
@@ -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
}
}
+710
View File
@@ -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 "<malformed symbol table>", 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
}
+146
View File
@@ -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
}
+524
View File
@@ -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
}
+210
View File
@@ -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)
}
}
+316
View File
@@ -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
}
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
#include <stdio.h>
int
main(void)
{
printf("hello, world\n");
return 0;
}
+390
View File
@@ -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
}
+243
View File
@@ -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)
}
}
+134
View File
@@ -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
)
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
#include <stdio.h>
int
main(void)
{
printf("hello, world\n");
return 0;
}
+328
View File
@@ -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
}
+81
View File
@@ -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)
}
}
+36
View File
@@ -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
)
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
#include <u.h>
#include <libc.h>
void
main(void)
{
print("hello, world\n");
}