mirror of
https://github.com/Binject/debug
synced 2026-06-08 10:28:33 +00:00
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:
+181
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
Vendored
+85
@@ -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;
|
||||
}
|
||||
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
+685
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
void
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
printf("hello, world\n");
|
||||
}
|
||||
+666
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,9 @@
|
||||
// +build ignore
|
||||
|
||||
// Empty include file to generate z symbols
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// EOF
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
printf("hello, world\n");
|
||||
return 0;
|
||||
}
|
||||
+390
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
printf("hello, world\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
Binary file not shown.
BIN
Binary file not shown.
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
|
||||
void
|
||||
main(void)
|
||||
{
|
||||
print("hello, world\n");
|
||||
}
|
||||
Reference in New Issue
Block a user