├── LICENSE ├── README.md ├── dwarf ├── attr_string.go ├── buf.go ├── class_string.go ├── const.go ├── entry.go ├── entry_test.go ├── export_test.go ├── line.go ├── line_test.go ├── open.go ├── tag_string.go ├── testdata │ ├── cycle.c │ ├── cycle.elf │ ├── line-clang.elf │ ├── line-gcc-win.bin │ ├── line-gcc.elf │ ├── line1.c │ ├── line1.h │ ├── line2.c │ ├── ranges.c │ ├── ranges.elf │ ├── split.c │ ├── split.elf │ ├── typedef.c │ ├── typedef.elf │ ├── typedef.elf4 │ └── typedef.macho ├── type.go ├── type_test.go ├── typeunit.go └── unit.go ├── elf ├── elf.go ├── elf_test.go ├── exports.go ├── file.go ├── file_test.go ├── reader.go ├── relocations.go ├── symbols_test.go ├── testdata │ ├── compressed-32.obj │ ├── compressed-64.obj │ ├── gcc-386-freebsd-exec │ ├── gcc-amd64-linux-exec │ ├── gcc-amd64-openbsd-debug-with-rela.obj │ ├── go-relocation-test-clang-arm.obj │ ├── go-relocation-test-clang-x86.obj │ ├── go-relocation-test-gcc424-x86-64.obj │ ├── go-relocation-test-gcc441-x86-64.obj │ ├── go-relocation-test-gcc441-x86.obj │ ├── go-relocation-test-gcc482-aarch64.obj │ ├── go-relocation-test-gcc482-ppc64le.obj │ ├── go-relocation-test-gcc492-arm.obj │ ├── go-relocation-test-gcc492-mips64.obj │ ├── go-relocation-test-gcc492-mipsle.obj │ ├── go-relocation-test-gcc493-mips64le.obj │ ├── go-relocation-test-gcc5-ppc.obj │ ├── go-relocation-test-gcc531-s390x.obj │ ├── go-relocation-test-gcc540-mips.obj │ ├── go-relocation-test-gcc620-sparc64.obj │ ├── go-relocation-test-gcc720-riscv64.obj │ ├── hello-world-core.gz │ ├── hello.c │ └── zdebug-test-gcc484-x86-64.obj └── write.go ├── go.mod ├── goobj2 ├── file.go ├── file_test.go ├── internal │ ├── bio │ │ ├── buf.go │ │ ├── buf_mmap.go │ │ ├── buf_nommap.go │ │ └── must.go │ ├── goobj2 │ │ ├── extras.go │ │ └── goobj2.go │ ├── objabi │ │ └── objabi.go │ └── unsafeheader │ │ └── unsafeheader.go ├── testdata │ ├── aes_encrypt.go │ ├── format.go │ ├── hello_world.go │ ├── http_not_found_handler.go │ ├── loop_de_loop.go │ ├── reflect.go │ └── trim_string.go └── write.go ├── gosym ├── pclntab.go ├── pclntab_test.go ├── symtab.go ├── symtab_test.go └── testdata │ ├── main.go │ ├── pclinetest.h │ └── pclinetest.s ├── macho ├── README.md ├── exports.go ├── fat.go ├── file.go ├── file_test.go ├── macho.go ├── reloctype.go ├── reloctype_string.go ├── testdata │ ├── clang-386-darwin-exec-with-rpath │ ├── clang-386-darwin.obj │ ├── clang-amd64-darwin-exec-with-rpath │ ├── clang-amd64-darwin.obj │ ├── fat-gcc-386-amd64-darwin-exec │ ├── gcc-386-darwin-exec │ ├── gcc-amd64-darwin-exec │ ├── gcc-amd64-darwin-exec-debug │ └── hello.c └── write.go ├── pe ├── cert.go ├── exports.go ├── file.go ├── file_cgo_test.go ├── file_test.go ├── imports.go ├── net.go ├── pe.go ├── reloc.go ├── section.go ├── string.go ├── symbol.go ├── testdata │ ├── gcc-386-mingw-exec │ ├── gcc-386-mingw-no-symbols-exec │ ├── gcc-386-mingw-obj │ ├── gcc-amd64-mingw-exec │ ├── gcc-amd64-mingw-obj │ └── hello.c └── write.go └── plan9obj ├── file.go ├── file_test.go ├── plan9obj.go └── testdata ├── 386-plan9-exec ├── amd64-plan9-exec └── hello.c /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Debug 2 | We have forked the debug/ folder from the standard library, to take direct control of the debug/elf, debug/macho, and debug/pe binary format parsers. To these parsers, we have added the ability to also generate executable files from the parsed intermediate data structures. This lets us load a file with debug parsers, make changes by interacting with the parser structures, and then write those changes back out to a new file. 3 | 4 | 5 | ## Read more about the project here: 6 | https://www.symbolcrash.com/2019/02/23/introducing-symbol-crash/ 7 | -------------------------------------------------------------------------------- /dwarf/attr_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type Attr -trimprefix=Attr"; DO NOT EDIT. 2 | 3 | package dwarf 4 | 5 | import "strconv" 6 | 7 | const _Attr_name = "SiblingLocationNameOrderingByteSizeBitOffsetBitSizeStmtListLowpcHighpcLanguageDiscrDiscrValueVisibilityImportStringLengthCommonRefCompDirConstValueContainingTypeDefaultValueInlineIsOptionalLowerBoundProducerPrototypedReturnAddrStartScopeStrideSizeUpperBoundAbstractOriginAccessibilityAddrClassArtificialBaseTypesCallingCountDataMemberLocDeclColumnDeclFileDeclLineDeclarationDiscrListEncodingExternalFrameBaseFriendIdentifierCaseMacroInfoNamelistItemPrioritySegmentSpecificationStaticLinkTypeUseLocationVarParamVirtualityVtableElemLocAllocatedAssociatedDataLocationStrideEntrypcUseUTF8ExtensionRangesTrampolineCallColumnCallFileCallLineDescription" 8 | 9 | var _Attr_map = map[Attr]string{ 10 | 1: _Attr_name[0:7], 11 | 2: _Attr_name[7:15], 12 | 3: _Attr_name[15:19], 13 | 9: _Attr_name[19:27], 14 | 11: _Attr_name[27:35], 15 | 12: _Attr_name[35:44], 16 | 13: _Attr_name[44:51], 17 | 16: _Attr_name[51:59], 18 | 17: _Attr_name[59:64], 19 | 18: _Attr_name[64:70], 20 | 19: _Attr_name[70:78], 21 | 21: _Attr_name[78:83], 22 | 22: _Attr_name[83:93], 23 | 23: _Attr_name[93:103], 24 | 24: _Attr_name[103:109], 25 | 25: _Attr_name[109:121], 26 | 26: _Attr_name[121:130], 27 | 27: _Attr_name[130:137], 28 | 28: _Attr_name[137:147], 29 | 29: _Attr_name[147:161], 30 | 30: _Attr_name[161:173], 31 | 32: _Attr_name[173:179], 32 | 33: _Attr_name[179:189], 33 | 34: _Attr_name[189:199], 34 | 37: _Attr_name[199:207], 35 | 39: _Attr_name[207:217], 36 | 42: _Attr_name[217:227], 37 | 44: _Attr_name[227:237], 38 | 46: _Attr_name[237:247], 39 | 47: _Attr_name[247:257], 40 | 49: _Attr_name[257:271], 41 | 50: _Attr_name[271:284], 42 | 51: _Attr_name[284:293], 43 | 52: _Attr_name[293:303], 44 | 53: _Attr_name[303:312], 45 | 54: _Attr_name[312:319], 46 | 55: _Attr_name[319:324], 47 | 56: _Attr_name[324:337], 48 | 57: _Attr_name[337:347], 49 | 58: _Attr_name[347:355], 50 | 59: _Attr_name[355:363], 51 | 60: _Attr_name[363:374], 52 | 61: _Attr_name[374:383], 53 | 62: _Attr_name[383:391], 54 | 63: _Attr_name[391:399], 55 | 64: _Attr_name[399:408], 56 | 65: _Attr_name[408:414], 57 | 66: _Attr_name[414:428], 58 | 67: _Attr_name[428:437], 59 | 68: _Attr_name[437:449], 60 | 69: _Attr_name[449:457], 61 | 70: _Attr_name[457:464], 62 | 71: _Attr_name[464:477], 63 | 72: _Attr_name[477:487], 64 | 73: _Attr_name[487:491], 65 | 74: _Attr_name[491:502], 66 | 75: _Attr_name[502:510], 67 | 76: _Attr_name[510:520], 68 | 77: _Attr_name[520:533], 69 | 78: _Attr_name[533:542], 70 | 79: _Attr_name[542:552], 71 | 80: _Attr_name[552:564], 72 | 81: _Attr_name[564:570], 73 | 82: _Attr_name[570:577], 74 | 83: _Attr_name[577:584], 75 | 84: _Attr_name[584:593], 76 | 85: _Attr_name[593:599], 77 | 86: _Attr_name[599:609], 78 | 87: _Attr_name[609:619], 79 | 88: _Attr_name[619:627], 80 | 89: _Attr_name[627:635], 81 | 90: _Attr_name[635:646], 82 | } 83 | 84 | func (i Attr) String() string { 85 | if str, ok := _Attr_map[i]; ok { 86 | return str 87 | } 88 | return "Attr(" + strconv.FormatInt(int64(i), 10) + ")" 89 | } 90 | -------------------------------------------------------------------------------- /dwarf/buf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Buffered reading and decoding of DWARF data streams. 6 | 7 | package dwarf 8 | 9 | import ( 10 | "encoding/binary" 11 | "strconv" 12 | ) 13 | 14 | // Data buffer being decoded. 15 | type buf struct { 16 | dwarf *Data 17 | order binary.ByteOrder 18 | format dataFormat 19 | name string 20 | off Offset 21 | data []byte 22 | err error 23 | } 24 | 25 | // Data format, other than byte order. This affects the handling of 26 | // certain field formats. 27 | type dataFormat interface { 28 | // DWARF version number. Zero means unknown. 29 | version() int 30 | 31 | // 64-bit DWARF format? 32 | dwarf64() (dwarf64 bool, isKnown bool) 33 | 34 | // Size of an address, in bytes. Zero means unknown. 35 | addrsize() int 36 | } 37 | 38 | // Some parts of DWARF have no data format, e.g., abbrevs. 39 | type unknownFormat struct{} 40 | 41 | func (u unknownFormat) version() int { 42 | return 0 43 | } 44 | 45 | func (u unknownFormat) dwarf64() (bool, bool) { 46 | return false, false 47 | } 48 | 49 | func (u unknownFormat) addrsize() int { 50 | return 0 51 | } 52 | 53 | func makeBuf(d *Data, format dataFormat, name string, off Offset, data []byte) buf { 54 | return buf{d, d.order, format, name, off, data, nil} 55 | } 56 | 57 | func (b *buf) uint8() uint8 { 58 | if len(b.data) < 1 { 59 | b.error("underflow") 60 | return 0 61 | } 62 | val := b.data[0] 63 | b.data = b.data[1:] 64 | b.off++ 65 | return val 66 | } 67 | 68 | func (b *buf) bytes(n int) []byte { 69 | if len(b.data) < n { 70 | b.error("underflow") 71 | return nil 72 | } 73 | data := b.data[0:n] 74 | b.data = b.data[n:] 75 | b.off += Offset(n) 76 | return data 77 | } 78 | 79 | func (b *buf) skip(n int) { b.bytes(n) } 80 | 81 | func (b *buf) string() string { 82 | for i := 0; i < len(b.data); i++ { 83 | if b.data[i] == 0 { 84 | s := string(b.data[0:i]) 85 | b.data = b.data[i+1:] 86 | b.off += Offset(i + 1) 87 | return s 88 | } 89 | } 90 | b.error("underflow") 91 | return "" 92 | } 93 | 94 | func (b *buf) uint16() uint16 { 95 | a := b.bytes(2) 96 | if a == nil { 97 | return 0 98 | } 99 | return b.order.Uint16(a) 100 | } 101 | 102 | func (b *buf) uint32() uint32 { 103 | a := b.bytes(4) 104 | if a == nil { 105 | return 0 106 | } 107 | return b.order.Uint32(a) 108 | } 109 | 110 | func (b *buf) uint64() uint64 { 111 | a := b.bytes(8) 112 | if a == nil { 113 | return 0 114 | } 115 | return b.order.Uint64(a) 116 | } 117 | 118 | // Read a varint, which is 7 bits per byte, little endian. 119 | // the 0x80 bit means read another byte. 120 | func (b *buf) varint() (c uint64, bits uint) { 121 | for i := 0; i < len(b.data); i++ { 122 | byte := b.data[i] 123 | c |= uint64(byte&0x7F) << bits 124 | bits += 7 125 | if byte&0x80 == 0 { 126 | b.off += Offset(i + 1) 127 | b.data = b.data[i+1:] 128 | return c, bits 129 | } 130 | } 131 | return 0, 0 132 | } 133 | 134 | // Unsigned int is just a varint. 135 | func (b *buf) uint() uint64 { 136 | x, _ := b.varint() 137 | return x 138 | } 139 | 140 | // Signed int is a sign-extended varint. 141 | func (b *buf) int() int64 { 142 | ux, bits := b.varint() 143 | x := int64(ux) 144 | if x&(1<<(bits-1)) != 0 { 145 | x |= -1 << bits 146 | } 147 | return x 148 | } 149 | 150 | // Address-sized uint. 151 | func (b *buf) addr() uint64 { 152 | switch b.format.addrsize() { 153 | case 1: 154 | return uint64(b.uint8()) 155 | case 2: 156 | return uint64(b.uint16()) 157 | case 4: 158 | return uint64(b.uint32()) 159 | case 8: 160 | return b.uint64() 161 | } 162 | b.error("unknown address size") 163 | return 0 164 | } 165 | 166 | func (b *buf) unitLength() (length Offset, dwarf64 bool) { 167 | length = Offset(b.uint32()) 168 | if length == 0xffffffff { 169 | dwarf64 = true 170 | length = Offset(b.uint64()) 171 | } else if length >= 0xfffffff0 { 172 | b.error("unit length has reserved value") 173 | } 174 | return 175 | } 176 | 177 | func (b *buf) error(s string) { 178 | if b.err == nil { 179 | b.data = nil 180 | b.err = DecodeError{b.name, b.off, s} 181 | } 182 | } 183 | 184 | type DecodeError struct { 185 | Name string 186 | Offset Offset 187 | Err string 188 | } 189 | 190 | func (e DecodeError) Error() string { 191 | return "decoding dwarf section " + e.Name + " at offset 0x" + strconv.FormatInt(int64(e.Offset), 16) + ": " + e.Err 192 | } 193 | -------------------------------------------------------------------------------- /dwarf/class_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=Class"; DO NOT EDIT. 2 | 3 | package dwarf 4 | 5 | import "strconv" 6 | 7 | const _Class_name = "ClassUnknownClassAddressClassBlockClassConstantClassExprLocClassFlagClassLinePtrClassLocListPtrClassMacPtrClassRangeListPtrClassReferenceClassReferenceSigClassStringClassReferenceAltClassStringAlt" 8 | 9 | var _Class_index = [...]uint8{0, 12, 24, 34, 47, 59, 68, 80, 95, 106, 123, 137, 154, 165, 182, 196} 10 | 11 | func (i Class) String() string { 12 | if i < 0 || i >= Class(len(_Class_index)-1) { 13 | return "Class(" + strconv.FormatInt(int64(i), 10) + ")" 14 | } 15 | return _Class_name[_Class_index[i]:_Class_index[i+1]] 16 | } 17 | -------------------------------------------------------------------------------- /dwarf/const.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Constants 6 | 7 | package dwarf 8 | 9 | //go:generate stringer -type Attr -trimprefix=Attr 10 | 11 | // An Attr identifies the attribute type in a DWARF Entry's Field. 12 | type Attr uint32 13 | 14 | const ( 15 | AttrSibling Attr = 0x01 16 | AttrLocation Attr = 0x02 17 | AttrName Attr = 0x03 18 | AttrOrdering Attr = 0x09 19 | AttrByteSize Attr = 0x0B 20 | AttrBitOffset Attr = 0x0C 21 | AttrBitSize Attr = 0x0D 22 | AttrStmtList Attr = 0x10 23 | AttrLowpc Attr = 0x11 24 | AttrHighpc Attr = 0x12 25 | AttrLanguage Attr = 0x13 26 | AttrDiscr Attr = 0x15 27 | AttrDiscrValue Attr = 0x16 28 | AttrVisibility Attr = 0x17 29 | AttrImport Attr = 0x18 30 | AttrStringLength Attr = 0x19 31 | AttrCommonRef Attr = 0x1A 32 | AttrCompDir Attr = 0x1B 33 | AttrConstValue Attr = 0x1C 34 | AttrContainingType Attr = 0x1D 35 | AttrDefaultValue Attr = 0x1E 36 | AttrInline Attr = 0x20 37 | AttrIsOptional Attr = 0x21 38 | AttrLowerBound Attr = 0x22 39 | AttrProducer Attr = 0x25 40 | AttrPrototyped Attr = 0x27 41 | AttrReturnAddr Attr = 0x2A 42 | AttrStartScope Attr = 0x2C 43 | AttrStrideSize Attr = 0x2E 44 | AttrUpperBound Attr = 0x2F 45 | AttrAbstractOrigin Attr = 0x31 46 | AttrAccessibility Attr = 0x32 47 | AttrAddrClass Attr = 0x33 48 | AttrArtificial Attr = 0x34 49 | AttrBaseTypes Attr = 0x35 50 | AttrCalling Attr = 0x36 51 | AttrCount Attr = 0x37 52 | AttrDataMemberLoc Attr = 0x38 53 | AttrDeclColumn Attr = 0x39 54 | AttrDeclFile Attr = 0x3A 55 | AttrDeclLine Attr = 0x3B 56 | AttrDeclaration Attr = 0x3C 57 | AttrDiscrList Attr = 0x3D 58 | AttrEncoding Attr = 0x3E 59 | AttrExternal Attr = 0x3F 60 | AttrFrameBase Attr = 0x40 61 | AttrFriend Attr = 0x41 62 | AttrIdentifierCase Attr = 0x42 63 | AttrMacroInfo Attr = 0x43 64 | AttrNamelistItem Attr = 0x44 65 | AttrPriority Attr = 0x45 66 | AttrSegment Attr = 0x46 67 | AttrSpecification Attr = 0x47 68 | AttrStaticLink Attr = 0x48 69 | AttrType Attr = 0x49 70 | AttrUseLocation Attr = 0x4A 71 | AttrVarParam Attr = 0x4B 72 | AttrVirtuality Attr = 0x4C 73 | AttrVtableElemLoc Attr = 0x4D 74 | AttrAllocated Attr = 0x4E 75 | AttrAssociated Attr = 0x4F 76 | AttrDataLocation Attr = 0x50 77 | AttrStride Attr = 0x51 78 | AttrEntrypc Attr = 0x52 79 | AttrUseUTF8 Attr = 0x53 80 | AttrExtension Attr = 0x54 81 | AttrRanges Attr = 0x55 82 | AttrTrampoline Attr = 0x56 83 | AttrCallColumn Attr = 0x57 84 | AttrCallFile Attr = 0x58 85 | AttrCallLine Attr = 0x59 86 | AttrDescription Attr = 0x5A 87 | ) 88 | 89 | func (a Attr) GoString() string { 90 | if str, ok := _Attr_map[a]; ok { 91 | return "dwarf.Attr" + str 92 | } 93 | return "dwarf." + a.String() 94 | } 95 | 96 | // A format is a DWARF data encoding format. 97 | type format uint32 98 | 99 | const ( 100 | // value formats 101 | formAddr format = 0x01 102 | formDwarfBlock2 format = 0x03 103 | formDwarfBlock4 format = 0x04 104 | formData2 format = 0x05 105 | formData4 format = 0x06 106 | formData8 format = 0x07 107 | formString format = 0x08 108 | formDwarfBlock format = 0x09 109 | formDwarfBlock1 format = 0x0A 110 | formData1 format = 0x0B 111 | formFlag format = 0x0C 112 | formSdata format = 0x0D 113 | formStrp format = 0x0E 114 | formUdata format = 0x0F 115 | formRefAddr format = 0x10 116 | formRef1 format = 0x11 117 | formRef2 format = 0x12 118 | formRef4 format = 0x13 119 | formRef8 format = 0x14 120 | formRefUdata format = 0x15 121 | formIndirect format = 0x16 122 | // The following are new in DWARF 4. 123 | formSecOffset format = 0x17 124 | formExprloc format = 0x18 125 | formFlagPresent format = 0x19 126 | formRefSig8 format = 0x20 127 | // Extensions for multi-file compression (.dwz) 128 | // http://www.dwarfstd.org/ShowIssue.php?issue=120604.1 129 | formGnuRefAlt format = 0x1f20 130 | formGnuStrpAlt format = 0x1f21 131 | ) 132 | 133 | //go:generate stringer -type Tag -trimprefix=Tag 134 | 135 | // A Tag is the classification (the type) of an Entry. 136 | type Tag uint32 137 | 138 | const ( 139 | TagArrayType Tag = 0x01 140 | TagClassType Tag = 0x02 141 | TagEntryPoint Tag = 0x03 142 | TagEnumerationType Tag = 0x04 143 | TagFormalParameter Tag = 0x05 144 | TagImportedDeclaration Tag = 0x08 145 | TagLabel Tag = 0x0A 146 | TagLexDwarfBlock Tag = 0x0B 147 | TagMember Tag = 0x0D 148 | TagPointerType Tag = 0x0F 149 | TagReferenceType Tag = 0x10 150 | TagCompileUnit Tag = 0x11 151 | TagStringType Tag = 0x12 152 | TagStructType Tag = 0x13 153 | TagSubroutineType Tag = 0x15 154 | TagTypedef Tag = 0x16 155 | TagUnionType Tag = 0x17 156 | TagUnspecifiedParameters Tag = 0x18 157 | TagVariant Tag = 0x19 158 | TagCommonDwarfBlock Tag = 0x1A 159 | TagCommonInclusion Tag = 0x1B 160 | TagInheritance Tag = 0x1C 161 | TagInlinedSubroutine Tag = 0x1D 162 | TagModule Tag = 0x1E 163 | TagPtrToMemberType Tag = 0x1F 164 | TagSetType Tag = 0x20 165 | TagSubrangeType Tag = 0x21 166 | TagWithStmt Tag = 0x22 167 | TagAccessDeclaration Tag = 0x23 168 | TagBaseType Tag = 0x24 169 | TagCatchDwarfBlock Tag = 0x25 170 | TagConstType Tag = 0x26 171 | TagConstant Tag = 0x27 172 | TagEnumerator Tag = 0x28 173 | TagFileType Tag = 0x29 174 | TagFriend Tag = 0x2A 175 | TagNamelist Tag = 0x2B 176 | TagNamelistItem Tag = 0x2C 177 | TagPackedType Tag = 0x2D 178 | TagSubprogram Tag = 0x2E 179 | TagTemplateTypeParameter Tag = 0x2F 180 | TagTemplateValueParameter Tag = 0x30 181 | TagThrownType Tag = 0x31 182 | TagTryDwarfBlock Tag = 0x32 183 | TagVariantPart Tag = 0x33 184 | TagVariable Tag = 0x34 185 | TagVolatileType Tag = 0x35 186 | // The following are new in DWARF 3. 187 | TagDwarfProcedure Tag = 0x36 188 | TagRestrictType Tag = 0x37 189 | TagInterfaceType Tag = 0x38 190 | TagNamespace Tag = 0x39 191 | TagImportedModule Tag = 0x3A 192 | TagUnspecifiedType Tag = 0x3B 193 | TagPartialUnit Tag = 0x3C 194 | TagImportedUnit Tag = 0x3D 195 | TagMutableType Tag = 0x3E // Later removed from DWARF. 196 | TagCondition Tag = 0x3F 197 | TagSharedType Tag = 0x40 198 | // The following are new in DWARF 4. 199 | TagTypeUnit Tag = 0x41 200 | TagRvalueReferenceType Tag = 0x42 201 | TagTemplateAlias Tag = 0x43 202 | ) 203 | 204 | func (t Tag) GoString() string { 205 | if t <= TagTemplateAlias { 206 | return "dwarf.Tag" + t.String() 207 | } 208 | return "dwarf." + t.String() 209 | } 210 | 211 | // Location expression operators. 212 | // The debug info encodes value locations like 8(R3) 213 | // as a sequence of these op codes. 214 | // This package does not implement full expressions; 215 | // the opPlusUconst operator is expected by the type parser. 216 | const ( 217 | opAddr = 0x03 /* 1 op, const addr */ 218 | opDeref = 0x06 219 | opConst1u = 0x08 /* 1 op, 1 byte const */ 220 | opConst1s = 0x09 /* " signed */ 221 | opConst2u = 0x0A /* 1 op, 2 byte const */ 222 | opConst2s = 0x0B /* " signed */ 223 | opConst4u = 0x0C /* 1 op, 4 byte const */ 224 | opConst4s = 0x0D /* " signed */ 225 | opConst8u = 0x0E /* 1 op, 8 byte const */ 226 | opConst8s = 0x0F /* " signed */ 227 | opConstu = 0x10 /* 1 op, LEB128 const */ 228 | opConsts = 0x11 /* " signed */ 229 | opDup = 0x12 230 | opDrop = 0x13 231 | opOver = 0x14 232 | opPick = 0x15 /* 1 op, 1 byte stack index */ 233 | opSwap = 0x16 234 | opRot = 0x17 235 | opXderef = 0x18 236 | opAbs = 0x19 237 | opAnd = 0x1A 238 | opDiv = 0x1B 239 | opMinus = 0x1C 240 | opMod = 0x1D 241 | opMul = 0x1E 242 | opNeg = 0x1F 243 | opNot = 0x20 244 | opOr = 0x21 245 | opPlus = 0x22 246 | opPlusUconst = 0x23 /* 1 op, ULEB128 addend */ 247 | opShl = 0x24 248 | opShr = 0x25 249 | opShra = 0x26 250 | opXor = 0x27 251 | opSkip = 0x2F /* 1 op, signed 2-byte constant */ 252 | opBra = 0x28 /* 1 op, signed 2-byte constant */ 253 | opEq = 0x29 254 | opGe = 0x2A 255 | opGt = 0x2B 256 | opLe = 0x2C 257 | opLt = 0x2D 258 | opNe = 0x2E 259 | opLit0 = 0x30 260 | /* OpLitN = OpLit0 + N for N = 0..31 */ 261 | opReg0 = 0x50 262 | /* OpRegN = OpReg0 + N for N = 0..31 */ 263 | opBreg0 = 0x70 /* 1 op, signed LEB128 constant */ 264 | /* OpBregN = OpBreg0 + N for N = 0..31 */ 265 | opRegx = 0x90 /* 1 op, ULEB128 register */ 266 | opFbreg = 0x91 /* 1 op, SLEB128 offset */ 267 | opBregx = 0x92 /* 2 op, ULEB128 reg; SLEB128 off */ 268 | opPiece = 0x93 /* 1 op, ULEB128 size of piece */ 269 | opDerefSize = 0x94 /* 1-byte size of data retrieved */ 270 | opXderefSize = 0x95 /* 1-byte size of data retrieved */ 271 | opNop = 0x96 272 | /* next four new in Dwarf v3 */ 273 | opPushObjAddr = 0x97 274 | opCall2 = 0x98 /* 2-byte offset of DIE */ 275 | opCall4 = 0x99 /* 4-byte offset of DIE */ 276 | opCallRef = 0x9A /* 4- or 8- byte offset of DIE */ 277 | /* 0xE0-0xFF reserved for user-specific */ 278 | ) 279 | 280 | // Basic type encodings -- the value for AttrEncoding in a TagBaseType Entry. 281 | const ( 282 | encAddress = 0x01 283 | encBoolean = 0x02 284 | encComplexFloat = 0x03 285 | encFloat = 0x04 286 | encSigned = 0x05 287 | encSignedChar = 0x06 288 | encUnsigned = 0x07 289 | encUnsignedChar = 0x08 290 | encImaginaryFloat = 0x09 291 | ) 292 | 293 | // Statement program standard opcode encodings. 294 | const ( 295 | lnsCopy = 1 296 | lnsAdvancePC = 2 297 | lnsAdvanceLine = 3 298 | lnsSetFile = 4 299 | lnsSetColumn = 5 300 | lnsNegateStmt = 6 301 | lnsSetBasicBlock = 7 302 | lnsConstAddPC = 8 303 | lnsFixedAdvancePC = 9 304 | 305 | // DWARF 3 306 | lnsSetPrologueEnd = 10 307 | lnsSetEpilogueBegin = 11 308 | lnsSetISA = 12 309 | ) 310 | 311 | // Statement program extended opcode encodings. 312 | const ( 313 | lneEndSequence = 1 314 | lneSetAddress = 2 315 | lneDefineFile = 3 316 | 317 | // DWARF 4 318 | lneSetDiscriminator = 4 319 | ) 320 | -------------------------------------------------------------------------------- /dwarf/entry_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package dwarf_test 6 | 7 | import ( 8 | . "debug/dwarf" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func TestSplit(t *testing.T) { 14 | // debug/dwarf doesn't (currently) support split DWARF, but 15 | // the attributes that pointed to the split DWARF used to 16 | // cause loading the DWARF data to fail entirely (issue 17 | // #12592). Test that we can at least read the DWARF data. 18 | d := elfData(t, "testdata/split.elf") 19 | r := d.Reader() 20 | e, err := r.Next() 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | if e.Tag != TagCompileUnit { 25 | t.Fatalf("bad tag: have %s, want %s", e.Tag, TagCompileUnit) 26 | } 27 | // Check that we were able to parse the unknown section offset 28 | // field, even if we can't figure out its DWARF class. 29 | const AttrGNUAddrBase Attr = 0x2133 30 | f := e.AttrField(AttrGNUAddrBase) 31 | if _, ok := f.Val.(int64); !ok { 32 | t.Fatalf("bad attribute value type: have %T, want int64", f.Val) 33 | } 34 | if f.Class != ClassUnknown { 35 | t.Fatalf("bad class: have %s, want %s", f.Class, ClassUnknown) 36 | } 37 | } 38 | 39 | // wantRange maps from a PC to the ranges of the compilation unit 40 | // containing that PC. 41 | type wantRange struct { 42 | pc uint64 43 | ranges [][2]uint64 44 | } 45 | 46 | func TestReaderSeek(t *testing.T) { 47 | want := []wantRange{ 48 | {0x40059d, [][2]uint64{{0x40059d, 0x400601}}}, 49 | {0x400600, [][2]uint64{{0x40059d, 0x400601}}}, 50 | {0x400601, [][2]uint64{{0x400601, 0x400611}}}, 51 | {0x4005f0, [][2]uint64{{0x40059d, 0x400601}}}, // loop test 52 | {0x10, nil}, 53 | {0x400611, nil}, 54 | } 55 | testRanges(t, "testdata/line-gcc.elf", want) 56 | } 57 | 58 | func TestRangesSection(t *testing.T) { 59 | want := []wantRange{ 60 | {0x400500, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}}, 61 | {0x400400, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}}, 62 | {0x400548, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}}, 63 | {0x400407, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}}, 64 | {0x400408, nil}, 65 | {0x400449, nil}, 66 | {0x4003ff, nil}, 67 | } 68 | testRanges(t, "testdata/ranges.elf", want) 69 | } 70 | 71 | func testRanges(t *testing.T, name string, want []wantRange) { 72 | d := elfData(t, name) 73 | r := d.Reader() 74 | for _, w := range want { 75 | entry, err := r.SeekPC(w.pc) 76 | if err != nil { 77 | if w.ranges != nil { 78 | t.Errorf("%s: missing Entry for %#x", name, w.pc) 79 | } 80 | if err != ErrUnknownPC { 81 | t.Errorf("%s: expected ErrUnknownPC for %#x, got %v", name, w.pc, err) 82 | } 83 | continue 84 | } 85 | 86 | ranges, err := d.Ranges(entry) 87 | if err != nil { 88 | t.Errorf("%s: %v", name, err) 89 | continue 90 | } 91 | if !reflect.DeepEqual(ranges, w.ranges) { 92 | t.Errorf("%s: for %#x got %x, expected %x", name, w.pc, ranges, w.ranges) 93 | } 94 | } 95 | } 96 | 97 | func TestReaderRanges(t *testing.T) { 98 | d := elfData(t, "testdata/line-gcc.elf") 99 | 100 | subprograms := []struct { 101 | name string 102 | ranges [][2]uint64 103 | }{ 104 | {"f1", [][2]uint64{{0x40059d, 0x4005e7}}}, 105 | {"main", [][2]uint64{{0x4005e7, 0x400601}}}, 106 | {"f2", [][2]uint64{{0x400601, 0x400611}}}, 107 | } 108 | 109 | r := d.Reader() 110 | i := 0 111 | for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() { 112 | if entry.Tag != TagSubprogram { 113 | continue 114 | } 115 | 116 | if i > len(subprograms) { 117 | t.Fatalf("too many subprograms (expected at most %d)", i) 118 | } 119 | 120 | if got := entry.Val(AttrName).(string); got != subprograms[i].name { 121 | t.Errorf("subprogram %d name is %s, expected %s", i, got, subprograms[i].name) 122 | } 123 | ranges, err := d.Ranges(entry) 124 | if err != nil { 125 | t.Errorf("subprogram %d: %v", i, err) 126 | continue 127 | } 128 | if !reflect.DeepEqual(ranges, subprograms[i].ranges) { 129 | t.Errorf("subprogram %d ranges are %x, expected %x", i, ranges, subprograms[i].ranges) 130 | } 131 | i++ 132 | } 133 | 134 | if i < len(subprograms) { 135 | t.Errorf("saw only %d subprograms, expected %d", i, len(subprograms)) 136 | } 137 | } 138 | 139 | func Test64Bit(t *testing.T) { 140 | // I don't know how to generate a 64-bit DWARF debug 141 | // compilation unit except by using XCOFF, so this is 142 | // hand-written. 143 | tests := []struct { 144 | name string 145 | info []byte 146 | }{ 147 | { 148 | "32-bit little", 149 | []byte{0x30, 0, 0, 0, // comp unit length 150 | 4, 0, // DWARF version 4 151 | 0, 0, 0, 0, // abbrev offset 152 | 8, // address size 153 | 0, 154 | 0, 0, 0, 0, 0, 0, 0, 0, 155 | 0, 0, 0, 0, 0, 0, 0, 0, 156 | 0, 0, 0, 0, 0, 0, 0, 0, 157 | 0, 0, 0, 0, 0, 0, 0, 0, 158 | 0, 0, 0, 0, 0, 0, 0, 0, 159 | }, 160 | }, 161 | { 162 | "64-bit little", 163 | []byte{0xff, 0xff, 0xff, 0xff, // 64-bit DWARF 164 | 0x30, 0, 0, 0, 0, 0, 0, 0, // comp unit length 165 | 4, 0, // DWARF version 4 166 | 0, 0, 0, 0, 0, 0, 0, 0, // abbrev offset 167 | 8, // address size 168 | 0, 0, 0, 0, 0, 169 | 0, 0, 0, 0, 0, 0, 0, 0, 170 | 0, 0, 0, 0, 0, 0, 0, 0, 171 | 0, 0, 0, 0, 0, 0, 0, 0, 172 | 0, 0, 0, 0, 0, 0, 0, 0, 173 | }, 174 | }, 175 | { 176 | "64-bit big", 177 | []byte{0xff, 0xff, 0xff, 0xff, // 64-bit DWARF 178 | 0, 0, 0, 0, 0, 0, 0, 0x30, // comp unit length 179 | 0, 4, // DWARF version 4 180 | 0, 0, 0, 0, 0, 0, 0, 0, // abbrev offset 181 | 8, // address size 182 | 0, 0, 0, 0, 0, 183 | 0, 0, 0, 0, 0, 0, 0, 0, 184 | 0, 0, 0, 0, 0, 0, 0, 0, 185 | 0, 0, 0, 0, 0, 0, 0, 0, 186 | 0, 0, 0, 0, 0, 0, 0, 0, 187 | }, 188 | }, 189 | } 190 | 191 | for _, test := range tests { 192 | _, err := New(nil, nil, nil, test.info, nil, nil, nil, nil) 193 | if err != nil { 194 | t.Errorf("%s: %v", test.name, err) 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /dwarf/export_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package dwarf 6 | 7 | var PathJoin = pathJoin 8 | -------------------------------------------------------------------------------- /dwarf/line_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package dwarf_test 6 | 7 | import ( 8 | . "debug/dwarf" 9 | "io" 10 | "strings" 11 | "testing" 12 | ) 13 | 14 | var ( 15 | file1C = &LineFile{Name: "/home/austin/go.dev/src/debug/dwarf/testdata/line1.c"} 16 | file1H = &LineFile{Name: "/home/austin/go.dev/src/debug/dwarf/testdata/line1.h"} 17 | file2C = &LineFile{Name: "/home/austin/go.dev/src/debug/dwarf/testdata/line2.c"} 18 | ) 19 | 20 | func TestLineELFGCC(t *testing.T) { 21 | // Generated by: 22 | // # gcc --version | head -n1 23 | // gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 24 | // # gcc -g -o line-gcc.elf line*.c 25 | 26 | // Line table based on readelf --debug-dump=rawline,decodedline 27 | want := []LineEntry{ 28 | {Address: 0x40059d, File: file1H, Line: 2, IsStmt: true}, 29 | {Address: 0x4005a5, File: file1H, Line: 2, IsStmt: true}, 30 | {Address: 0x4005b4, File: file1H, Line: 5, IsStmt: true}, 31 | {Address: 0x4005bd, File: file1H, Line: 6, IsStmt: true, Discriminator: 2}, 32 | {Address: 0x4005c7, File: file1H, Line: 5, IsStmt: true, Discriminator: 2}, 33 | {Address: 0x4005cb, File: file1H, Line: 5, IsStmt: false, Discriminator: 1}, 34 | {Address: 0x4005d1, File: file1H, Line: 7, IsStmt: true}, 35 | {Address: 0x4005e7, File: file1C, Line: 6, IsStmt: true}, 36 | {Address: 0x4005eb, File: file1C, Line: 7, IsStmt: true}, 37 | {Address: 0x4005f5, File: file1C, Line: 8, IsStmt: true}, 38 | {Address: 0x4005ff, File: file1C, Line: 9, IsStmt: true}, 39 | {Address: 0x400601, EndSequence: true}, 40 | 41 | {Address: 0x400601, File: file2C, Line: 4, IsStmt: true}, 42 | {Address: 0x400605, File: file2C, Line: 5, IsStmt: true}, 43 | {Address: 0x40060f, File: file2C, Line: 6, IsStmt: true}, 44 | {Address: 0x400611, EndSequence: true}, 45 | } 46 | 47 | testLineTable(t, want, elfData(t, "testdata/line-gcc.elf")) 48 | } 49 | 50 | func TestLineGCCWindows(t *testing.T) { 51 | // Generated by: 52 | // > gcc --version 53 | // gcc (tdm64-1) 4.9.2 54 | // > gcc -g -o line-gcc-win.bin line1.c C:\workdir\go\src\debug\dwarf\testdata\line2.c 55 | 56 | toWindows := func(lf *LineFile) *LineFile { 57 | lf2 := *lf 58 | lf2.Name = strings.Replace(lf2.Name, "/home/austin/go.dev/", "C:\\workdir\\go\\", -1) 59 | lf2.Name = strings.Replace(lf2.Name, "/", "\\", -1) 60 | return &lf2 61 | } 62 | file1C := toWindows(file1C) 63 | file1H := toWindows(file1H) 64 | file2C := toWindows(file2C) 65 | 66 | // Line table based on objdump --dwarf=rawline,decodedline 67 | want := []LineEntry{ 68 | {Address: 0x401530, File: file1H, Line: 2, IsStmt: true}, 69 | {Address: 0x401538, File: file1H, Line: 5, IsStmt: true}, 70 | {Address: 0x401541, File: file1H, Line: 6, IsStmt: true, Discriminator: 3}, 71 | {Address: 0x40154b, File: file1H, Line: 5, IsStmt: true, Discriminator: 3}, 72 | {Address: 0x40154f, File: file1H, Line: 5, IsStmt: false, Discriminator: 1}, 73 | {Address: 0x401555, File: file1H, Line: 7, IsStmt: true}, 74 | {Address: 0x40155b, File: file1C, Line: 6, IsStmt: true}, 75 | {Address: 0x401563, File: file1C, Line: 6, IsStmt: true}, 76 | {Address: 0x401568, File: file1C, Line: 7, IsStmt: true}, 77 | {Address: 0x40156d, File: file1C, Line: 8, IsStmt: true}, 78 | {Address: 0x401572, File: file1C, Line: 9, IsStmt: true}, 79 | {Address: 0x401578, EndSequence: true}, 80 | 81 | {Address: 0x401580, File: file2C, Line: 4, IsStmt: true}, 82 | {Address: 0x401588, File: file2C, Line: 5, IsStmt: true}, 83 | {Address: 0x401595, File: file2C, Line: 6, IsStmt: true}, 84 | {Address: 0x40159b, EndSequence: true}, 85 | } 86 | 87 | testLineTable(t, want, peData(t, "testdata/line-gcc-win.bin")) 88 | } 89 | 90 | func TestLineELFClang(t *testing.T) { 91 | // Generated by: 92 | // # clang --version | head -n1 93 | // Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4) 94 | // # clang -g -o line-clang.elf line*. 95 | 96 | want := []LineEntry{ 97 | {Address: 0x400530, File: file1C, Line: 6, IsStmt: true}, 98 | {Address: 0x400534, File: file1C, Line: 7, IsStmt: true, PrologueEnd: true}, 99 | {Address: 0x400539, File: file1C, Line: 8, IsStmt: true}, 100 | {Address: 0x400545, File: file1C, Line: 9, IsStmt: true}, 101 | {Address: 0x400550, File: file1H, Line: 2, IsStmt: true}, 102 | {Address: 0x400554, File: file1H, Line: 5, IsStmt: true, PrologueEnd: true}, 103 | {Address: 0x400568, File: file1H, Line: 6, IsStmt: true}, 104 | {Address: 0x400571, File: file1H, Line: 5, IsStmt: true}, 105 | {Address: 0x400581, File: file1H, Line: 7, IsStmt: true}, 106 | {Address: 0x400583, EndSequence: true}, 107 | 108 | {Address: 0x400590, File: file2C, Line: 4, IsStmt: true}, 109 | {Address: 0x4005a0, File: file2C, Line: 5, IsStmt: true, PrologueEnd: true}, 110 | {Address: 0x4005a7, File: file2C, Line: 6, IsStmt: true}, 111 | {Address: 0x4005b0, EndSequence: true}, 112 | } 113 | 114 | testLineTable(t, want, elfData(t, "testdata/line-clang.elf")) 115 | } 116 | 117 | func TestLineSeek(t *testing.T) { 118 | d := elfData(t, "testdata/line-gcc.elf") 119 | 120 | // Get the line table for the first CU. 121 | cu, err := d.Reader().Next() 122 | if err != nil { 123 | t.Fatal("d.Reader().Next:", err) 124 | } 125 | lr, err := d.LineReader(cu) 126 | if err != nil { 127 | t.Fatal("d.LineReader:", err) 128 | } 129 | 130 | // Read entries forward. 131 | var line LineEntry 132 | var posTable []LineReaderPos 133 | var table []LineEntry 134 | for { 135 | posTable = append(posTable, lr.Tell()) 136 | 137 | err := lr.Next(&line) 138 | if err != nil { 139 | if err == io.EOF { 140 | break 141 | } 142 | t.Fatal("lr.Next:", err) 143 | } 144 | table = append(table, line) 145 | } 146 | 147 | // Test that Reset returns to the first line. 148 | lr.Reset() 149 | if err := lr.Next(&line); err != nil { 150 | t.Fatal("lr.Next after Reset failed:", err) 151 | } else if line != table[0] { 152 | t.Fatal("lr.Next after Reset returned", line, "instead of", table[0]) 153 | } 154 | 155 | // Check that entries match when seeking backward. 156 | for i := len(posTable) - 1; i >= 0; i-- { 157 | lr.Seek(posTable[i]) 158 | err := lr.Next(&line) 159 | if i == len(posTable)-1 { 160 | if err != io.EOF { 161 | t.Fatal("expected io.EOF after seek to end, got", err) 162 | } 163 | } else if err != nil { 164 | t.Fatal("lr.Next after seek to", posTable[i], "failed:", err) 165 | } else if line != table[i] { 166 | t.Fatal("lr.Next after seek to", posTable[i], "returned", line, "instead of", table[i]) 167 | } 168 | } 169 | 170 | // Check that seeking to a PC returns the right line. 171 | if err := lr.SeekPC(table[0].Address-1, &line); err != ErrUnknownPC { 172 | t.Fatalf("lr.SeekPC to %#x returned %v instead of ErrUnknownPC", table[0].Address-1, err) 173 | } 174 | for i, testLine := range table { 175 | if testLine.EndSequence { 176 | if err := lr.SeekPC(testLine.Address, &line); err != ErrUnknownPC { 177 | t.Fatalf("lr.SeekPC to %#x returned %v instead of ErrUnknownPC", testLine.Address, err) 178 | } 179 | continue 180 | } 181 | 182 | nextPC := table[i+1].Address 183 | for pc := testLine.Address; pc < nextPC; pc++ { 184 | if err := lr.SeekPC(pc, &line); err != nil { 185 | t.Fatalf("lr.SeekPC to %#x failed: %v", pc, err) 186 | } else if line != testLine { 187 | t.Fatalf("lr.SeekPC to %#x returned %v instead of %v", pc, line, testLine) 188 | } 189 | } 190 | } 191 | } 192 | 193 | func testLineTable(t *testing.T, want []LineEntry, d *Data) { 194 | // Get line table from d. 195 | var got []LineEntry 196 | dr := d.Reader() 197 | for { 198 | ent, err := dr.Next() 199 | if err != nil { 200 | t.Fatal("dr.Next:", err) 201 | } else if ent == nil { 202 | break 203 | } 204 | 205 | if ent.Tag != TagCompileUnit { 206 | dr.SkipChildren() 207 | continue 208 | } 209 | 210 | // Decode CU's line table. 211 | lr, err := d.LineReader(ent) 212 | if err != nil { 213 | t.Fatal("d.LineReader:", err) 214 | } else if lr == nil { 215 | continue 216 | } 217 | 218 | for { 219 | var line LineEntry 220 | err := lr.Next(&line) 221 | if err != nil { 222 | if err == io.EOF { 223 | break 224 | } 225 | t.Fatal("lr.Next:", err) 226 | } 227 | // Ignore sources from the Windows build environment. 228 | if strings.HasPrefix(line.File.Name, "C:\\crossdev\\") || 229 | strings.HasPrefix(line.File.Name, "C:/crossdev/") { 230 | continue 231 | } 232 | got = append(got, line) 233 | } 234 | } 235 | 236 | // Compare line tables. 237 | if !compareLines(got, want) { 238 | t.Log("Line tables do not match. Got:") 239 | dumpLines(t, got) 240 | t.Log("Want:") 241 | dumpLines(t, want) 242 | t.FailNow() 243 | } 244 | } 245 | 246 | func compareLines(a, b []LineEntry) bool { 247 | if len(a) != len(b) { 248 | return false 249 | } 250 | 251 | for i := range a { 252 | al, bl := a[i], b[i] 253 | // If both are EndSequence, then the only other valid 254 | // field is Address. Otherwise, test equality of all 255 | // fields. 256 | if al.EndSequence && bl.EndSequence && al.Address == bl.Address { 257 | continue 258 | } 259 | if al.File.Name != bl.File.Name { 260 | return false 261 | } 262 | al.File = nil 263 | bl.File = nil 264 | if al != bl { 265 | return false 266 | } 267 | } 268 | return true 269 | } 270 | 271 | func dumpLines(t *testing.T, lines []LineEntry) { 272 | for _, l := range lines { 273 | t.Logf(" %+v File:%+v", l, l.File) 274 | } 275 | } 276 | 277 | type joinTest struct { 278 | dirname, filename string 279 | path string 280 | } 281 | 282 | var joinTests = []joinTest{ 283 | {"a", "b", "a/b"}, 284 | {"a", "", "a"}, 285 | {"", "b", "b"}, 286 | {"/a", "b", "/a/b"}, 287 | {"/a/", "b", "/a/b"}, 288 | 289 | {`C:\Windows\`, `System32`, `C:\Windows\System32`}, 290 | {`C:\Windows\`, ``, `C:\Windows\`}, 291 | {`C:\`, `Windows`, `C:\Windows`}, 292 | {`C:\Windows\`, `C:System32`, `C:\Windows\System32`}, 293 | {`C:\Windows`, `a/b`, `C:\Windows\a/b`}, 294 | {`\\host\share\`, `foo`, `\\host\share\foo`}, 295 | {`\\host\share\`, `foo\bar`, `\\host\share\foo\bar`}, 296 | {`//host/share/`, `foo/bar`, `//host/share/foo/bar`}, 297 | 298 | // The following are "best effort". We shouldn't see relative 299 | // base directories in DWARF, but these test that pathJoin 300 | // doesn't fail miserably if it sees one. 301 | {`C:`, `a`, `C:a`}, 302 | {`C:`, `a\b`, `C:a\b`}, 303 | {`C:.`, `a`, `C:.\a`}, 304 | {`C:a`, `b`, `C:a\b`}, 305 | } 306 | 307 | func TestPathJoin(t *testing.T) { 308 | for _, test := range joinTests { 309 | got := PathJoin(test.dirname, test.filename) 310 | if test.path != got { 311 | t.Errorf("pathJoin(%q, %q) = %q, want %q", test.dirname, test.filename, got, test.path) 312 | } 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /dwarf/open.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package dwarf provides access to DWARF debugging information loaded from 6 | // executable files, as defined in the DWARF 2.0 Standard at 7 | // http://dwarfstd.org/doc/dwarf-2.0.0.pdf 8 | package dwarf 9 | 10 | import "encoding/binary" 11 | 12 | // Data represents the DWARF debugging information 13 | // loaded from an executable file (for example, an ELF or Mach-O executable). 14 | type Data struct { 15 | // raw data 16 | abbrev []byte 17 | aranges []byte 18 | frame []byte 19 | info []byte 20 | line []byte 21 | pubnames []byte 22 | ranges []byte 23 | str []byte 24 | 25 | // parsed data 26 | abbrevCache map[uint64]abbrevTable 27 | order binary.ByteOrder 28 | typeCache map[Offset]Type 29 | typeSigs map[uint64]*typeUnit 30 | unit []unit 31 | } 32 | 33 | // New returns a new Data object initialized from the given parameters. 34 | // Rather than calling this function directly, clients should typically use 35 | // the DWARF method of the File type of the appropriate package debug/elf, 36 | // debug/macho, or debug/pe. 37 | // 38 | // The []byte arguments are the data from the corresponding debug section 39 | // in the object file; for example, for an ELF object, abbrev is the contents of 40 | // the ".debug_abbrev" section. 41 | func New(abbrev, aranges, frame, info, line, pubnames, ranges, str []byte) (*Data, error) { 42 | d := &Data{ 43 | abbrev: abbrev, 44 | aranges: aranges, 45 | frame: frame, 46 | info: info, 47 | line: line, 48 | pubnames: pubnames, 49 | ranges: ranges, 50 | str: str, 51 | abbrevCache: make(map[uint64]abbrevTable), 52 | typeCache: make(map[Offset]Type), 53 | typeSigs: make(map[uint64]*typeUnit), 54 | } 55 | 56 | // Sniff .debug_info to figure out byte order. 57 | // 32-bit DWARF: 4 byte length, 2 byte version. 58 | // 64-bit DWARf: 4 bytes of 0xff, 8 byte length, 2 byte version. 59 | if len(d.info) < 6 { 60 | return nil, DecodeError{"info", Offset(len(d.info)), "too short"} 61 | } 62 | offset := 4 63 | if d.info[0] == 0xff && d.info[1] == 0xff && d.info[2] == 0xff && d.info[3] == 0xff { 64 | if len(d.info) < 14 { 65 | return nil, DecodeError{"info", Offset(len(d.info)), "too short"} 66 | } 67 | offset = 12 68 | } 69 | // Fetch the version, a tiny 16-bit number (1, 2, 3, 4, 5). 70 | x, y := d.info[offset], d.info[offset+1] 71 | switch { 72 | case x == 0 && y == 0: 73 | return nil, DecodeError{"info", 4, "unsupported version 0"} 74 | case x == 0: 75 | d.order = binary.BigEndian 76 | case y == 0: 77 | d.order = binary.LittleEndian 78 | default: 79 | return nil, DecodeError{"info", 4, "cannot determine byte order"} 80 | } 81 | 82 | u, err := d.parseUnits() 83 | if err != nil { 84 | return nil, err 85 | } 86 | d.unit = u 87 | return d, nil 88 | } 89 | 90 | // AddTypes will add one .debug_types section to the DWARF data. A 91 | // typical object with DWARF version 4 debug info will have multiple 92 | // .debug_types sections. The name is used for error reporting only, 93 | // and serves to distinguish one .debug_types section from another. 94 | func (d *Data) AddTypes(name string, types []byte) error { 95 | return d.parseTypes(name, types) 96 | } 97 | -------------------------------------------------------------------------------- /dwarf/tag_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type Tag -trimprefix=Tag"; DO NOT EDIT. 2 | 3 | package dwarf 4 | 5 | import "strconv" 6 | 7 | const ( 8 | _Tag_name_0 = "ArrayTypeClassTypeEntryPointEnumerationTypeFormalParameter" 9 | _Tag_name_1 = "ImportedDeclaration" 10 | _Tag_name_2 = "LabelLexDwarfBlock" 11 | _Tag_name_3 = "Member" 12 | _Tag_name_4 = "PointerTypeReferenceTypeCompileUnitStringTypeStructType" 13 | _Tag_name_5 = "SubroutineTypeTypedefUnionTypeUnspecifiedParametersVariantCommonDwarfBlockCommonInclusionInheritanceInlinedSubroutineModulePtrToMemberTypeSetTypeSubrangeTypeWithStmtAccessDeclarationBaseTypeCatchDwarfBlockConstTypeConstantEnumeratorFileTypeFriendNamelistNamelistItemPackedTypeSubprogramTemplateTypeParameterTemplateValueParameterThrownTypeTryDwarfBlockVariantPartVariableVolatileTypeDwarfProcedureRestrictTypeInterfaceTypeNamespaceImportedModuleUnspecifiedTypePartialUnitImportedUnitMutableTypeConditionSharedTypeTypeUnitRvalueReferenceTypeTemplateAlias" 14 | ) 15 | 16 | var ( 17 | _Tag_index_0 = [...]uint8{0, 9, 18, 28, 43, 58} 18 | _Tag_index_2 = [...]uint8{0, 5, 18} 19 | _Tag_index_4 = [...]uint8{0, 11, 24, 35, 45, 55} 20 | _Tag_index_5 = [...]uint16{0, 14, 21, 30, 51, 58, 74, 89, 100, 117, 123, 138, 145, 157, 165, 182, 190, 205, 214, 222, 232, 240, 246, 254, 266, 276, 286, 307, 329, 339, 352, 363, 371, 383, 397, 409, 422, 431, 445, 460, 471, 483, 494, 503, 513, 521, 540, 553} 21 | ) 22 | 23 | func (i Tag) String() string { 24 | switch { 25 | case 1 <= i && i <= 5: 26 | i -= 1 27 | return _Tag_name_0[_Tag_index_0[i]:_Tag_index_0[i+1]] 28 | case i == 8: 29 | return _Tag_name_1 30 | case 10 <= i && i <= 11: 31 | i -= 10 32 | return _Tag_name_2[_Tag_index_2[i]:_Tag_index_2[i+1]] 33 | case i == 13: 34 | return _Tag_name_3 35 | case 15 <= i && i <= 19: 36 | i -= 15 37 | return _Tag_name_4[_Tag_index_4[i]:_Tag_index_4[i+1]] 38 | case 21 <= i && i <= 67: 39 | i -= 21 40 | return _Tag_name_5[_Tag_index_5[i]:_Tag_index_5[i+1]] 41 | default: 42 | return "Tag(" + strconv.FormatInt(int64(i), 10) + ")" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /dwarf/testdata/cycle.c: -------------------------------------------------------------------------------- 1 | typedef struct aaa *AAA; 2 | typedef AAA BBB; 3 | struct aaa { BBB val; }; 4 | 5 | AAA x(void) { 6 | return (AAA)0; 7 | } 8 | -------------------------------------------------------------------------------- /dwarf/testdata/cycle.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/dwarf/testdata/cycle.elf -------------------------------------------------------------------------------- /dwarf/testdata/line-clang.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/dwarf/testdata/line-clang.elf -------------------------------------------------------------------------------- /dwarf/testdata/line-gcc-win.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/dwarf/testdata/line-gcc-win.bin -------------------------------------------------------------------------------- /dwarf/testdata/line-gcc.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/dwarf/testdata/line-gcc.elf -------------------------------------------------------------------------------- /dwarf/testdata/line1.c: -------------------------------------------------------------------------------- 1 | #include "line1.h" 2 | 3 | void f2(); 4 | 5 | int main() 6 | { 7 | f1(); 8 | f2(); 9 | } 10 | -------------------------------------------------------------------------------- /dwarf/testdata/line1.h: -------------------------------------------------------------------------------- 1 | static void f1() 2 | { 3 | char buf[10]; 4 | int i; 5 | for(i = 0; i < 10; i++) 6 | buf[i] = 1; 7 | } 8 | -------------------------------------------------------------------------------- /dwarf/testdata/line2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void f2() 4 | { 5 | printf("hello\n"); 6 | } 7 | -------------------------------------------------------------------------------- /dwarf/testdata/ranges.c: -------------------------------------------------------------------------------- 1 | // gcc -g -O2 -freorder-blocks-and-partition 2 | 3 | const char *arr[10000]; 4 | const char *hot = "hot"; 5 | const char *cold = "cold"; 6 | 7 | __attribute__((noinline)) 8 | void fn(int path) { 9 | int i; 10 | 11 | if (path) { 12 | for (i = 0; i < sizeof arr / sizeof arr[0]; i++) { 13 | arr[i] = hot; 14 | } 15 | } else { 16 | for (i = 0; i < sizeof arr / sizeof arr[0]; i++) { 17 | arr[i] = cold; 18 | } 19 | } 20 | } 21 | 22 | int main(int argc, char *argv[]) { 23 | fn(argc); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /dwarf/testdata/ranges.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/dwarf/testdata/ranges.elf -------------------------------------------------------------------------------- /dwarf/testdata/split.c: -------------------------------------------------------------------------------- 1 | // gcc -gsplit-dwarf split.c -o split.elf 2 | 3 | int main() 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /dwarf/testdata/split.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/dwarf/testdata/split.elf -------------------------------------------------------------------------------- /dwarf/testdata/typedef.c: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Linux ELF: 7 | gcc -gdwarf-2 -m64 -c typedef.c && gcc -gdwarf-2 -m64 -o typedef.elf typedef.o 8 | 9 | OS X Mach-O: 10 | gcc -gdwarf-2 -m64 -c typedef.c -o typedef.macho 11 | */ 12 | #include 13 | 14 | typedef volatile int* t_ptr_volatile_int; 15 | typedef const char *t_ptr_const_char; 16 | typedef long t_long; 17 | typedef unsigned short t_ushort; 18 | typedef int t_func_int_of_float_double(float, double); 19 | typedef int (*t_ptr_func_int_of_float_double)(float, double); 20 | typedef int (*t_ptr_func_int_of_float_complex)(float complex); 21 | typedef int (*t_ptr_func_int_of_double_complex)(double complex); 22 | typedef int (*t_ptr_func_int_of_long_double_complex)(long double complex); 23 | typedef int *t_func_ptr_int_of_char_schar_uchar(char, signed char, unsigned char); 24 | typedef void t_func_void_of_char(char); 25 | typedef void t_func_void_of_void(void); 26 | typedef void t_func_void_of_ptr_char_dots(char*, ...); 27 | typedef struct my_struct { 28 | volatile int vi; 29 | char x : 1; 30 | int y : 4; 31 | int z[0]; 32 | long long array[40]; 33 | int zz[0]; 34 | } t_my_struct; 35 | typedef struct my_struct1 { 36 | int zz [1]; 37 | } t_my_struct1; 38 | typedef union my_union { 39 | volatile int vi; 40 | char x : 1; 41 | int y : 4; 42 | long long array[40]; 43 | } t_my_union; 44 | typedef enum my_enum { 45 | e1 = 1, 46 | e2 = 2, 47 | e3 = -5, 48 | e4 = 1000000000000000LL, 49 | } t_my_enum; 50 | 51 | typedef struct list t_my_list; 52 | struct list { 53 | short val; 54 | t_my_list *next; 55 | }; 56 | 57 | typedef struct tree { 58 | struct tree *left, *right; 59 | unsigned long long val; 60 | } t_my_tree; 61 | 62 | t_ptr_volatile_int *a2; 63 | t_ptr_const_char **a3a; 64 | t_long *a4; 65 | t_ushort *a5; 66 | t_func_int_of_float_double *a6; 67 | t_ptr_func_int_of_float_double *a7; 68 | t_func_ptr_int_of_char_schar_uchar *a8; 69 | t_func_void_of_char *a9; 70 | t_func_void_of_void *a10; 71 | t_func_void_of_ptr_char_dots *a11; 72 | t_my_struct *a12; 73 | t_my_struct1 *a12a; 74 | t_my_union *a12b; 75 | t_my_enum *a13; 76 | t_my_list *a14; 77 | t_my_tree *a15; 78 | t_ptr_func_int_of_float_complex *a16; 79 | t_ptr_func_int_of_double_complex *a17; 80 | t_ptr_func_int_of_long_double_complex *a18; 81 | 82 | int main() 83 | { 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /dwarf/testdata/typedef.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/dwarf/testdata/typedef.elf -------------------------------------------------------------------------------- /dwarf/testdata/typedef.elf4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/dwarf/testdata/typedef.elf4 -------------------------------------------------------------------------------- /dwarf/testdata/typedef.macho: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/dwarf/testdata/typedef.macho -------------------------------------------------------------------------------- /dwarf/type_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package dwarf_test 6 | 7 | import ( 8 | . "debug/dwarf" 9 | "debug/elf" 10 | "debug/macho" 11 | "debug/pe" 12 | "testing" 13 | ) 14 | 15 | var typedefTests = map[string]string{ 16 | "t_ptr_volatile_int": "*volatile int", 17 | "t_ptr_const_char": "*const char", 18 | "t_long": "long int", 19 | "t_ushort": "short unsigned int", 20 | "t_func_int_of_float_double": "func(float, double) int", 21 | "t_ptr_func_int_of_float_double": "*func(float, double) int", 22 | "t_ptr_func_int_of_float_complex": "*func(complex float) int", 23 | "t_ptr_func_int_of_double_complex": "*func(complex double) int", 24 | "t_ptr_func_int_of_long_double_complex": "*func(complex long double) int", 25 | "t_func_ptr_int_of_char_schar_uchar": "func(char, signed char, unsigned char) *int", 26 | "t_func_void_of_char": "func(char) void", 27 | "t_func_void_of_void": "func() void", 28 | "t_func_void_of_ptr_char_dots": "func(*char, ...) void", 29 | "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}", 30 | "t_my_struct1": "struct my_struct1 {zz [1]int@0}", 31 | "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}", 32 | "t_my_enum": "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}", 33 | "t_my_list": "struct list {val short int@0; next *t_my_list@8}", 34 | "t_my_tree": "struct tree {left *struct tree@0; right *struct tree@8; val long long unsigned int@16}", 35 | } 36 | 37 | // As Apple converts gcc to a clang-based front end 38 | // they keep breaking the DWARF output. This map lists the 39 | // conversion from real answer to Apple answer. 40 | var machoBug = map[string]string{ 41 | "func(*char, ...) void": "func(*char) void", 42 | "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}": "enum my_enum {e1=1; e2=2; e3=-5; e4=-1530494976}", 43 | } 44 | 45 | func elfData(t *testing.T, name string) *Data { 46 | f, err := elf.Open(name) 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | 51 | d, err := f.DWARF() 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | return d 56 | } 57 | 58 | func machoData(t *testing.T, name string) *Data { 59 | f, err := macho.Open(name) 60 | if err != nil { 61 | t.Fatal(err) 62 | } 63 | 64 | d, err := f.DWARF() 65 | if err != nil { 66 | t.Fatal(err) 67 | } 68 | return d 69 | } 70 | 71 | func peData(t *testing.T, name string) *Data { 72 | f, err := pe.Open(name) 73 | if err != nil { 74 | t.Fatal(err) 75 | } 76 | 77 | d, err := f.DWARF() 78 | if err != nil { 79 | t.Fatal(err) 80 | } 81 | return d 82 | } 83 | 84 | func TestTypedefsELF(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf"), "elf") } 85 | 86 | func TestTypedefsMachO(t *testing.T) { 87 | testTypedefs(t, machoData(t, "testdata/typedef.macho"), "macho") 88 | } 89 | 90 | func TestTypedefsELFDwarf4(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf4"), "elf") } 91 | 92 | func testTypedefs(t *testing.T, d *Data, kind string) { 93 | r := d.Reader() 94 | seen := make(map[string]bool) 95 | for { 96 | e, err := r.Next() 97 | if err != nil { 98 | t.Fatal("r.Next:", err) 99 | } 100 | if e == nil { 101 | break 102 | } 103 | if e.Tag == TagTypedef { 104 | typ, err := d.Type(e.Offset) 105 | if err != nil { 106 | t.Fatal("d.Type:", err) 107 | } 108 | t1 := typ.(*TypedefType) 109 | var typstr string 110 | if ts, ok := t1.Type.(*StructType); ok { 111 | typstr = ts.Defn() 112 | } else { 113 | typstr = t1.Type.String() 114 | } 115 | 116 | if want, ok := typedefTests[t1.Name]; ok { 117 | if seen[t1.Name] { 118 | t.Errorf("multiple definitions for %s", t1.Name) 119 | } 120 | seen[t1.Name] = true 121 | if typstr != want && (kind != "macho" || typstr != machoBug[want]) { 122 | t.Errorf("%s:\n\thave %s\n\twant %s", t1.Name, typstr, want) 123 | } 124 | } 125 | } 126 | if e.Tag != TagCompileUnit { 127 | r.SkipChildren() 128 | } 129 | } 130 | 131 | for k := range typedefTests { 132 | if !seen[k] { 133 | t.Errorf("missing %s", k) 134 | } 135 | } 136 | } 137 | 138 | func TestTypedefCycle(t *testing.T) { 139 | // See issue #13039: reading a typedef cycle starting from a 140 | // different place than the size needed to be computed from 141 | // used to crash. 142 | // 143 | // cycle.elf built with GCC 4.8.4: 144 | // gcc -g -c -o cycle.elf cycle.c 145 | d := elfData(t, "testdata/cycle.elf") 146 | r := d.Reader() 147 | offsets := []Offset{} 148 | for { 149 | e, err := r.Next() 150 | if err != nil { 151 | t.Fatal("r.Next:", err) 152 | } 153 | if e == nil { 154 | break 155 | } 156 | switch e.Tag { 157 | case TagBaseType, TagTypedef, TagPointerType, TagStructType: 158 | offsets = append(offsets, e.Offset) 159 | } 160 | } 161 | 162 | // Parse each type with a fresh type cache. 163 | for _, offset := range offsets { 164 | d := elfData(t, "testdata/cycle.elf") 165 | _, err := d.Type(offset) 166 | if err != nil { 167 | t.Fatalf("d.Type(0x%x): %s", offset, err) 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /dwarf/typeunit.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package dwarf 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | // Parse the type units stored in a DWARF4 .debug_types section. Each 13 | // type unit defines a single primary type and an 8-byte signature. 14 | // Other sections may then use formRefSig8 to refer to the type. 15 | 16 | // The typeUnit format is a single type with a signature. It holds 17 | // the same data as a compilation unit. 18 | type typeUnit struct { 19 | unit 20 | toff Offset // Offset to signature type within data. 21 | name string // Name of .debug_type section. 22 | cache Type // Cache the type, nil to start. 23 | } 24 | 25 | // Parse a .debug_types section. 26 | func (d *Data) parseTypes(name string, types []byte) error { 27 | b := makeBuf(d, unknownFormat{}, name, 0, types) 28 | for len(b.data) > 0 { 29 | base := b.off 30 | n, dwarf64 := b.unitLength() 31 | if n != Offset(uint32(n)) { 32 | b.error("type unit length overflow") 33 | return b.err 34 | } 35 | hdroff := b.off 36 | vers := int(b.uint16()) 37 | if vers != 4 { 38 | b.error("unsupported DWARF version " + strconv.Itoa(vers)) 39 | return b.err 40 | } 41 | var ao uint64 42 | if !dwarf64 { 43 | ao = uint64(b.uint32()) 44 | } else { 45 | ao = b.uint64() 46 | } 47 | atable, err := d.parseAbbrev(ao, vers) 48 | if err != nil { 49 | return err 50 | } 51 | asize := b.uint8() 52 | sig := b.uint64() 53 | 54 | var toff uint32 55 | if !dwarf64 { 56 | toff = b.uint32() 57 | } else { 58 | to64 := b.uint64() 59 | if to64 != uint64(uint32(to64)) { 60 | b.error("type unit type offset overflow") 61 | return b.err 62 | } 63 | toff = uint32(to64) 64 | } 65 | 66 | boff := b.off 67 | d.typeSigs[sig] = &typeUnit{ 68 | unit: unit{ 69 | base: base, 70 | off: boff, 71 | data: b.bytes(int(n - (b.off - hdroff))), 72 | atable: atable, 73 | asize: int(asize), 74 | vers: vers, 75 | is64: dwarf64, 76 | }, 77 | toff: Offset(toff), 78 | name: name, 79 | } 80 | if b.err != nil { 81 | return b.err 82 | } 83 | } 84 | return nil 85 | } 86 | 87 | // Return the type for a type signature. 88 | func (d *Data) sigToType(sig uint64) (Type, error) { 89 | tu := d.typeSigs[sig] 90 | if tu == nil { 91 | return nil, fmt.Errorf("no type unit with signature %v", sig) 92 | } 93 | if tu.cache != nil { 94 | return tu.cache, nil 95 | } 96 | 97 | b := makeBuf(d, tu, tu.name, tu.off, tu.data) 98 | r := &typeUnitReader{d: d, tu: tu, b: b} 99 | t, err := d.readType(tu.name, r, tu.toff, make(map[Offset]Type), nil) 100 | if err != nil { 101 | return nil, err 102 | } 103 | 104 | tu.cache = t 105 | return t, nil 106 | } 107 | 108 | // typeUnitReader is a typeReader for a tagTypeUnit. 109 | type typeUnitReader struct { 110 | d *Data 111 | tu *typeUnit 112 | b buf 113 | err error 114 | } 115 | 116 | // Seek to a new position in the type unit. 117 | func (tur *typeUnitReader) Seek(off Offset) { 118 | tur.err = nil 119 | doff := off - tur.tu.off 120 | if doff < 0 || doff >= Offset(len(tur.tu.data)) { 121 | tur.err = fmt.Errorf("%s: offset %d out of range; max %d", tur.tu.name, doff, len(tur.tu.data)) 122 | return 123 | } 124 | tur.b = makeBuf(tur.d, tur.tu, tur.tu.name, off, tur.tu.data[doff:]) 125 | } 126 | 127 | // AddressSize returns the size in bytes of addresses in the current type unit. 128 | func (tur *typeUnitReader) AddressSize() int { 129 | return tur.tu.unit.asize 130 | } 131 | 132 | // Next reads the next Entry from the type unit. 133 | func (tur *typeUnitReader) Next() (*Entry, error) { 134 | if tur.err != nil { 135 | return nil, tur.err 136 | } 137 | if len(tur.tu.data) == 0 { 138 | return nil, nil 139 | } 140 | e := tur.b.entry(tur.tu.atable, tur.tu.base) 141 | if tur.b.err != nil { 142 | tur.err = tur.b.err 143 | return nil, tur.err 144 | } 145 | return e, nil 146 | } 147 | 148 | // clone returns a new reader for the type unit. 149 | func (tur *typeUnitReader) clone() typeReader { 150 | return &typeUnitReader{ 151 | d: tur.d, 152 | tu: tur.tu, 153 | b: makeBuf(tur.d, tur.tu, tur.tu.name, tur.tu.off, tur.tu.data), 154 | } 155 | } 156 | 157 | // offset returns the current offset. 158 | func (tur *typeUnitReader) offset() Offset { 159 | return tur.b.off 160 | } 161 | -------------------------------------------------------------------------------- /dwarf/unit.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package dwarf 6 | 7 | import ( 8 | "sort" 9 | "strconv" 10 | ) 11 | 12 | // DWARF debug info is split into a sequence of compilation units. 13 | // Each unit has its own abbreviation table and address size. 14 | 15 | type unit struct { 16 | base Offset // byte offset of header within the aggregate info 17 | off Offset // byte offset of data within the aggregate info 18 | data []byte 19 | atable abbrevTable 20 | asize int 21 | vers int 22 | is64 bool // True for 64-bit DWARF format 23 | } 24 | 25 | // Implement the dataFormat interface. 26 | 27 | func (u *unit) version() int { 28 | return u.vers 29 | } 30 | 31 | func (u *unit) dwarf64() (bool, bool) { 32 | return u.is64, true 33 | } 34 | 35 | func (u *unit) addrsize() int { 36 | return u.asize 37 | } 38 | 39 | func (d *Data) parseUnits() ([]unit, error) { 40 | // Count units. 41 | nunit := 0 42 | b := makeBuf(d, unknownFormat{}, "info", 0, d.info) 43 | for len(b.data) > 0 { 44 | len, _ := b.unitLength() 45 | if len != Offset(uint32(len)) { 46 | b.error("unit length overflow") 47 | break 48 | } 49 | b.skip(int(len)) 50 | nunit++ 51 | } 52 | if b.err != nil { 53 | return nil, b.err 54 | } 55 | 56 | // Again, this time writing them down. 57 | b = makeBuf(d, unknownFormat{}, "info", 0, d.info) 58 | units := make([]unit, nunit) 59 | for i := range units { 60 | u := &units[i] 61 | u.base = b.off 62 | var n Offset 63 | n, u.is64 = b.unitLength() 64 | dataOff := b.off 65 | vers := b.uint16() 66 | if vers != 2 && vers != 3 && vers != 4 { 67 | b.error("unsupported DWARF version " + strconv.Itoa(int(vers))) 68 | break 69 | } 70 | u.vers = int(vers) 71 | var abbrevOff uint64 72 | if u.is64 { 73 | abbrevOff = b.uint64() 74 | } else { 75 | abbrevOff = uint64(b.uint32()) 76 | } 77 | atable, err := d.parseAbbrev(abbrevOff, u.vers) 78 | if err != nil { 79 | if b.err == nil { 80 | b.err = err 81 | } 82 | break 83 | } 84 | u.atable = atable 85 | u.asize = int(b.uint8()) 86 | u.off = b.off 87 | u.data = b.bytes(int(n - (b.off - dataOff))) 88 | } 89 | if b.err != nil { 90 | return nil, b.err 91 | } 92 | return units, nil 93 | } 94 | 95 | // offsetToUnit returns the index of the unit containing offset off. 96 | // It returns -1 if no unit contains this offset. 97 | func (d *Data) offsetToUnit(off Offset) int { 98 | // Find the unit after off 99 | next := sort.Search(len(d.unit), func(i int) bool { 100 | return d.unit[i].off > off 101 | }) 102 | if next == 0 { 103 | return -1 104 | } 105 | u := &d.unit[next-1] 106 | if u.off <= off && off < u.off+Offset(len(u.data)) { 107 | return next - 1 108 | } 109 | return -1 110 | } 111 | -------------------------------------------------------------------------------- /elf/elf_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package elf 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | ) 11 | 12 | type nameTest struct { 13 | val interface{} 14 | str string 15 | } 16 | 17 | var nameTests = []nameTest{ 18 | {ELFOSABI_LINUX, "ELFOSABI_LINUX"}, 19 | {ET_EXEC, "ET_EXEC"}, 20 | {EM_860, "EM_860"}, 21 | {SHN_LOPROC, "SHN_LOPROC"}, 22 | {SHT_PROGBITS, "SHT_PROGBITS"}, 23 | {SHF_MERGE + SHF_TLS, "SHF_MERGE+SHF_TLS"}, 24 | {PT_LOAD, "PT_LOAD"}, 25 | {PF_W + PF_R + 0x50, "PF_W+PF_R+0x50"}, 26 | {DT_SYMBOLIC, "DT_SYMBOLIC"}, 27 | {DF_BIND_NOW, "DF_BIND_NOW"}, 28 | {NT_FPREGSET, "NT_FPREGSET"}, 29 | {STB_GLOBAL, "STB_GLOBAL"}, 30 | {STT_COMMON, "STT_COMMON"}, 31 | {STV_HIDDEN, "STV_HIDDEN"}, 32 | {R_X86_64_PC32, "R_X86_64_PC32"}, 33 | {R_ALPHA_OP_PUSH, "R_ALPHA_OP_PUSH"}, 34 | {R_ARM_THM_ABS5, "R_ARM_THM_ABS5"}, 35 | {R_386_GOT32, "R_386_GOT32"}, 36 | {R_PPC_GOT16_HI, "R_PPC_GOT16_HI"}, 37 | {R_SPARC_GOT22, "R_SPARC_GOT22"}, 38 | {ET_LOOS + 5, "ET_LOOS+5"}, 39 | {ProgFlag(0x50), "0x50"}, 40 | } 41 | 42 | func TestNames(t *testing.T) { 43 | for i, tt := range nameTests { 44 | s := fmt.Sprint(tt.val) 45 | if s != tt.str { 46 | t.Errorf("#%d: Sprint(%d) = %q, want %q", i, tt.val, s, tt.str) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /elf/exports.go: -------------------------------------------------------------------------------- 1 | package elf 2 | 3 | /* 4 | Any symbol in the dynamic symbol table (in .dynsym) for which .st_shndx == SHN_UNDEF 5 | (references undefined section) is an import, and every other symbol is defined and exported. 6 | */ 7 | 8 | // Export - describes a single export entry 9 | type Export struct { 10 | Name string 11 | VirtualAddress uint64 12 | } 13 | 14 | // Exports - gets exports 15 | func (f *File) Exports() ([]Export, error) { 16 | 17 | var exports []Export 18 | symbols, err := f.DynamicSymbols() 19 | if err != nil { 20 | return nil, err 21 | } 22 | for _, s := range symbols { 23 | if s.Section != SHN_UNDEF { 24 | exports = append(exports, Export{ 25 | Name: s.Name, 26 | VirtualAddress: s.Value, 27 | }) 28 | } 29 | } 30 | 31 | return exports, nil 32 | } 33 | -------------------------------------------------------------------------------- /elf/reader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package elf 6 | 7 | import ( 8 | "io" 9 | "os" 10 | ) 11 | 12 | // errorReader returns error from all operations. 13 | type errorReader struct { 14 | error 15 | } 16 | 17 | func (r errorReader) Read(p []byte) (n int, err error) { 18 | return 0, r.error 19 | } 20 | 21 | func (r errorReader) ReadAt(p []byte, off int64) (n int, err error) { 22 | return 0, r.error 23 | } 24 | 25 | func (r errorReader) Seek(offset int64, whence int) (int64, error) { 26 | return 0, r.error 27 | } 28 | 29 | func (r errorReader) Close() error { 30 | return r.error 31 | } 32 | 33 | // readSeekerFromReader converts an io.Reader into an io.ReadSeeker. 34 | // In general Seek may not be efficient, but it is optimized for 35 | // common cases such as seeking to the end to find the length of the 36 | // data. 37 | type readSeekerFromReader struct { 38 | reset func() (io.Reader, error) 39 | r io.Reader 40 | size int64 41 | offset int64 42 | } 43 | 44 | func (r *readSeekerFromReader) start() { 45 | x, err := r.reset() 46 | if err != nil { 47 | r.r = errorReader{err} 48 | } else { 49 | r.r = x 50 | } 51 | r.offset = 0 52 | } 53 | 54 | func (r *readSeekerFromReader) Read(p []byte) (n int, err error) { 55 | if r.r == nil { 56 | r.start() 57 | } 58 | n, err = r.r.Read(p) 59 | r.offset += int64(n) 60 | return n, err 61 | } 62 | 63 | func (r *readSeekerFromReader) Seek(offset int64, whence int) (int64, error) { 64 | var newOffset int64 65 | switch whence { 66 | case seekStart: 67 | newOffset = offset 68 | case seekCurrent: 69 | newOffset = r.offset + offset 70 | case seekEnd: 71 | newOffset = r.size + offset 72 | default: 73 | return 0, os.ErrInvalid 74 | } 75 | 76 | switch { 77 | case newOffset == r.offset: 78 | return newOffset, nil 79 | 80 | case newOffset < 0, newOffset > r.size: 81 | return 0, os.ErrInvalid 82 | 83 | case newOffset == 0: 84 | r.r = nil 85 | 86 | case newOffset == r.size: 87 | r.r = errorReader{io.EOF} 88 | 89 | default: 90 | if newOffset < r.offset { 91 | // Restart at the beginning. 92 | r.start() 93 | } 94 | // Read until we reach offset. 95 | var buf [512]byte 96 | for r.offset < newOffset { 97 | b := buf[:] 98 | if newOffset-r.offset < int64(len(buf)) { 99 | b = buf[:newOffset-r.offset] 100 | } 101 | if _, err := r.Read(b); err != nil { 102 | return 0, err 103 | } 104 | } 105 | } 106 | r.offset = newOffset 107 | return r.offset, nil 108 | } 109 | -------------------------------------------------------------------------------- /elf/testdata/compressed-32.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/compressed-32.obj -------------------------------------------------------------------------------- /elf/testdata/compressed-64.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/compressed-64.obj -------------------------------------------------------------------------------- /elf/testdata/gcc-386-freebsd-exec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/gcc-386-freebsd-exec -------------------------------------------------------------------------------- /elf/testdata/gcc-amd64-linux-exec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/gcc-amd64-linux-exec -------------------------------------------------------------------------------- /elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-clang-arm.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-clang-arm.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-clang-x86.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-clang-x86.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-gcc424-x86-64.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-gcc424-x86-64.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-gcc441-x86-64.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-gcc441-x86-64.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-gcc441-x86.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-gcc441-x86.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-gcc482-aarch64.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-gcc482-aarch64.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-gcc482-ppc64le.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-gcc482-ppc64le.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-gcc492-arm.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-gcc492-arm.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-gcc492-mips64.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-gcc492-mips64.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-gcc492-mipsle.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-gcc492-mipsle.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-gcc493-mips64le.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-gcc493-mips64le.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-gcc5-ppc.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-gcc5-ppc.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-gcc531-s390x.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-gcc531-s390x.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-gcc540-mips.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-gcc540-mips.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-gcc620-sparc64.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-gcc620-sparc64.obj -------------------------------------------------------------------------------- /elf/testdata/go-relocation-test-gcc720-riscv64.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/go-relocation-test-gcc720-riscv64.obj -------------------------------------------------------------------------------- /elf/testdata/hello-world-core.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/hello-world-core.gz -------------------------------------------------------------------------------- /elf/testdata/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void 4 | main(int argc, char *argv[]) 5 | { 6 | printf("hello, world\n"); 7 | } 8 | -------------------------------------------------------------------------------- /elf/testdata/zdebug-test-gcc484-x86-64.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/elf/testdata/zdebug-test-gcc484-x86-64.obj -------------------------------------------------------------------------------- /elf/write.go: -------------------------------------------------------------------------------- 1 | package elf 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/binary" 7 | "io/ioutil" 8 | "log" 9 | "os" 10 | ) 11 | 12 | // Bytes - returns the bytes of an Elf file 13 | func (elfFile *File) Bytes() ([]byte, error) { 14 | 15 | bytesWritten := uint64(0) 16 | elfBuf := bytes.NewBuffer(nil) 17 | w := bufio.NewWriter(elfBuf) 18 | 19 | // Write Elf Magic 20 | w.WriteByte('\x7f') 21 | w.WriteByte('E') 22 | w.WriteByte('L') 23 | w.WriteByte('F') 24 | bytesWritten += 4 25 | 26 | // ident[EI_CLASS] 27 | w.WriteByte(byte(elfFile.Class)) 28 | // ident[EI_DATA] 29 | w.WriteByte(byte(elfFile.Data)) 30 | // ident[EI_VERSION] 31 | w.WriteByte(byte(elfFile.Version)) 32 | // ident[EI_OSABI] 33 | w.WriteByte(byte(elfFile.OSABI)) 34 | // ident[EI_ABIVERSION] 35 | w.WriteByte(byte(elfFile.ABIVersion)) 36 | // ident[EI_PAD] ( 7 bytes ) 37 | w.Write([]byte{0, 0, 0, 0, 0, 0, 0}) 38 | bytesWritten += 12 39 | 40 | // Type 41 | binary.Write(w, elfFile.ByteOrder, uint16(elfFile.Type)) 42 | // Machine 43 | binary.Write(w, elfFile.ByteOrder, uint16(elfFile.Machine)) 44 | // Version 45 | binary.Write(w, elfFile.ByteOrder, uint32(elfFile.Version)) 46 | bytesWritten += 8 47 | 48 | phsize := 0 49 | 50 | switch elfFile.Class { 51 | case ELFCLASS32: 52 | phsize = 0x20 53 | // Entry 32 54 | binary.Write(w, elfFile.ByteOrder, uint32(elfFile.Entry)) 55 | // PH Offset 32 56 | binary.Write(w, elfFile.ByteOrder, uint32(0x34)) 57 | // SH Offset 32 // 0x20 0x28 4 8 e_shoff Points to the start of the section header table. 58 | binary.Write(w, elfFile.ByteOrder, int32(elfFile.FileHeader.SHTOffset)) 59 | // Flags 60 | binary.Write(w, elfFile.ByteOrder, uint32(0)) // todo 61 | // EH Size 62 | binary.Write(w, elfFile.ByteOrder, uint16(52)) 63 | // PH Size // 0x2A 0x36 2 e_phentsize Contains the size of a program header table entry. 64 | binary.Write(w, elfFile.ByteOrder, uint16(phsize)) 65 | // PH Num // 0x2C 0x38 2 e_phnum Contains the number of entries in the program header table. 66 | binary.Write(w, elfFile.ByteOrder, uint16(len(elfFile.Progs))) 67 | // SH Size // 0x2E 0x3A 2 e_shentsize Contains the size of a section header table entry. 68 | binary.Write(w, elfFile.ByteOrder, uint16(0x28)) 69 | bytesWritten += 24 70 | 71 | case ELFCLASS64: 72 | phsize = 0x38 73 | // Entry 64 74 | binary.Write(w, elfFile.ByteOrder, uint64(elfFile.Entry)) 75 | // PH Offset 64 76 | binary.Write(w, elfFile.ByteOrder, uint64(0x40)) 77 | // SH Offset 64 // 0x20 0x28 4 8 e_shoff Points to the start of the section header table. 78 | binary.Write(w, elfFile.ByteOrder, int64(elfFile.FileHeader.SHTOffset)) 79 | // Flags 80 | binary.Write(w, elfFile.ByteOrder, uint32(0)) // I think right? 81 | // EH Size 82 | binary.Write(w, elfFile.ByteOrder, uint16(64)) 83 | // PH Size // 0x2A 0x36 2 e_phentsize Contains the size of a program header table entry. 84 | binary.Write(w, elfFile.ByteOrder, uint16(phsize)) 85 | // PH Num // 0x2C 0x38 2 e_phnum Contains the number of entries in the program header table. 86 | binary.Write(w, elfFile.ByteOrder, uint16(len(elfFile.Progs))) 87 | // SH Size // 0x2E 0x3A 2 e_shentsize Contains the size of a section header table entry. 88 | binary.Write(w, elfFile.ByteOrder, uint16(0x40)) 89 | bytesWritten += 36 90 | } 91 | 92 | // SH Num // 0x30 0x3C 2 e_shnum Contains the number of entries in the section header table. 93 | binary.Write(w, elfFile.ByteOrder, uint16(len(elfFile.Sections))) 94 | // SH Str Ndx // 0x32 0x3E 2 e_shstrndx Contains index of the section header table entry that contains the section names. 95 | binary.Write(w, elfFile.ByteOrder, uint16(elfFile.ShStrIndex)) 96 | bytesWritten += 4 97 | 98 | // Program Header 99 | for _, p := range elfFile.Progs { 100 | // Type (segment) 101 | binary.Write(w, elfFile.ByteOrder, uint32(p.Type)) 102 | bytesWritten += 4 103 | 104 | switch elfFile.Class { 105 | case ELFCLASS32: 106 | // Offset of Segment in File 107 | binary.Write(w, elfFile.ByteOrder, uint32(p.Off)) 108 | 109 | // Vaddr 110 | binary.Write(w, elfFile.ByteOrder, uint32(p.Vaddr)) 111 | 112 | // Paddr 113 | binary.Write(w, elfFile.ByteOrder, uint32(p.Paddr)) 114 | 115 | // File Size 116 | binary.Write(w, elfFile.ByteOrder, uint32(p.Filesz)) 117 | 118 | // Memory Size 119 | binary.Write(w, elfFile.ByteOrder, uint32(p.Memsz)) 120 | 121 | // Flags (segment) 122 | binary.Write(w, elfFile.ByteOrder, uint32(p.Flags)) 123 | 124 | // Alignment 125 | binary.Write(w, elfFile.ByteOrder, uint32(p.Align)) 126 | 127 | bytesWritten += 28 128 | 129 | case ELFCLASS64: 130 | // Flags (segment) 131 | binary.Write(w, elfFile.ByteOrder, uint32(p.Flags)) 132 | 133 | // Offset of Segment in File 134 | binary.Write(w, elfFile.ByteOrder, uint64(p.Off)) 135 | 136 | // Vaddr 137 | binary.Write(w, elfFile.ByteOrder, uint64(p.Vaddr)) 138 | 139 | // Paddr 140 | binary.Write(w, elfFile.ByteOrder, uint64(p.Paddr)) 141 | 142 | // File Size 143 | binary.Write(w, elfFile.ByteOrder, uint64(p.Filesz)) 144 | 145 | // Memory Size 146 | binary.Write(w, elfFile.ByteOrder, uint64(p.Memsz)) 147 | 148 | // Alignment 149 | binary.Write(w, elfFile.ByteOrder, uint64(p.Align)) 150 | 151 | bytesWritten += 52 152 | } 153 | } 154 | 155 | sortedSections := elfFile.Sections[:] 156 | //sort.Slice(sortedSections, func(a, b int) bool { return elfFile.Sections[a].Link < elfFile.Sections[b].Link }) 157 | for _, s := range sortedSections { 158 | 159 | //log.Printf("Writing section: %s type: %+v\n", s.Name, s.Type) 160 | //log.Printf("written: %x offset: %x\n", bytesWritten, s.Offset) 161 | 162 | if s.Type == SHT_NULL || s.Type == SHT_NOBITS || s.FileSize == 0 { 163 | //log.Println("continuing...") 164 | continue 165 | } 166 | 167 | if bytesWritten > s.Offset { 168 | log.Printf("Overlapping Sections in Generated Elf: %+v\n", s.Name) 169 | continue 170 | } 171 | if s.Offset != 0 && bytesWritten < s.Offset { 172 | pad := make([]byte, s.Offset-bytesWritten) 173 | w.Write(pad) 174 | //log.Printf("Padding before section %s at %x: length:%x to:%x\n", s.Name, bytesWritten, len(pad), s.Offset) 175 | bytesWritten += uint64(len(pad)) 176 | } 177 | 178 | slen := 0 179 | switch s.Type { 180 | case SHT_DYNAMIC: 181 | for _, taggedValue := range elfFile.DynTags { 182 | //log.Printf("writing %d (%x) -> %d (%x)\n", taggedValue.Tag, taggedValue.Tag, taggedValue.Value, taggedValue.Value) 183 | switch elfFile.Class { 184 | case ELFCLASS32: 185 | binary.Write(w, elfFile.ByteOrder, uint32(taggedValue.Tag)) 186 | binary.Write(w, elfFile.ByteOrder, uint32(taggedValue.Value)) 187 | bytesWritten += 8 188 | case ELFCLASS64: 189 | binary.Write(w, elfFile.ByteOrder, uint64(taggedValue.Tag)) 190 | binary.Write(w, elfFile.ByteOrder, uint64(taggedValue.Value)) 191 | bytesWritten += 16 192 | } 193 | } 194 | default: 195 | section, err := ioutil.ReadAll(s.Open()) 196 | if err != nil { 197 | return nil, err 198 | } 199 | binary.Write(w, elfFile.ByteOrder, section) 200 | slen = len(section) 201 | //log.Printf("Wrote %s section at %x, length %x\n", s.Name, bytesWritten, slen) 202 | bytesWritten += uint64(slen) 203 | } 204 | 205 | // todo: elfFile.Insertion should be renamed InsertionLoadEnd or similar 206 | if s.Type == SHT_PROGBITS && len(elfFile.Insertion) > 0 && s.Size-uint64(slen) >= uint64(len(elfFile.Insertion)) { 207 | binary.Write(w, elfFile.ByteOrder, elfFile.Insertion) 208 | bytesWritten += uint64(len(elfFile.Insertion)) 209 | } 210 | w.Flush() 211 | } 212 | 213 | // Pad to Section Header Table 214 | if bytesWritten < uint64(elfFile.FileHeader.SHTOffset) { 215 | pad := make([]byte, uint64(elfFile.FileHeader.SHTOffset)-bytesWritten) 216 | w.Write(pad) 217 | //log.Printf("Padding before SHT at %x: length:%x to:%x\n", bytesWritten, len(pad), elfFile.FileHeader.SHTOffset) 218 | bytesWritten += uint64(len(pad)) 219 | } 220 | 221 | // Write Section Header Table 222 | 223 | for _, s := range elfFile.Sections[:] { 224 | 225 | switch elfFile.Class { 226 | case ELFCLASS32: 227 | binary.Write(w, elfFile.ByteOrder, &Section32{ 228 | Name: s.Shname, 229 | Type: uint32(s.Type), 230 | Flags: uint32(s.Flags), 231 | Addr: uint32(s.Addr), 232 | Off: uint32(s.Offset), 233 | Size: uint32(s.Size), 234 | Link: s.Link, 235 | Info: s.Info, 236 | Addralign: uint32(s.Addralign), 237 | Entsize: uint32(s.Entsize)}) 238 | case ELFCLASS64: 239 | binary.Write(w, elfFile.ByteOrder, &Section64{ 240 | Name: s.Shname, 241 | Type: uint32(s.Type), 242 | Flags: uint64(s.Flags), 243 | Addr: s.Addr, 244 | Off: s.Offset, 245 | Size: s.Size, 246 | Link: s.Link, 247 | Info: s.Info, 248 | Addralign: s.Addralign, 249 | Entsize: s.Entsize}) 250 | } 251 | } 252 | 253 | // Do I have a PT_NOTE segment to add at the end? 254 | 255 | if len(elfFile.InsertionEOF) > 0 { 256 | binary.Write(w, elfFile.ByteOrder, elfFile.InsertionEOF) 257 | bytesWritten += uint64(len(elfFile.InsertionEOF)) 258 | } 259 | 260 | w.Flush() 261 | return elfBuf.Bytes(), nil 262 | } 263 | 264 | // WriteFile - Creates a new file and writes it using the Bytes func above 265 | func (elfFile *File) WriteFile(destFile string) error { 266 | f, err := os.Create(destFile) 267 | if err != nil { 268 | return err 269 | } 270 | defer f.Close() 271 | elfData, err := elfFile.Bytes() 272 | if err != nil { 273 | return err 274 | } 275 | _, err = f.Write(elfData) 276 | if err != nil { 277 | return err 278 | } 279 | 280 | return nil 281 | } 282 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Binject/debug 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /goobj2/file_test.go: -------------------------------------------------------------------------------- 1 | package goobj2 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | func getNewObjPath(objPath string) string { 14 | return filepath.Join(filepath.Dir(objPath), "new_"+filepath.Base(objPath)) 15 | } 16 | 17 | type test struct { 18 | name string 19 | path string 20 | pkg string 21 | obj bool 22 | } 23 | 24 | func TestWrite(t *testing.T) { 25 | var tests []test 26 | 27 | filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error { 28 | if err != nil { 29 | t.Fatalf("failed to walk testdata dir: %v", err) 30 | } 31 | 32 | if info.IsDir() { 33 | return nil 34 | } 35 | 36 | tests = append(tests, test{info.Name(), path, "", false}) 37 | 38 | return nil 39 | }) 40 | 41 | tempDir := t.TempDir() 42 | for _, tt := range tests { 43 | t.Run(tt.name, func(t *testing.T) { 44 | basename := strings.TrimSuffix(tt.name, filepath.Ext(tt.name)) 45 | var objPath string 46 | if tt.obj { 47 | objPath = tt.path 48 | } else { 49 | objPath = filepath.Join(tempDir, basename+".o") 50 | cmd := exec.Command("go", "tool", "compile", "-o", objPath, tt.path) 51 | if err := cmd.Run(); err != nil { 52 | t.Fatalf("failed to compile: %v", err) 53 | } 54 | } 55 | 56 | // parse obj file 57 | pkg, err := Parse(objPath, tt.pkg) 58 | if err != nil { 59 | t.Fatalf("failed to parse object file: %v", err) 60 | } 61 | //ioutil.WriteFile(objPath+"_parsed", []byte(pretty.Sprint(pkg)), 0777) 62 | 63 | // write obj file 64 | newObjPath := getNewObjPath(objPath) 65 | WriteObjFile2(pkg, newObjPath) 66 | 67 | // compare bytes of the original and written object files 68 | objBytes, err := ioutil.ReadFile(objPath) 69 | if err != nil { 70 | t.Fatalf("failed to read object file: %v", err) 71 | } 72 | newObjBytes, err := ioutil.ReadFile(newObjPath) 73 | if err != nil { 74 | t.Fatalf("failed to read new object file: %v", err) 75 | } 76 | 77 | if !bytes.Equal(objBytes, newObjBytes) { 78 | t.Error("object files are not the same") 79 | } 80 | 81 | // compare parsed packages of the two object files 82 | _, err = Parse(newObjPath, tt.pkg) 83 | if err != nil { 84 | t.Fatalf("failed to parse new object file: %v", err) 85 | } 86 | 87 | /*if !reflect.DeepEqual(pkg, pkg2) { 88 | t.Errorf("Packages are not equal:\n%v", strings.Join(pretty.Diff(pkg, pkg2), "\n")) 89 | }*/ 90 | }) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /goobj2/internal/bio/buf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package bio implements common I/O abstractions used within the Go toolchain. 6 | package bio 7 | 8 | import ( 9 | "bufio" 10 | "io" 11 | "log" 12 | "os" 13 | ) 14 | 15 | // Reader implements a seekable buffered io.Reader. 16 | type Reader struct { 17 | f *os.File 18 | *bufio.Reader 19 | } 20 | 21 | // Writer implements a seekable buffered io.Writer. 22 | type Writer struct { 23 | f *os.File 24 | *bufio.Writer 25 | } 26 | 27 | // Create creates the file named name and returns a Writer 28 | // for that file. 29 | func Create(name string) (*Writer, error) { 30 | f, err := os.Create(name) 31 | if err != nil { 32 | return nil, err 33 | } 34 | return &Writer{f: f, Writer: bufio.NewWriter(f)}, nil 35 | } 36 | 37 | // Open returns a Reader for the file named name. 38 | func Open(name string) (*Reader, error) { 39 | f, err := os.Open(name) 40 | if err != nil { 41 | return nil, err 42 | } 43 | return &Reader{f: f, Reader: bufio.NewReader(f)}, nil 44 | } 45 | 46 | func (r *Reader) MustSeek(offset int64, whence int) int64 { 47 | if whence == 1 { 48 | offset -= int64(r.Buffered()) 49 | } 50 | off, err := r.f.Seek(offset, whence) 51 | if err != nil { 52 | log.Fatalf("seeking in output: %v", err) 53 | } 54 | r.Reset(r.f) 55 | return off 56 | } 57 | 58 | func (w *Writer) MustSeek(offset int64, whence int) int64 { 59 | if err := w.Flush(); err != nil { 60 | log.Fatalf("writing output: %v", err) 61 | } 62 | off, err := w.f.Seek(offset, whence) 63 | if err != nil { 64 | log.Fatalf("seeking in output: %v", err) 65 | } 66 | return off 67 | } 68 | 69 | func (r *Reader) Offset() int64 { 70 | off, err := r.f.Seek(0, 1) 71 | if err != nil { 72 | log.Fatalf("seeking in output [0, 1]: %v", err) 73 | } 74 | off -= int64(r.Buffered()) 75 | return off 76 | } 77 | 78 | func (w *Writer) Offset() int64 { 79 | if err := w.Flush(); err != nil { 80 | log.Fatalf("writing output: %v", err) 81 | } 82 | off, err := w.f.Seek(0, 1) 83 | if err != nil { 84 | log.Fatalf("seeking in output [0, 1]: %v", err) 85 | } 86 | return off 87 | } 88 | 89 | func (r *Reader) Close() error { 90 | return r.f.Close() 91 | } 92 | 93 | func (w *Writer) Close() error { 94 | err := w.Flush() 95 | err1 := w.f.Close() 96 | if err == nil { 97 | err = err1 98 | } 99 | return err 100 | } 101 | 102 | func (r *Reader) File() *os.File { 103 | return r.f 104 | } 105 | 106 | func (w *Writer) File() *os.File { 107 | return w.f 108 | } 109 | 110 | // Slice reads the next length bytes of r into a slice. 111 | // 112 | // This slice may be backed by mmap'ed memory. Currently, this memory 113 | // will never be unmapped. The second result reports whether the 114 | // backing memory is read-only. 115 | func (r *Reader) Slice(length uint64) ([]byte, bool, error) { 116 | if length == 0 { 117 | return []byte{}, false, nil 118 | } 119 | 120 | data, ok := r.sliceOS(length) 121 | if ok { 122 | return data, true, nil 123 | } 124 | 125 | data = make([]byte, length) 126 | _, err := io.ReadFull(r, data) 127 | if err != nil { 128 | return nil, false, err 129 | } 130 | return data, false, nil 131 | } 132 | 133 | // SliceRO returns a slice containing the next length bytes of r 134 | // backed by a read-only mmap'd data. If the mmap cannot be 135 | // established (limit exceeded, region too small, etc) a nil slice 136 | // will be returned. If mmap succeeds, it will never be unmapped. 137 | func (r *Reader) SliceRO(length uint64) []byte { 138 | data, ok := r.sliceOS(length) 139 | if ok { 140 | return data 141 | } 142 | return nil 143 | } 144 | -------------------------------------------------------------------------------- /goobj2/internal/bio/buf_mmap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin dragonfly freebsd linux netbsd openbsd 6 | 7 | package bio 8 | 9 | import ( 10 | "runtime" 11 | "sync/atomic" 12 | "syscall" 13 | ) 14 | 15 | // mmapLimit is the maximum number of mmaped regions to create before 16 | // falling back to reading into a heap-allocated slice. This exists 17 | // because some operating systems place a limit on the number of 18 | // distinct mapped regions per process. As of this writing: 19 | // 20 | // Darwin unlimited 21 | // DragonFly 1000000 (vm.max_proc_mmap) 22 | // FreeBSD unlimited 23 | // Linux 65530 (vm.max_map_count) // TODO: query /proc/sys/vm/max_map_count? 24 | // NetBSD unlimited 25 | // OpenBSD unlimited 26 | var mmapLimit int32 = 1<<31 - 1 27 | 28 | func init() { 29 | // Linux is the only practically concerning OS. 30 | if runtime.GOOS == "linux" { 31 | mmapLimit = 30000 32 | } 33 | } 34 | 35 | func (r *Reader) sliceOS(length uint64) ([]byte, bool) { 36 | // For small slices, don't bother with the overhead of a 37 | // mapping, especially since we have no way to unmap it. 38 | const threshold = 16 << 10 39 | if length < threshold { 40 | return nil, false 41 | } 42 | 43 | // Have we reached the mmap limit? 44 | if atomic.AddInt32(&mmapLimit, -1) < 0 { 45 | atomic.AddInt32(&mmapLimit, 1) 46 | return nil, false 47 | } 48 | 49 | // Page-align the offset. 50 | off := r.Offset() 51 | align := syscall.Getpagesize() 52 | aoff := off &^ int64(align-1) 53 | 54 | data, err := syscall.Mmap(int(r.f.Fd()), aoff, int(length+uint64(off-aoff)), syscall.PROT_READ, syscall.MAP_SHARED|syscall.MAP_FILE) 55 | if err != nil { 56 | return nil, false 57 | } 58 | 59 | data = data[off-aoff:] 60 | r.MustSeek(int64(length), 1) 61 | return data, true 62 | } 63 | -------------------------------------------------------------------------------- /goobj2/internal/bio/buf_nommap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd 6 | 7 | package bio 8 | 9 | func (r *Reader) sliceOS(length uint64) ([]byte, bool) { 10 | return nil, false 11 | } 12 | -------------------------------------------------------------------------------- /goobj2/internal/bio/must.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package bio 6 | 7 | import ( 8 | "io" 9 | "log" 10 | ) 11 | 12 | // MustClose closes Closer c and calls log.Fatal if it returns a non-nil error. 13 | func MustClose(c io.Closer) { 14 | if err := c.Close(); err != nil { 15 | log.Fatal(err) 16 | } 17 | } 18 | 19 | // MustWriter returns a Writer that wraps the provided Writer, 20 | // except that it calls log.Fatal instead of returning a non-nil error. 21 | func MustWriter(w io.Writer) io.Writer { 22 | return mustWriter{w} 23 | } 24 | 25 | type mustWriter struct { 26 | w io.Writer 27 | } 28 | 29 | func (w mustWriter) Write(b []byte) (int, error) { 30 | n, err := w.w.Write(b) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | return n, nil 35 | } 36 | 37 | func (w mustWriter) WriteString(s string) (int, error) { 38 | n, err := io.WriteString(w.w, s) 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | return n, nil 43 | } 44 | -------------------------------------------------------------------------------- /goobj2/internal/goobj2/extras.go: -------------------------------------------------------------------------------- 1 | package goobj2 2 | 3 | func (r Reader) Header() Header { 4 | return r.h 5 | } 6 | -------------------------------------------------------------------------------- /goobj2/internal/unsafeheader/unsafeheader.go: -------------------------------------------------------------------------------- 1 | // Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT. 2 | //go:generate bundle -o unsafeheader.go -prefix= internal/unsafeheader 3 | 4 | // Package unsafeheader contains header declarations for the Go runtime's slice 5 | // and string implementations. 6 | // 7 | // This package allows packages that cannot import "reflect" to use types that 8 | // are tested to be equivalent to reflect.SliceHeader and reflect.StringHeader. 9 | // 10 | 11 | package unsafeheader 12 | 13 | import ( 14 | "unsafe" 15 | ) 16 | 17 | // Slice is the runtime representation of a slice. 18 | // It cannot be used safely or portably and its representation may 19 | // change in a later release. 20 | // 21 | // Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the 22 | // data it references will not be garbage collected. 23 | type Slice struct { 24 | Data unsafe.Pointer 25 | Len int 26 | Cap int 27 | } 28 | 29 | // String is the runtime representation of a string. 30 | // It cannot be used safely or portably and its representation may 31 | // change in a later release. 32 | // 33 | // Unlike reflect.StringHeader, its Data field is sufficient to guarantee the 34 | // data it references will not be garbage collected. 35 | type String struct { 36 | Data unsafe.Pointer 37 | Len int 38 | } 39 | -------------------------------------------------------------------------------- /goobj2/testdata/aes_encrypt.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "crypto/rand" 7 | "encoding/hex" 8 | "fmt" 9 | "io" 10 | ) 11 | 12 | func main() { 13 | // Load your secret key from a safe place and reuse it across multiple 14 | // Seal/Open calls. (Obviously don't use this example key for anything 15 | // real.) If you want to convert a passphrase to a key, use a suitable 16 | // package like bcrypt or scrypt. 17 | // When decoded the key should be 16 bytes (AES-128) or 32 (AES-256). 18 | key, _ := hex.DecodeString("6368616e676520746869732070617373776f726420746f206120736563726574") 19 | plaintext := []byte("exampleplaintext") 20 | 21 | block, err := aes.NewCipher(key) 22 | if err != nil { 23 | panic(err.Error()) 24 | } 25 | 26 | // Never use more than 2^32 random nonces with a given key because of the risk of a repeat. 27 | nonce := make([]byte, 12) 28 | if _, err := io.ReadFull(rand.Reader, nonce); err != nil { 29 | panic(err.Error()) 30 | } 31 | 32 | aesgcm, err := cipher.NewGCM(block) 33 | if err != nil { 34 | panic(err.Error()) 35 | } 36 | 37 | ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil) 38 | fmt.Printf("%x\n", ciphertext) 39 | } 40 | -------------------------------------------------------------------------------- /goobj2/testdata/format.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/format" 7 | "go/parser" 8 | "go/token" 9 | "log" 10 | ) 11 | 12 | func main() { 13 | const expr = "(6+2*3)/4" 14 | 15 | // parser.ParseExpr parses the argument and returns the 16 | // corresponding ast.Node. 17 | node, err := parser.ParseExpr(expr) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | // Create a FileSet for node. Since the node does not come 23 | // from a real source file, fset will be empty. 24 | fset := token.NewFileSet() 25 | 26 | var buf bytes.Buffer 27 | err = format.Node(&buf, fset, node) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | fmt.Println(buf.String()) 33 | } 34 | -------------------------------------------------------------------------------- /goobj2/testdata/hello_world.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("Hello World!") 7 | } 8 | -------------------------------------------------------------------------------- /goobj2/testdata/http_not_found_handler.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | func newPeopleHandler() http.Handler { 10 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 11 | fmt.Fprintln(w, "This is the people handler.") 12 | }) 13 | } 14 | 15 | func main() { 16 | mux := http.NewServeMux() 17 | 18 | // Create sample handler to returns 404 19 | mux.Handle("/resources", http.NotFoundHandler()) 20 | 21 | // Create sample handler that returns 200 22 | mux.Handle("/resources/people/", newPeopleHandler()) 23 | 24 | log.Fatal(http.ListenAndServe(":8080", mux)) 25 | } 26 | -------------------------------------------------------------------------------- /goobj2/testdata/loop_de_loop.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("I like fruit loops") 7 | 8 | j := 0 9 | for i := 0; i < 10; i++ { 10 | fmt.Println(i, j-i) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /goobj2/testdata/reflect.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "reflect" 8 | ) 9 | 10 | func main() { 11 | typ := reflect.StructOf([]reflect.StructField{ 12 | { 13 | Name: "Height", 14 | Type: reflect.TypeOf(float64(0)), 15 | Tag: `json:"height"`, 16 | }, 17 | { 18 | Name: "Age", 19 | Type: reflect.TypeOf(int(0)), 20 | Tag: `json:"age"`, 21 | }, 22 | }) 23 | 24 | v := reflect.New(typ).Elem() 25 | v.Field(0).SetFloat(0.4) 26 | v.Field(1).SetInt(2) 27 | s := v.Addr().Interface() 28 | 29 | w := new(bytes.Buffer) 30 | if err := json.NewEncoder(w).Encode(s); err != nil { 31 | panic(err) 32 | } 33 | 34 | fmt.Printf("value: %+v\n", s) 35 | fmt.Printf("json: %s", w.Bytes()) 36 | 37 | r := bytes.NewReader([]byte(`{"height":1.5,"age":10}`)) 38 | if err := json.NewDecoder(r).Decode(s); err != nil { 39 | panic(err) 40 | } 41 | fmt.Printf("value: %+v\n", s) 42 | 43 | } 44 | -------------------------------------------------------------------------------- /goobj2/testdata/trim_string.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func main() { 9 | s := "prefix this should be all you see" 10 | trimmed := strings.TrimPrefix(s, "prefix") 11 | if trimmed != "this should be all you see" { 12 | fmt.Println("oh noes") 13 | } 14 | 15 | fmt.Println(trimmed) 16 | } 17 | -------------------------------------------------------------------------------- /goobj2/write.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Writing Go object files. 6 | 7 | // This file is a modified version of cmd/internal/obj/objfile2.go 8 | 9 | package goobj2 10 | 11 | import ( 12 | "bytes" 13 | "fmt" 14 | "path/filepath" 15 | "strings" 16 | 17 | "github.com/Binject/debug/goobj2/internal/bio" 18 | "github.com/Binject/debug/goobj2/internal/goobj2" 19 | ) 20 | 21 | // Write writes the contents of the parsed archive to disk. 22 | func (pkg *Package) Write(path string) (err error) { 23 | b, err := bio.Create(path) 24 | if err != nil { 25 | return fmt.Errorf("error creating object file: %v", err) 26 | } 27 | defer func() { 28 | closeErr := b.Close() 29 | if closeErr != nil && err == nil { 30 | err = closeErr 31 | } 32 | }() 33 | 34 | // Archive headers 35 | b.Write(archiveHeader) 36 | var arhdr [archiveHeaderLen]byte 37 | var curArHdrOff, curObjStartOff int64 38 | for i := range pkg.ArchiveMembers { 39 | ctxt := &pkg.ArchiveMembers[i] 40 | ar := ctxt.ArchiveHeader 41 | curArHdrOff = b.Offset() 42 | 43 | copy(arhdr[:], fmt.Sprintf("%-16s%-12s%-6s%-6s%-8s%-10d`\n", ar.Name, ar.Date, ar.UID, ar.GID, ar.Mode, ar.Size)) 44 | b.Write(arhdr[:]) 45 | curObjStartOff = b.Offset() 46 | b.Write(ar.Data) 47 | if ctxt.IsDataObj { 48 | continue 49 | } 50 | 51 | genFuncInfoSyms(ctxt) 52 | 53 | w := writer{ 54 | Writer: goobj2.NewWriter(b), 55 | ctxt: ctxt, 56 | } 57 | 58 | start := b.Offset() 59 | 60 | // Header 61 | // We just reserve the space. We'll fill in the offsets later. 62 | ctxt.ObjHeader.Write(w.Writer) 63 | 64 | // String table 65 | w.StringTable() 66 | 67 | // Autolib 68 | ctxt.ObjHeader.Offsets[goobj2.BlkAutolib] = w.Offset() 69 | for i := range ctxt.Imports { 70 | ctxt.Imports[i].Write(w.Writer) 71 | } 72 | 73 | // Package references 74 | ctxt.ObjHeader.Offsets[goobj2.BlkPkgIdx] = w.Offset() 75 | w.StringRef("") 76 | for _, pkg := range ctxt.Packages { 77 | w.StringRef(pkg) 78 | } 79 | 80 | // DWARF file table 81 | ctxt.ObjHeader.Offsets[goobj2.BlkDwarfFile] = w.Offset() 82 | for _, f := range ctxt.DWARFFileList { 83 | w.StringRef(filepath.ToSlash(f)) 84 | } 85 | 86 | // Symbol definitions 87 | ctxt.ObjHeader.Offsets[goobj2.BlkSymdef] = w.Offset() 88 | for _, s := range ctxt.SymDefs { 89 | w.Sym(s) 90 | } 91 | 92 | // Non-pkg symbol definitions 93 | ctxt.ObjHeader.Offsets[goobj2.BlkNonpkgdef] = w.Offset() 94 | for _, s := range ctxt.NonPkgSymDefs { 95 | w.Sym(s) 96 | } 97 | 98 | // Non-pkg symbol references 99 | ctxt.ObjHeader.Offsets[goobj2.BlkNonpkgref] = w.Offset() 100 | for _, s := range ctxt.NonPkgSymRefs { 101 | w.Sym(s) 102 | } 103 | 104 | // Reloc indexes 105 | ctxt.ObjHeader.Offsets[goobj2.BlkRelocIdx] = w.Offset() 106 | nreloc := uint32(0) 107 | lists := [][]*Sym{ctxt.SymDefs, ctxt.NonPkgSymDefs} 108 | for _, list := range lists { 109 | for _, s := range list { 110 | w.Uint32(nreloc) 111 | nreloc += uint32(len(s.Reloc)) 112 | } 113 | } 114 | w.Uint32(nreloc) 115 | 116 | // Symbol Info indexes 117 | ctxt.ObjHeader.Offsets[goobj2.BlkAuxIdx] = w.Offset() 118 | naux := uint32(0) 119 | for _, list := range lists { 120 | for _, s := range list { 121 | w.Uint32(naux) 122 | naux += uint32(nAuxSym(s)) 123 | } 124 | } 125 | w.Uint32(naux) 126 | 127 | // Data indexes 128 | ctxt.ObjHeader.Offsets[goobj2.BlkDataIdx] = w.Offset() 129 | dataOff := uint32(0) 130 | for _, list := range lists { 131 | for _, s := range list { 132 | w.Uint32(dataOff) 133 | dataOff += uint32(len(s.Data)) 134 | } 135 | } 136 | w.Uint32(dataOff) 137 | 138 | // Relocs 139 | ctxt.ObjHeader.Offsets[goobj2.BlkReloc] = w.Offset() 140 | for _, list := range lists { 141 | for _, s := range list { 142 | for i := range s.Reloc { 143 | w.Reloc(&s.Reloc[i]) 144 | } 145 | } 146 | } 147 | 148 | // Aux symbol info 149 | ctxt.ObjHeader.Offsets[goobj2.BlkAux] = w.Offset() 150 | for _, list := range lists { 151 | for _, s := range list { 152 | w.Aux(s) 153 | } 154 | } 155 | 156 | // Data 157 | ctxt.ObjHeader.Offsets[goobj2.BlkData] = w.Offset() 158 | for _, list := range lists { 159 | for _, s := range list { 160 | w.Bytes(s.Data) 161 | } 162 | } 163 | 164 | // Pcdata 165 | ctxt.ObjHeader.Offsets[goobj2.BlkPcdata] = w.Offset() 166 | for _, ts := range ctxt.textSyms { 167 | w.Bytes(ts.Func.PCSP) 168 | w.Bytes(ts.Func.PCFile) 169 | w.Bytes(ts.Func.PCLine) 170 | w.Bytes(ts.Func.PCInline) 171 | for i := range ts.Func.PCData { 172 | w.Bytes(ts.Func.PCData[i]) 173 | } 174 | } 175 | 176 | // Referenced symbol names from other packages 177 | ctxt.ObjHeader.Offsets[goobj2.BlkRefName] = w.Offset() 178 | for _, ref := range ctxt.SymRefs { 179 | var o goobj2.RefName 180 | o.SetSym(ref.SymRef) 181 | o.SetName(ref.Name, w.Writer) 182 | o.Write(w.Writer) 183 | } 184 | 185 | objEnd := w.Offset() 186 | ctxt.ObjHeader.Offsets[goobj2.BlkEnd] = objEnd 187 | 188 | // If the object size is odd, make it even by adding an 189 | // extra null byte as padding 190 | size := int64(objEnd) + (start - curObjStartOff) 191 | end := start + int64(w.Offset()) 192 | if size%2 != 0 { 193 | b.WriteByte(0x00) 194 | end++ 195 | } 196 | 197 | // Fix size field of the last archive header 198 | b.MustSeek(curArHdrOff+48, 0) 199 | b.WriteString(fmt.Sprintf("%-10d", size)) 200 | 201 | // Fix up block offsets in the object header 202 | b.MustSeek(start, 0) 203 | ctxt.ObjHeader.Write(w.Writer) 204 | b.MustSeek(end, 0) 205 | } 206 | 207 | return nil 208 | } 209 | 210 | type writer struct { 211 | *goobj2.Writer 212 | ctxt *ArchiveMember 213 | } 214 | 215 | func (w *writer) StringTable() { 216 | w.AddString("") 217 | for _, p := range w.ctxt.Imports { 218 | w.AddString(p.Pkg) 219 | } 220 | for _, pkg := range w.ctxt.Packages { 221 | w.AddString(pkg) 222 | } 223 | 224 | writeSymStrings := func(s *Sym) { 225 | w.AddString(s.Name) 226 | 227 | for _, r := range s.Reloc { 228 | w.AddString(r.Name) 229 | } 230 | if s.Type != nil { 231 | w.AddString(s.Name) 232 | } 233 | 234 | if s.Kind == STEXT && s.Func != nil { 235 | for _, d := range s.Func.FuncData { 236 | w.AddString(d.Sym.Name) 237 | } 238 | for _, f := range s.Func.File { 239 | w.AddString(filepath.ToSlash(f.Name)) 240 | } 241 | for _, call := range s.Func.InlTree { 242 | w.AddString(call.File.Name) 243 | w.AddString(call.Func.Name) 244 | } 245 | 246 | dwsyms := []*SymRef{s.Func.DwarfRanges, s.Func.DwarfLoc, s.Func.DwarfDebugLines, s.Func.FuncInfo} 247 | for _, dws := range dwsyms { 248 | if dws != nil { 249 | w.AddString(dws.Name) 250 | } 251 | } 252 | } 253 | } 254 | 255 | // Symbols of type STEXT (that have functions) are written first 256 | for _, ts := range w.ctxt.textSyms { 257 | writeSymStrings(ts) 258 | } 259 | 260 | syms := [][]*Sym{w.ctxt.NonPkgSymDefs, w.ctxt.SymDefs, w.ctxt.NonPkgSymRefs} 261 | for _, list := range syms { 262 | for _, s := range list { 263 | if s.Kind == STEXT { 264 | continue 265 | } 266 | 267 | writeSymStrings(s) 268 | } 269 | } 270 | for _, r := range w.ctxt.SymRefs { 271 | w.AddString(r.Name) 272 | } 273 | 274 | for _, f := range w.ctxt.DWARFFileList { 275 | w.AddString(filepath.ToSlash(f)) 276 | } 277 | } 278 | 279 | func (w *writer) Sym(s *Sym) { 280 | name := s.Name 281 | if strings.HasPrefix(name, "gofile..") { 282 | name = filepath.ToSlash(name) 283 | } 284 | 285 | var o goobj2.Sym 286 | o.SetName(name, w.Writer) 287 | o.SetABI(s.ABI) 288 | o.SetType(uint8(s.Kind)) 289 | o.SetFlag(s.Flag) 290 | o.SetSiz(s.Size) 291 | o.SetAlign(s.Align) 292 | o.Write(w.Writer) 293 | } 294 | 295 | func (w *writer) Reloc(r *Reloc) { 296 | var o goobj2.Reloc 297 | o.SetOff(int32(r.Offset)) 298 | o.SetSiz(uint8(r.Size)) 299 | o.SetType(uint8(r.Type)) 300 | o.SetAdd(r.Add) 301 | o.SetSym(r.Sym) 302 | o.Write(w.Writer) 303 | } 304 | 305 | func (w *writer) aux1(typ uint8, rs goobj2.SymRef) { 306 | var o goobj2.Aux 307 | o.SetType(typ) 308 | o.SetSym(rs) 309 | o.Write(w.Writer) 310 | } 311 | 312 | func (w *writer) Aux(s *Sym) { 313 | if s.Type != nil { 314 | w.aux1(goobj2.AuxGotype, s.Type.SymRef) 315 | } 316 | if s.Func != nil { 317 | w.aux1(goobj2.AuxFuncInfo, s.Func.FuncInfo.SymRef) 318 | 319 | for _, d := range s.Func.FuncData { 320 | w.aux1(goobj2.AuxFuncdata, d.Sym.SymRef) 321 | } 322 | 323 | if s.Func.DwarfInfo != nil { 324 | w.aux1(goobj2.AuxDwarfInfo, s.Func.DwarfInfo.SymRef) 325 | } 326 | if s.Func.DwarfLoc != nil { 327 | w.aux1(goobj2.AuxDwarfLoc, s.Func.DwarfLoc.SymRef) 328 | } 329 | if s.Func.DwarfRanges != nil { 330 | w.aux1(goobj2.AuxDwarfRanges, s.Func.DwarfRanges.SymRef) 331 | } 332 | if s.Func.DwarfDebugLines != nil { 333 | w.aux1(goobj2.AuxDwarfLines, s.Func.DwarfDebugLines.SymRef) 334 | } 335 | } 336 | } 337 | 338 | // return the number of aux symbols s have. 339 | func nAuxSym(s *Sym) int { 340 | n := 0 341 | if s.Type != nil { 342 | n++ 343 | } 344 | if s.Func != nil { 345 | // FuncInfo is an aux symbol, each Funcdata is an aux symbol 346 | n += 1 + len(s.Func.FuncData) 347 | if s.Func.DwarfInfo != nil { 348 | n++ 349 | } 350 | if s.Func.DwarfLoc != nil { 351 | n++ 352 | } 353 | if s.Func.DwarfRanges != nil { 354 | n++ 355 | } 356 | if s.Func.DwarfDebugLines != nil { 357 | n++ 358 | } 359 | } 360 | return n 361 | } 362 | 363 | // generate symbols for FuncInfo. 364 | func genFuncInfoSyms(ctxt *ArchiveMember) { 365 | var pcdataoff uint32 366 | var b bytes.Buffer 367 | for _, s := range ctxt.textSyms { 368 | if s.Func == nil { 369 | continue 370 | } 371 | 372 | o := goobj2.FuncInfo{ 373 | Args: uint32(s.Func.Args), 374 | Locals: uint32(s.Func.Frame), 375 | } 376 | o.Pcsp = pcdataoff 377 | pcdataoff += uint32(len(s.Func.PCSP)) 378 | o.Pcfile = pcdataoff 379 | pcdataoff += uint32(len(s.Func.PCFile)) 380 | o.Pcline = pcdataoff 381 | pcdataoff += uint32(len(s.Func.PCLine)) 382 | o.Pcinline = pcdataoff 383 | pcdataoff += uint32(len(s.Func.PCInline)) 384 | o.Pcdata = make([]uint32, len(s.Func.PCData)) 385 | for i, pcd := range s.Func.PCData { 386 | o.Pcdata[i] = pcdataoff 387 | pcdataoff += uint32(len(pcd)) 388 | } 389 | o.PcdataEnd = pcdataoff 390 | o.Funcdataoff = make([]uint32, len(s.Func.FuncData)) 391 | for i, x := range s.Func.FuncData { 392 | o.Funcdataoff[i] = x.Offset 393 | } 394 | o.File = make([]goobj2.SymRef, len(s.Func.File)) 395 | for i, f := range s.Func.File { 396 | o.File[i] = f.SymRef 397 | } 398 | o.InlTree = make([]goobj2.InlTreeNode, len(s.Func.InlTree)) 399 | for i, inl := range s.Func.InlTree { 400 | o.InlTree[i] = goobj2.InlTreeNode{ 401 | Parent: inl.Parent, 402 | File: inl.File.SymRef, 403 | Line: inl.Line, 404 | Func: inl.Func.SymRef, 405 | ParentPC: inl.ParentPC, 406 | } 407 | } 408 | 409 | o.Write(&b) 410 | ctxt.symMap[s.Func.dataSymIdx].Data = append([]byte(nil), b.Bytes()...) 411 | b.Reset() 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /gosym/pclntab_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gosym 6 | 7 | import ( 8 | "debug/elf" 9 | "internal/testenv" 10 | "io/ioutil" 11 | "os" 12 | "os/exec" 13 | "path/filepath" 14 | "runtime" 15 | "strings" 16 | "testing" 17 | ) 18 | 19 | var ( 20 | pclineTempDir string 21 | pclinetestBinary string 22 | ) 23 | 24 | func dotest(t *testing.T) { 25 | testenv.MustHaveGoBuild(t) 26 | // For now, only works on amd64 platforms. 27 | if runtime.GOARCH != "amd64" { 28 | t.Skipf("skipping on non-AMD64 system %s", runtime.GOARCH) 29 | } 30 | var err error 31 | pclineTempDir, err = ioutil.TempDir("", "pclinetest") 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest") 36 | cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", pclinetestBinary) 37 | cmd.Dir = "testdata" 38 | cmd.Env = append(os.Environ(), "GOOS=linux") 39 | cmd.Stdout = os.Stdout 40 | cmd.Stderr = os.Stderr 41 | if err := cmd.Run(); err != nil { 42 | t.Fatal(err) 43 | } 44 | } 45 | 46 | func endtest() { 47 | if pclineTempDir != "" { 48 | os.RemoveAll(pclineTempDir) 49 | pclineTempDir = "" 50 | pclinetestBinary = "" 51 | } 52 | } 53 | 54 | // skipIfNotELF skips the test if we are not running on an ELF system. 55 | // These tests open and examine the test binary, and use elf.Open to do so. 56 | func skipIfNotELF(t *testing.T) { 57 | switch runtime.GOOS { 58 | case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris": 59 | // OK. 60 | default: 61 | t.Skipf("skipping on non-ELF system %s", runtime.GOOS) 62 | } 63 | } 64 | 65 | func getTable(t *testing.T) *Table { 66 | f, tab := crack(os.Args[0], t) 67 | f.Close() 68 | return tab 69 | } 70 | 71 | func crack(file string, t *testing.T) (*elf.File, *Table) { 72 | // Open self 73 | f, err := elf.Open(file) 74 | if err != nil { 75 | t.Fatal(err) 76 | } 77 | return parse(file, f, t) 78 | } 79 | 80 | func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) { 81 | s := f.Section(".gosymtab") 82 | if s == nil { 83 | t.Skip("no .gosymtab section") 84 | } 85 | symdat, err := s.Data() 86 | if err != nil { 87 | f.Close() 88 | t.Fatalf("reading %s gosymtab: %v", file, err) 89 | } 90 | pclndat, err := f.Section(".gopclntab").Data() 91 | if err != nil { 92 | f.Close() 93 | t.Fatalf("reading %s gopclntab: %v", file, err) 94 | } 95 | 96 | pcln := NewLineTable(pclndat, f.Section(".text").Addr) 97 | tab, err := NewTable(symdat, pcln) 98 | if err != nil { 99 | f.Close() 100 | t.Fatalf("parsing %s gosymtab: %v", file, err) 101 | } 102 | 103 | return f, tab 104 | } 105 | 106 | func TestLineFromAline(t *testing.T) { 107 | skipIfNotELF(t) 108 | 109 | tab := getTable(t) 110 | if tab.go12line != nil { 111 | // aline's don't exist in the Go 1.2 table. 112 | t.Skip("not relevant to Go 1.2 symbol table") 113 | } 114 | 115 | // Find the sym package 116 | pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj 117 | if pkg == nil { 118 | t.Fatalf("nil pkg") 119 | } 120 | 121 | // Walk every absolute line and ensure that we hit every 122 | // source line monotonically 123 | lastline := make(map[string]int) 124 | final := -1 125 | for i := 0; i < 10000; i++ { 126 | path, line := pkg.lineFromAline(i) 127 | // Check for end of object 128 | if path == "" { 129 | if final == -1 { 130 | final = i - 1 131 | } 132 | continue 133 | } else if final != -1 { 134 | t.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final, i, path, line) 135 | } 136 | // It's okay to see files multiple times (e.g., sys.a) 137 | if line == 1 { 138 | lastline[path] = 1 139 | continue 140 | } 141 | // Check that the is the next line in path 142 | ll, ok := lastline[path] 143 | if !ok { 144 | t.Errorf("file %s starts on line %d", path, line) 145 | } else if line != ll+1 { 146 | t.Fatalf("expected next line of file %s to be %d, got %d", path, ll+1, line) 147 | } 148 | lastline[path] = line 149 | } 150 | if final == -1 { 151 | t.Errorf("never reached end of object") 152 | } 153 | } 154 | 155 | func TestLineAline(t *testing.T) { 156 | skipIfNotELF(t) 157 | 158 | tab := getTable(t) 159 | if tab.go12line != nil { 160 | // aline's don't exist in the Go 1.2 table. 161 | t.Skip("not relevant to Go 1.2 symbol table") 162 | } 163 | 164 | for _, o := range tab.Files { 165 | // A source file can appear multiple times in a 166 | // object. alineFromLine will always return alines in 167 | // the first file, so track which lines we've seen. 168 | found := make(map[string]int) 169 | for i := 0; i < 1000; i++ { 170 | path, line := o.lineFromAline(i) 171 | if path == "" { 172 | break 173 | } 174 | 175 | // cgo files are full of 'Z' symbols, which we don't handle 176 | if len(path) > 4 && path[len(path)-4:] == ".cgo" { 177 | continue 178 | } 179 | 180 | if minline, ok := found[path]; path != "" && ok { 181 | if minline >= line { 182 | // We've already covered this file 183 | continue 184 | } 185 | } 186 | found[path] = line 187 | 188 | a, err := o.alineFromLine(path, line) 189 | if err != nil { 190 | 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) 191 | } else if a != i { 192 | 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) 193 | } 194 | } 195 | } 196 | } 197 | 198 | func TestPCLine(t *testing.T) { 199 | dotest(t) 200 | defer endtest() 201 | 202 | f, tab := crack(pclinetestBinary, t) 203 | defer f.Close() 204 | text := f.Section(".text") 205 | textdat, err := text.Data() 206 | if err != nil { 207 | t.Fatalf("reading .text: %v", err) 208 | } 209 | 210 | // Test PCToLine 211 | sym := tab.LookupFunc("main.linefrompc") 212 | wantLine := 0 213 | for pc := sym.Entry; pc < sym.End; pc++ { 214 | off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g 215 | if textdat[off] == 255 { 216 | break 217 | } 218 | wantLine += int(textdat[off]) 219 | t.Logf("off is %d %#x (max %d)", off, textdat[off], sym.End-pc) 220 | file, line, fn := tab.PCToLine(pc) 221 | if fn == nil { 222 | t.Errorf("failed to get line of PC %#x", pc) 223 | } else if !strings.HasSuffix(file, "pclinetest.s") || line != wantLine || fn != sym { 224 | t.Errorf("PCToLine(%#x) = %s:%d (%s), want %s:%d (%s)", pc, file, line, fn.Name, "pclinetest.s", wantLine, sym.Name) 225 | } 226 | } 227 | 228 | // Test LineToPC 229 | sym = tab.LookupFunc("main.pcfromline") 230 | lookupline := -1 231 | wantLine = 0 232 | off := uint64(0) // TODO(rsc): should not need off; bug in 8g 233 | for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) { 234 | file, line, fn := tab.PCToLine(pc) 235 | off = pc - text.Addr 236 | if textdat[off] == 255 { 237 | break 238 | } 239 | wantLine += int(textdat[off]) 240 | if line != wantLine { 241 | t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line) 242 | off = pc + 1 - text.Addr 243 | continue 244 | } 245 | if lookupline == -1 { 246 | lookupline = line 247 | } 248 | for ; lookupline <= line; lookupline++ { 249 | pc2, fn2, err := tab.LineToPC(file, lookupline) 250 | if lookupline != line { 251 | // Should be nothing on this line 252 | if err == nil { 253 | t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name) 254 | } 255 | } else if err != nil { 256 | t.Errorf("failed to get PC of line %d: %s", lookupline, err) 257 | } else if pc != pc2 { 258 | t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name) 259 | } 260 | } 261 | off = pc + 1 - text.Addr 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /gosym/symtab_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gosym 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | ) 11 | 12 | func assertString(t *testing.T, dsc, out, tgt string) { 13 | if out != tgt { 14 | t.Fatalf("Expected: %q Actual: %q for %s", tgt, out, dsc) 15 | } 16 | } 17 | 18 | func TestStandardLibPackage(t *testing.T) { 19 | s1 := Sym{Name: "io.(*LimitedReader).Read"} 20 | s2 := Sym{Name: "io.NewSectionReader"} 21 | assertString(t, fmt.Sprintf("package of %q", s1.Name), s1.PackageName(), "io") 22 | assertString(t, fmt.Sprintf("package of %q", s2.Name), s2.PackageName(), "io") 23 | assertString(t, fmt.Sprintf("receiver of %q", s1.Name), s1.ReceiverName(), "(*LimitedReader)") 24 | assertString(t, fmt.Sprintf("receiver of %q", s2.Name), s2.ReceiverName(), "") 25 | } 26 | 27 | func TestStandardLibPathPackage(t *testing.T) { 28 | s1 := Sym{Name: "debug/gosym.(*LineTable).PCToLine"} 29 | s2 := Sym{Name: "debug/gosym.NewTable"} 30 | assertString(t, fmt.Sprintf("package of %q", s1.Name), s1.PackageName(), "debug/gosym") 31 | assertString(t, fmt.Sprintf("package of %q", s2.Name), s2.PackageName(), "debug/gosym") 32 | assertString(t, fmt.Sprintf("receiver of %q", s1.Name), s1.ReceiverName(), "(*LineTable)") 33 | assertString(t, fmt.Sprintf("receiver of %q", s2.Name), s2.ReceiverName(), "") 34 | } 35 | 36 | func TestRemotePackage(t *testing.T) { 37 | s1 := Sym{Name: "github.com/docker/doc.ker/pkg/mflag.(*FlagSet).PrintDefaults"} 38 | s2 := Sym{Name: "github.com/docker/doc.ker/pkg/mflag.PrintDefaults"} 39 | assertString(t, fmt.Sprintf("package of %q", s1.Name), s1.PackageName(), "github.com/docker/doc.ker/pkg/mflag") 40 | assertString(t, fmt.Sprintf("package of %q", s2.Name), s2.PackageName(), "github.com/docker/doc.ker/pkg/mflag") 41 | assertString(t, fmt.Sprintf("receiver of %q", s1.Name), s1.ReceiverName(), "(*FlagSet)") 42 | assertString(t, fmt.Sprintf("receiver of %q", s2.Name), s2.ReceiverName(), "") 43 | } 44 | -------------------------------------------------------------------------------- /gosym/testdata/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func linefrompc() 4 | func pcfromline() 5 | 6 | func main() { 7 | // Prevent GC of our test symbols 8 | linefrompc() 9 | pcfromline() 10 | } 11 | -------------------------------------------------------------------------------- /gosym/testdata/pclinetest.h: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | // Empty include file to generate z symbols 4 | 5 | 6 | 7 | 8 | 9 | // EOF 10 | -------------------------------------------------------------------------------- /gosym/testdata/pclinetest.s: -------------------------------------------------------------------------------- 1 | TEXT ·linefrompc(SB),4,$0 // Each byte stores its line delta 2 | BYTE $2; 3 | BYTE $1; 4 | BYTE $1; BYTE $0; 5 | BYTE $1; BYTE $0; BYTE $0; 6 | BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; 7 | BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; 8 | 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; 9 | 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; 10 | 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; 11 | 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; 12 | 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; 13 | 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; 14 | 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; 15 | 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; 16 | BYTE $1; 17 | BYTE $1; 18 | BYTE $1; BYTE $0; 19 | BYTE $1; BYTE $0; BYTE $0; 20 | BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; 21 | BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; 22 | 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; 23 | 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; 24 | 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; 25 | #include "pclinetest.h" 26 | BYTE $2; 27 | #include "pclinetest.h" 28 | BYTE $2; 29 | BYTE $255; 30 | 31 | TEXT ·pcfromline(SB),4,$0 // Each record stores its line delta, then n, then n more bytes 32 | BYTE $32; BYTE $0; 33 | BYTE $1; BYTE $1; BYTE $0; 34 | BYTE $1; BYTE $0; 35 | 36 | BYTE $2; BYTE $4; BYTE $0; BYTE $0; BYTE $0; BYTE $0; 37 | 38 | 39 | #include "pclinetest.h" 40 | BYTE $4; BYTE $0; 41 | 42 | 43 | BYTE $3; BYTE $3; BYTE $0; BYTE $0; BYTE $0; 44 | #include "pclinetest.h" 45 | 46 | 47 | BYTE $4; BYTE $3; BYTE $0; BYTE $0; BYTE $0; 48 | BYTE $255; 49 | -------------------------------------------------------------------------------- /macho/README.md: -------------------------------------------------------------------------------- 1 | ## More Info 2 | 3 | More info on the Mach-O file structure here: [Mach-O Deep Dive](https://www.symbolcrash.com/2019/02/25/so-you-want-to-be-a-mach-o-man/) 4 | -------------------------------------------------------------------------------- /macho/exports.go: -------------------------------------------------------------------------------- 1 | package macho 2 | 3 | const N_SECT = 0x0E 4 | const N_PEXT = 0x10 5 | const N_EXT = 0x01 6 | 7 | // Export - describes a single export entry (similar to PE version for future refactoring) 8 | type Export struct { 9 | //Ordinal uint32 // no ordinals for Mach-O 10 | Name string 11 | VirtualAddress uint64 12 | } 13 | 14 | // Exports - gets exports, including private exports 15 | func (f *File) Exports() []Export { 16 | var exports []Export 17 | for _, symbol := range f.Symtab.Syms { 18 | if (symbol.Type&N_PEXT == N_PEXT || 19 | symbol.Type&N_EXT == N_EXT) && symbol.Value != 0 { 20 | var export Export 21 | export.Name = symbol.Name 22 | export.VirtualAddress = symbol.Value 23 | exports = append(exports, export) 24 | } 25 | } 26 | return exports 27 | } 28 | -------------------------------------------------------------------------------- /macho/fat.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package macho 6 | 7 | import ( 8 | "encoding/binary" 9 | "fmt" 10 | "io" 11 | "os" 12 | ) 13 | 14 | // A FatFile is a Mach-O universal binary that contains at least one architecture. 15 | type FatFile struct { 16 | Magic uint32 17 | Arches []FatArch 18 | closer io.Closer 19 | } 20 | 21 | // A FatArchHeader represents a fat header for a specific image architecture. 22 | type FatArchHeader struct { 23 | Cpu Cpu 24 | SubCpu uint32 25 | Offset uint32 26 | Size uint32 27 | Align uint32 28 | } 29 | 30 | const fatArchHeaderSize = 5 * 4 31 | 32 | // A FatArch is a Mach-O File inside a FatFile. 33 | type FatArch struct { 34 | FatArchHeader 35 | *File 36 | } 37 | 38 | // ErrNotFat is returned from NewFatFile or OpenFat when the file is not a 39 | // universal binary but may be a thin binary, based on its magic number. 40 | var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil} 41 | 42 | // NewFatFile creates a new FatFile for accessing all the Mach-O images in a 43 | // universal binary. The Mach-O binary is expected to start at position 0 in 44 | // the ReaderAt. 45 | func NewFatFile(r io.ReaderAt) (*FatFile, error) { 46 | var ff FatFile 47 | sr := io.NewSectionReader(r, 0, 1<<63-1) 48 | 49 | // Read the fat_header struct, which is always in big endian. 50 | // Start with the magic number. 51 | err := binary.Read(sr, binary.BigEndian, &ff.Magic) 52 | if err != nil { 53 | return nil, &FormatError{0, "error reading magic number", nil} 54 | } else if ff.Magic != MagicFat { 55 | // See if this is a Mach-O file via its magic number. The magic 56 | // must be converted to little endian first though. 57 | var buf [4]byte 58 | binary.BigEndian.PutUint32(buf[:], ff.Magic) 59 | leMagic := binary.LittleEndian.Uint32(buf[:]) 60 | if leMagic == Magic32 || leMagic == Magic64 { 61 | return nil, ErrNotFat 62 | } else { 63 | return nil, &FormatError{0, "invalid magic number", nil} 64 | } 65 | } 66 | offset := int64(4) 67 | 68 | // Read the number of FatArchHeaders that come after the fat_header. 69 | var narch uint32 70 | err = binary.Read(sr, binary.BigEndian, &narch) 71 | if err != nil { 72 | return nil, &FormatError{offset, "invalid fat_header", nil} 73 | } 74 | offset += 4 75 | 76 | if narch < 1 { 77 | return nil, &FormatError{offset, "file contains no images", nil} 78 | } 79 | 80 | // Combine the Cpu and SubCpu (both uint32) into a uint64 to make sure 81 | // there are not duplicate architectures. 82 | seenArches := make(map[uint64]bool, narch) 83 | // Make sure that all images are for the same MH_ type. 84 | var machoType Type 85 | 86 | // Following the fat_header comes narch fat_arch structs that index 87 | // Mach-O images further in the file. 88 | ff.Arches = make([]FatArch, narch) 89 | for i := uint32(0); i < narch; i++ { 90 | fa := &ff.Arches[i] 91 | err = binary.Read(sr, binary.BigEndian, &fa.FatArchHeader) 92 | if err != nil { 93 | return nil, &FormatError{offset, "invalid fat_arch header", nil} 94 | } 95 | offset += fatArchHeaderSize 96 | 97 | fr := io.NewSectionReader(r, int64(fa.Offset), int64(fa.Size)) 98 | fa.File, err = NewFile(fr) 99 | if err != nil { 100 | return nil, err 101 | } 102 | 103 | // Make sure the architecture for this image is not duplicate. 104 | seenArch := (uint64(fa.Cpu) << 32) | uint64(fa.SubCpu) 105 | if o, k := seenArches[seenArch]; o || k { 106 | return nil, &FormatError{offset, fmt.Sprintf("duplicate architecture cpu=%v, subcpu=%#x", fa.Cpu, fa.SubCpu), nil} 107 | } 108 | seenArches[seenArch] = true 109 | 110 | // Make sure the Mach-O type matches that of the first image. 111 | if i == 0 { 112 | machoType = fa.Type 113 | } else { 114 | if fa.Type != machoType { 115 | 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} 116 | } 117 | } 118 | } 119 | 120 | return &ff, nil 121 | } 122 | 123 | // OpenFat opens the named file using os.Open and prepares it for use as a Mach-O 124 | // universal binary. 125 | func OpenFat(name string) (*FatFile, error) { 126 | f, err := os.Open(name) 127 | if err != nil { 128 | return nil, err 129 | } 130 | ff, err := NewFatFile(f) 131 | if err != nil { 132 | f.Close() 133 | return nil, err 134 | } 135 | ff.closer = f 136 | return ff, nil 137 | } 138 | 139 | func (ff *FatFile) Close() error { 140 | var err error 141 | if ff.closer != nil { 142 | err = ff.closer.Close() 143 | ff.closer = nil 144 | } 145 | return err 146 | } 147 | -------------------------------------------------------------------------------- /macho/macho.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Mach-O header data structures 6 | // http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html 7 | 8 | package macho 9 | 10 | import "strconv" 11 | 12 | // A FileHeader represents a Mach-O file header. 13 | type FileHeader struct { 14 | Magic uint32 15 | Cpu Cpu 16 | SubCpu uint32 17 | Type Type 18 | Ncmd uint32 19 | Cmdsz uint32 20 | Flags uint32 21 | } 22 | 23 | const ( 24 | fileHeaderSize32 = 7 * 4 25 | fileHeaderSize64 = 8 * 4 26 | ) 27 | 28 | const ( 29 | Magic32 uint32 = 0xfeedface 30 | Magic64 uint32 = 0xfeedfacf 31 | MagicFat uint32 = 0xcafebabe 32 | ) 33 | 34 | // A Type is the Mach-O file type, e.g. an object file, executable, or dynamic library. 35 | type Type uint32 36 | 37 | const ( 38 | TypeObj Type = 1 39 | TypeExec Type = 2 40 | TypeDylib Type = 6 41 | TypeBundle Type = 8 42 | ) 43 | 44 | var typeStrings = []intName{ 45 | {uint32(TypeObj), "Obj"}, 46 | {uint32(TypeExec), "Exec"}, 47 | {uint32(TypeDylib), "Dylib"}, 48 | {uint32(TypeBundle), "Bundle"}, 49 | } 50 | 51 | func (t Type) String() string { return stringName(uint32(t), typeStrings, false) } 52 | func (t Type) GoString() string { return stringName(uint32(t), typeStrings, true) } 53 | 54 | // A Cpu is a Mach-O cpu type. 55 | type Cpu uint32 56 | 57 | const cpuArch64 = 0x01000000 58 | 59 | const ( 60 | Cpu386 Cpu = 7 61 | CpuAmd64 Cpu = Cpu386 | cpuArch64 62 | CpuArm Cpu = 12 63 | CpuArm64 Cpu = CpuArm | cpuArch64 64 | CpuPpc Cpu = 18 65 | CpuPpc64 Cpu = CpuPpc | cpuArch64 66 | ) 67 | 68 | var cpuStrings = []intName{ 69 | {uint32(Cpu386), "Cpu386"}, 70 | {uint32(CpuAmd64), "CpuAmd64"}, 71 | {uint32(CpuArm), "CpuArm"}, 72 | {uint32(CpuArm64), "CpuArm64"}, 73 | {uint32(CpuPpc), "CpuPpc"}, 74 | {uint32(CpuPpc64), "CpuPpc64"}, 75 | } 76 | 77 | var FinalSegEnd uint64 78 | 79 | func (i Cpu) String() string { return stringName(uint32(i), cpuStrings, false) } 80 | func (i Cpu) GoString() string { return stringName(uint32(i), cpuStrings, true) } 81 | 82 | // A LoadCmd is a Mach-O load command. 83 | type LoadCmd uint32 84 | 85 | const ( 86 | LoadCmdSegment LoadCmd = 0x1 87 | LoadCmdSymtab LoadCmd = 0x2 88 | LoadCmdThread LoadCmd = 0x4 89 | LoadCmdUnixThread LoadCmd = 0x5 // thread+stack 90 | LoadCmdDysymtab LoadCmd = 0xb 91 | LoadCmdDylib LoadCmd = 0xc // load dylib command 92 | LoadCmdDylinker LoadCmd = 0xf // id dylinker command (not load dylinker command) 93 | LoadCmdSegment64 LoadCmd = 0x19 94 | LoadCmdSignature LoadCmd = 0x1d 95 | LoadCmdFuncStarts LoadCmd = 0x26 // Function Starts 96 | LoadCmdDataInCode LoadCmd = 0x29 // Data In Code 97 | 98 | LoadReqDyld LoadCmd = 0x80000000 99 | LoadCmdMain LoadCmd = (0x28 | LoadReqDyld) // replacement for LC_UNIXTHREAD 100 | LoadCmdRpath LoadCmd = 0x8000001c 101 | LoadCmdDylinkInfo LoadCmd = 0x80000022 // Dynamic Linker Info Only 102 | ) 103 | 104 | var cmdStrings = []intName{ 105 | {uint32(LoadCmdSegment), "LoadCmdSegment"}, 106 | {uint32(LoadCmdThread), "LoadCmdThread"}, 107 | {uint32(LoadCmdUnixThread), "LoadCmdUnixThread"}, 108 | {uint32(LoadCmdDylib), "LoadCmdDylib"}, 109 | {uint32(LoadCmdSegment64), "LoadCmdSegment64"}, 110 | {uint32(LoadCmdRpath), "LoadCmdRpath"}, 111 | {uint32(LoadCmdSignature), "LoadCmdSignature"}, 112 | {uint32(LoadCmdFuncStarts), "LoadCmdFuncStarts"}, 113 | {uint32(LoadCmdDataInCode), "LoadCmdDataInCode"}, 114 | {uint32(LoadCmdDylinkInfo), "LoadCmdDylinkInfo"}, 115 | } 116 | 117 | func (i LoadCmd) String() string { return stringName(uint32(i), cmdStrings, false) } 118 | func (i LoadCmd) GoString() string { return stringName(uint32(i), cmdStrings, true) } 119 | 120 | // Prog.Flag 121 | type ProgFlag uint32 122 | 123 | const ( 124 | PF_NONE ProgFlag = 0x0 /* None */ 125 | PF_R ProgFlag = 0x1 /* Readable. */ 126 | PF_W ProgFlag = 0x2 /* Writable. */ 127 | PF_X ProgFlag = 0x4 /* Executable. */ 128 | ) 129 | 130 | type ( 131 | // A Segment32 is a 32-bit Mach-O segment load command. 132 | Segment32 struct { 133 | Cmd LoadCmd 134 | Len uint32 135 | Name [16]byte 136 | Addr uint32 137 | Memsz uint32 138 | Offset uint32 139 | Filesz uint32 140 | Maxprot uint32 // ProgFlag - Maximum Segment Virtual Memory Protections 141 | Prot uint32 // ProgFlag - Initial Segment Virtual Memory Protections 142 | Nsect uint32 143 | Flag uint32 144 | } 145 | 146 | // A Segment64 is a 64-bit Mach-O segment load command. 147 | Segment64 struct { 148 | Cmd LoadCmd 149 | Len uint32 150 | Name [16]byte 151 | Addr uint64 152 | Memsz uint64 153 | Offset uint64 154 | Filesz uint64 155 | Maxprot uint32 156 | Prot uint32 157 | Nsect uint32 158 | Flag uint32 159 | } 160 | 161 | // A SigBlockCmd is a Mach-O symbol table command. 162 | SigBlockCmd struct { 163 | Cmd LoadCmd 164 | Len uint32 165 | Sigoff uint32 166 | Sigsize uint32 167 | } 168 | 169 | // A SymtabCmd is a Mach-O symbol table command. 170 | SymtabCmd struct { 171 | Cmd LoadCmd 172 | Len uint32 173 | Symoff uint32 174 | Nsyms uint32 175 | Stroff uint32 176 | Strsize uint32 177 | } 178 | 179 | // A DysymtabCmd is a Mach-O dynamic symbol table command. 180 | DysymtabCmd struct { 181 | Cmd LoadCmd 182 | Len uint32 183 | Ilocalsym uint32 184 | Nlocalsym uint32 185 | Iextdefsym uint32 186 | Nextdefsym uint32 187 | Iundefsym uint32 188 | Nundefsym uint32 189 | Tocoffset uint32 190 | Ntoc uint32 191 | Modtaboff uint32 192 | Nmodtab uint32 193 | Extrefsymoff uint32 194 | Nextrefsyms uint32 195 | Indirectsymoff uint32 196 | Nindirectsyms uint32 197 | Extreloff uint32 198 | Nextrel uint32 199 | Locreloff uint32 200 | Nlocrel uint32 201 | } 202 | 203 | // DylinkerCmd is a load command to identify the dynamic linker 204 | DylinkerCmd struct { 205 | Cmd LoadCmd 206 | Len uint32 207 | Name uint32 208 | } 209 | 210 | // A DylibCmd is a Mach-O load dynamic library command. 211 | DylibCmd struct { 212 | Cmd LoadCmd 213 | Len uint32 214 | Name uint32 215 | Time uint32 216 | CurrentVersion uint32 217 | CompatVersion uint32 218 | } 219 | 220 | // A FuncStartsCmd is a Mach-O load Function Starts command 221 | FuncStartsCmd struct { 222 | Cmd LoadCmd 223 | Len uint32 224 | Dataoff uint32 225 | Datasize uint32 226 | } 227 | 228 | // A DataInCodeCmd is a Mach-O load for Data In Code command 229 | DataInCodeCmd struct { 230 | Cmd LoadCmd 231 | Len uint32 232 | Dataoff uint32 233 | Datasize uint32 234 | } 235 | 236 | // A DylinkInfoCmd is a Mach-O load for Dynamic Linker Info Only Command 237 | DylinkInfoCmd struct { 238 | Cmd LoadCmd 239 | Len uint32 240 | Rebaseoff uint32 241 | Rebasesize uint32 242 | Bindinginfooff uint32 243 | Bindinginfosize uint32 244 | Weakbindingoff uint32 245 | Weakbindingsize uint32 246 | Lazybindingoff uint32 247 | Lazybindingsize uint32 248 | Exportinfooff uint32 249 | Exportinfosize uint32 250 | } 251 | 252 | // A RpathCmd is a Mach-O rpath command. 253 | RpathCmd struct { 254 | Cmd LoadCmd 255 | Len uint32 256 | Path uint32 257 | } 258 | 259 | // A Thread is a Mach-O thread state command. 260 | Thread struct { 261 | Cmd LoadCmd 262 | Len uint32 263 | Type uint32 264 | Data []uint32 265 | } 266 | 267 | // A EntryPointCmd is a Mach-O entry point command. 268 | EntryPointCmd struct { 269 | Cmd uint32 /* LC_MAIN only used in MH_EXECUTE filetypes */ 270 | CmdSize uint32 /* 24 */ 271 | EntryOff uint64 /* file (__TEXT) offset of main() */ 272 | StackSize uint64 /* if not zero, initial stack size */ 273 | } 274 | ) 275 | 276 | const ( 277 | FlagNoUndefs uint32 = 0x1 278 | FlagIncrLink uint32 = 0x2 279 | FlagDyldLink uint32 = 0x4 280 | FlagBindAtLoad uint32 = 0x8 281 | FlagPrebound uint32 = 0x10 282 | FlagSplitSegs uint32 = 0x20 283 | FlagLazyInit uint32 = 0x40 284 | FlagTwoLevel uint32 = 0x80 285 | FlagForceFlat uint32 = 0x100 286 | FlagNoMultiDefs uint32 = 0x200 287 | FlagNoFixPrebinding uint32 = 0x400 288 | FlagPrebindable uint32 = 0x800 289 | FlagAllModsBound uint32 = 0x1000 290 | FlagSubsectionsViaSymbols uint32 = 0x2000 291 | FlagCanonical uint32 = 0x4000 292 | FlagWeakDefines uint32 = 0x8000 293 | FlagBindsToWeak uint32 = 0x10000 294 | FlagAllowStackExecution uint32 = 0x20000 295 | FlagRootSafe uint32 = 0x40000 296 | FlagSetuidSafe uint32 = 0x80000 297 | FlagNoReexportedDylibs uint32 = 0x100000 298 | FlagPIE uint32 = 0x200000 299 | FlagDeadStrippableDylib uint32 = 0x400000 300 | FlagHasTLVDescriptors uint32 = 0x800000 301 | FlagNoHeapExecution uint32 = 0x1000000 302 | FlagAppExtensionSafe uint32 = 0x2000000 303 | ) 304 | 305 | // A Section32 is a 32-bit Mach-O section header. 306 | type Section32 struct { 307 | Name [16]byte 308 | Seg [16]byte 309 | Addr uint32 310 | Size uint32 311 | Offset uint32 312 | Align uint32 313 | Reloff uint32 314 | Nreloc uint32 315 | Flags uint32 316 | Reserve1 uint32 317 | Reserve2 uint32 318 | } 319 | 320 | // A Section64 is a 64-bit Mach-O section header. 321 | type Section64 struct { 322 | Name [16]byte 323 | Seg [16]byte 324 | Addr uint64 325 | Size uint64 326 | Offset uint32 327 | Align uint32 328 | Reloff uint32 329 | Nreloc uint32 330 | Flags uint32 331 | Reserve1 uint32 332 | Reserve2 uint32 333 | Reserve3 uint32 334 | } 335 | 336 | // An Nlist32 is a Mach-O 32-bit symbol table entry. 337 | type Nlist32 struct { 338 | Name uint32 339 | Type uint8 340 | Sect uint8 341 | Desc uint16 342 | Value uint32 343 | } 344 | 345 | // An Nlist64 is a Mach-O 64-bit symbol table entry. 346 | type Nlist64 struct { 347 | Name uint32 348 | Type uint8 349 | Sect uint8 350 | Desc uint16 351 | Value uint64 352 | } 353 | 354 | // Regs386 is the Mach-O 386 register structure. 355 | type Regs386 struct { 356 | AX uint32 357 | BX uint32 358 | CX uint32 359 | DX uint32 360 | DI uint32 361 | SI uint32 362 | BP uint32 363 | SP uint32 364 | SS uint32 365 | FLAGS uint32 366 | IP uint32 367 | CS uint32 368 | DS uint32 369 | ES uint32 370 | FS uint32 371 | GS uint32 372 | } 373 | 374 | // RegsAMD64 is the Mach-O AMD64 register structure. 375 | type RegsAMD64 struct { 376 | AX uint64 377 | BX uint64 378 | CX uint64 379 | DX uint64 380 | DI uint64 381 | SI uint64 382 | BP uint64 383 | SP uint64 384 | R8 uint64 385 | R9 uint64 386 | R10 uint64 387 | R11 uint64 388 | R12 uint64 389 | R13 uint64 390 | R14 uint64 391 | R15 uint64 392 | IP uint64 393 | FLAGS uint64 394 | CS uint64 395 | FS uint64 396 | GS uint64 397 | } 398 | 399 | type intName struct { 400 | i uint32 401 | s string 402 | } 403 | 404 | func stringName(i uint32, names []intName, goSyntax bool) string { 405 | for _, n := range names { 406 | if n.i == i { 407 | if goSyntax { 408 | return "macho." + n.s 409 | } 410 | return n.s 411 | } 412 | } 413 | return strconv.FormatUint(uint64(i), 10) 414 | } 415 | -------------------------------------------------------------------------------- /macho/reloctype.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package macho 6 | 7 | //go:generate stringer -type=RelocTypeGeneric,RelocTypeX86_64,RelocTypeARM,RelocTypeARM64 -output reloctype_string.go 8 | 9 | type RelocTypeGeneric int 10 | 11 | const ( 12 | GENERIC_RELOC_VANILLA RelocTypeGeneric = 0 13 | GENERIC_RELOC_PAIR RelocTypeGeneric = 1 14 | GENERIC_RELOC_SECTDIFF RelocTypeGeneric = 2 15 | GENERIC_RELOC_PB_LA_PTR RelocTypeGeneric = 3 16 | GENERIC_RELOC_LOCAL_SECTDIFF RelocTypeGeneric = 4 17 | GENERIC_RELOC_TLV RelocTypeGeneric = 5 18 | ) 19 | 20 | func (r RelocTypeGeneric) GoString() string { return "macho." + r.String() } 21 | 22 | type RelocTypeX86_64 int 23 | 24 | const ( 25 | X86_64_RELOC_UNSIGNED RelocTypeX86_64 = 0 26 | X86_64_RELOC_SIGNED RelocTypeX86_64 = 1 27 | X86_64_RELOC_BRANCH RelocTypeX86_64 = 2 28 | X86_64_RELOC_GOT_LOAD RelocTypeX86_64 = 3 29 | X86_64_RELOC_GOT RelocTypeX86_64 = 4 30 | X86_64_RELOC_SUBTRACTOR RelocTypeX86_64 = 5 31 | X86_64_RELOC_SIGNED_1 RelocTypeX86_64 = 6 32 | X86_64_RELOC_SIGNED_2 RelocTypeX86_64 = 7 33 | X86_64_RELOC_SIGNED_4 RelocTypeX86_64 = 8 34 | X86_64_RELOC_TLV RelocTypeX86_64 = 9 35 | ) 36 | 37 | func (r RelocTypeX86_64) GoString() string { return "macho." + r.String() } 38 | 39 | type RelocTypeARM int 40 | 41 | const ( 42 | ARM_RELOC_VANILLA RelocTypeARM = 0 43 | ARM_RELOC_PAIR RelocTypeARM = 1 44 | ARM_RELOC_SECTDIFF RelocTypeARM = 2 45 | ARM_RELOC_LOCAL_SECTDIFF RelocTypeARM = 3 46 | ARM_RELOC_PB_LA_PTR RelocTypeARM = 4 47 | ARM_RELOC_BR24 RelocTypeARM = 5 48 | ARM_THUMB_RELOC_BR22 RelocTypeARM = 6 49 | ARM_THUMB_32BIT_BRANCH RelocTypeARM = 7 50 | ARM_RELOC_HALF RelocTypeARM = 8 51 | ARM_RELOC_HALF_SECTDIFF RelocTypeARM = 9 52 | ) 53 | 54 | func (r RelocTypeARM) GoString() string { return "macho." + r.String() } 55 | 56 | type RelocTypeARM64 int 57 | 58 | const ( 59 | ARM64_RELOC_UNSIGNED RelocTypeARM64 = 0 60 | ARM64_RELOC_SUBTRACTOR RelocTypeARM64 = 1 61 | ARM64_RELOC_BRANCH26 RelocTypeARM64 = 2 62 | ARM64_RELOC_PAGE21 RelocTypeARM64 = 3 63 | ARM64_RELOC_PAGEOFF12 RelocTypeARM64 = 4 64 | ARM64_RELOC_GOT_LOAD_PAGE21 RelocTypeARM64 = 5 65 | ARM64_RELOC_GOT_LOAD_PAGEOFF12 RelocTypeARM64 = 6 66 | ARM64_RELOC_POINTER_TO_GOT RelocTypeARM64 = 7 67 | ARM64_RELOC_TLVP_LOAD_PAGE21 RelocTypeARM64 = 8 68 | ARM64_RELOC_TLVP_LOAD_PAGEOFF12 RelocTypeARM64 = 9 69 | ARM64_RELOC_ADDEND RelocTypeARM64 = 10 70 | ) 71 | 72 | func (r RelocTypeARM64) GoString() string { return "macho." + r.String() } 73 | -------------------------------------------------------------------------------- /macho/reloctype_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=RelocTypeGeneric,RelocTypeX86_64,RelocTypeARM,RelocTypeARM64 -output reloctype_string.go"; DO NOT EDIT. 2 | 3 | package macho 4 | 5 | import "strconv" 6 | 7 | const _RelocTypeGeneric_name = "GENERIC_RELOC_VANILLAGENERIC_RELOC_PAIRGENERIC_RELOC_SECTDIFFGENERIC_RELOC_PB_LA_PTRGENERIC_RELOC_LOCAL_SECTDIFFGENERIC_RELOC_TLV" 8 | 9 | var _RelocTypeGeneric_index = [...]uint8{0, 21, 39, 61, 84, 112, 129} 10 | 11 | func (i RelocTypeGeneric) String() string { 12 | if i < 0 || i >= RelocTypeGeneric(len(_RelocTypeGeneric_index)-1) { 13 | return "RelocTypeGeneric(" + strconv.FormatInt(int64(i), 10) + ")" 14 | } 15 | return _RelocTypeGeneric_name[_RelocTypeGeneric_index[i]:_RelocTypeGeneric_index[i+1]] 16 | } 17 | 18 | const _RelocTypeX86_64_name = "X86_64_RELOC_UNSIGNEDX86_64_RELOC_SIGNEDX86_64_RELOC_BRANCHX86_64_RELOC_GOT_LOADX86_64_RELOC_GOTX86_64_RELOC_SUBTRACTORX86_64_RELOC_SIGNED_1X86_64_RELOC_SIGNED_2X86_64_RELOC_SIGNED_4X86_64_RELOC_TLV" 19 | 20 | var _RelocTypeX86_64_index = [...]uint8{0, 21, 40, 59, 80, 96, 119, 140, 161, 182, 198} 21 | 22 | func (i RelocTypeX86_64) String() string { 23 | if i < 0 || i >= RelocTypeX86_64(len(_RelocTypeX86_64_index)-1) { 24 | return "RelocTypeX86_64(" + strconv.FormatInt(int64(i), 10) + ")" 25 | } 26 | return _RelocTypeX86_64_name[_RelocTypeX86_64_index[i]:_RelocTypeX86_64_index[i+1]] 27 | } 28 | 29 | const _RelocTypeARM_name = "ARM_RELOC_VANILLAARM_RELOC_PAIRARM_RELOC_SECTDIFFARM_RELOC_LOCAL_SECTDIFFARM_RELOC_PB_LA_PTRARM_RELOC_BR24ARM_THUMB_RELOC_BR22ARM_THUMB_32BIT_BRANCHARM_RELOC_HALFARM_RELOC_HALF_SECTDIFF" 30 | 31 | var _RelocTypeARM_index = [...]uint8{0, 17, 31, 49, 73, 92, 106, 126, 148, 162, 185} 32 | 33 | func (i RelocTypeARM) String() string { 34 | if i < 0 || i >= RelocTypeARM(len(_RelocTypeARM_index)-1) { 35 | return "RelocTypeARM(" + strconv.FormatInt(int64(i), 10) + ")" 36 | } 37 | return _RelocTypeARM_name[_RelocTypeARM_index[i]:_RelocTypeARM_index[i+1]] 38 | } 39 | 40 | const _RelocTypeARM64_name = "ARM64_RELOC_UNSIGNEDARM64_RELOC_SUBTRACTORARM64_RELOC_BRANCH26ARM64_RELOC_PAGE21ARM64_RELOC_PAGEOFF12ARM64_RELOC_GOT_LOAD_PAGE21ARM64_RELOC_GOT_LOAD_PAGEOFF12ARM64_RELOC_POINTER_TO_GOTARM64_RELOC_TLVP_LOAD_PAGE21ARM64_RELOC_TLVP_LOAD_PAGEOFF12ARM64_RELOC_ADDEND" 41 | 42 | var _RelocTypeARM64_index = [...]uint16{0, 20, 42, 62, 80, 101, 128, 158, 184, 212, 243, 261} 43 | 44 | func (i RelocTypeARM64) String() string { 45 | if i < 0 || i >= RelocTypeARM64(len(_RelocTypeARM64_index)-1) { 46 | return "RelocTypeARM64(" + strconv.FormatInt(int64(i), 10) + ")" 47 | } 48 | return _RelocTypeARM64_name[_RelocTypeARM64_index[i]:_RelocTypeARM64_index[i+1]] 49 | } 50 | -------------------------------------------------------------------------------- /macho/testdata/clang-386-darwin-exec-with-rpath: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/macho/testdata/clang-386-darwin-exec-with-rpath -------------------------------------------------------------------------------- /macho/testdata/clang-386-darwin.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/macho/testdata/clang-386-darwin.obj -------------------------------------------------------------------------------- /macho/testdata/clang-amd64-darwin-exec-with-rpath: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/macho/testdata/clang-amd64-darwin-exec-with-rpath -------------------------------------------------------------------------------- /macho/testdata/clang-amd64-darwin.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/macho/testdata/clang-amd64-darwin.obj -------------------------------------------------------------------------------- /macho/testdata/fat-gcc-386-amd64-darwin-exec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/macho/testdata/fat-gcc-386-amd64-darwin-exec -------------------------------------------------------------------------------- /macho/testdata/gcc-386-darwin-exec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/macho/testdata/gcc-386-darwin-exec -------------------------------------------------------------------------------- /macho/testdata/gcc-amd64-darwin-exec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/macho/testdata/gcc-amd64-darwin-exec -------------------------------------------------------------------------------- /macho/testdata/gcc-amd64-darwin-exec-debug: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/macho/testdata/gcc-amd64-darwin-exec-debug -------------------------------------------------------------------------------- /macho/testdata/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int 4 | main(void) 5 | { 6 | printf("hello, world\n"); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /pe/cert.go: -------------------------------------------------------------------------------- 1 | package pe 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | ) 8 | 9 | // CERTIFICATE_TABLE is the index of the Certificate Table info in the Data Directory structure 10 | // in the PE header 11 | const CERTIFICATE_TABLE = 4 12 | 13 | func readCertTable(f *File, r io.ReadSeeker) ([]byte, error) { 14 | if f.OptionalHeader == nil { // Optional header is optional, might not exist 15 | return nil, nil 16 | } 17 | 18 | var certTableOffset, certTableSize uint32 19 | 20 | switch f.FileHeader.Machine { 21 | case IMAGE_FILE_MACHINE_I386: 22 | certTableOffset = f.OptionalHeader.(*OptionalHeader32).DataDirectory[CERTIFICATE_TABLE].VirtualAddress 23 | certTableSize = f.OptionalHeader.(*OptionalHeader32).DataDirectory[CERTIFICATE_TABLE].Size 24 | case IMAGE_FILE_MACHINE_AMD64: 25 | certTableOffset = f.OptionalHeader.(*OptionalHeader64).DataDirectory[CERTIFICATE_TABLE].VirtualAddress 26 | certTableSize = f.OptionalHeader.(*OptionalHeader64).DataDirectory[CERTIFICATE_TABLE].Size 27 | default: 28 | return nil, errors.New("architecture not supported") 29 | } 30 | 31 | // check if certificate table exists 32 | if certTableOffset == 0 || certTableSize == 0 { 33 | return nil, nil 34 | } 35 | 36 | var err error 37 | _, err = r.Seek(int64(certTableOffset), seekStart) 38 | if err != nil { 39 | return nil, fmt.Errorf("fail to seek to certificate table: %v", err) 40 | } 41 | 42 | // grab the cert 43 | cert := make([]byte, certTableSize) 44 | _, err = io.ReadFull(r, cert) 45 | if err != nil { 46 | return nil, fmt.Errorf("fail to read certificate table: %v", err) 47 | } 48 | 49 | return cert, nil 50 | } 51 | -------------------------------------------------------------------------------- /pe/exports.go: -------------------------------------------------------------------------------- 1 | package pe 2 | 3 | import ( 4 | "encoding/binary" 5 | ) 6 | 7 | // ExportDirectory - data directory definition for exported functions 8 | type ExportDirectory struct { 9 | ExportFlags uint32 // reserved, must be zero 10 | TimeDateStamp uint32 11 | MajorVersion uint16 12 | MinorVersion uint16 13 | NameRVA uint32 // pointer to the name of the DLL 14 | OrdinalBase uint32 15 | NumberOfFunctions uint32 16 | NumberOfNames uint32 // also Ordinal Table Len 17 | AddressTableAddr uint32 // RVA of EAT, relative to image base 18 | NameTableAddr uint32 // RVA of export name pointer table, relative to image base 19 | OrdinalTableAddr uint32 // address of the ordinal table, relative to iamge base 20 | 21 | DllName string 22 | } 23 | 24 | // Export - describes a single export entry 25 | type Export struct { 26 | Ordinal uint32 27 | Name string 28 | VirtualAddress uint32 29 | Forward string 30 | } 31 | 32 | // Exports - gets exports 33 | func (f *File) Exports() ([]Export, error) { 34 | pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 35 | 36 | // grab the number of data directory entries 37 | var ddLength uint32 38 | if pe64 { 39 | ddLength = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes 40 | } else { 41 | ddLength = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes 42 | } 43 | 44 | // check that the length of data directory entries is large 45 | // enough to include the exports directory. 46 | if ddLength < IMAGE_DIRECTORY_ENTRY_EXPORT+1 { 47 | return nil, nil 48 | } 49 | 50 | // grab the export data directory entry 51 | var edd DataDirectory 52 | if pe64 { 53 | edd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] 54 | } else { 55 | edd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] 56 | } 57 | 58 | // figure out which section contains the export directory table 59 | var ds *Section 60 | ds = nil 61 | for _, s := range f.Sections { 62 | if s.VirtualAddress <= edd.VirtualAddress && edd.VirtualAddress < s.VirtualAddress+s.VirtualSize { 63 | ds = s 64 | break 65 | } 66 | } 67 | 68 | // didn't find a section, so no exports were found 69 | if ds == nil { 70 | return nil, nil 71 | } 72 | 73 | d, err := ds.Data() 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | exportDirOffset := edd.VirtualAddress - ds.VirtualAddress 79 | 80 | // seek to the virtual address specified in the export data directory 81 | dxd := d[exportDirOffset:] 82 | 83 | // deserialize export directory 84 | var dt ExportDirectory 85 | dt.ExportFlags = binary.LittleEndian.Uint32(dxd[0:4]) 86 | dt.TimeDateStamp = binary.LittleEndian.Uint32(dxd[4:8]) 87 | dt.MajorVersion = binary.LittleEndian.Uint16(dxd[8:10]) 88 | dt.MinorVersion = binary.LittleEndian.Uint16(dxd[10:12]) 89 | dt.NameRVA = binary.LittleEndian.Uint32(dxd[12:16]) 90 | dt.OrdinalBase = binary.LittleEndian.Uint32(dxd[16:20]) 91 | dt.NumberOfFunctions = binary.LittleEndian.Uint32(dxd[20:24]) 92 | dt.NumberOfNames = binary.LittleEndian.Uint32(dxd[24:28]) 93 | dt.AddressTableAddr = binary.LittleEndian.Uint32(dxd[28:32]) 94 | dt.NameTableAddr = binary.LittleEndian.Uint32(dxd[32:36]) 95 | dt.OrdinalTableAddr = binary.LittleEndian.Uint32(dxd[36:40]) 96 | 97 | dt.DllName, _ = getString(d, int(dt.NameRVA-ds.VirtualAddress)) 98 | 99 | ordinalTable := make(map[uint16]uint32) 100 | if dt.OrdinalTableAddr > ds.VirtualAddress && dt.NameTableAddr > ds.VirtualAddress { 101 | // seek to ordinal table 102 | dno := d[dt.OrdinalTableAddr-ds.VirtualAddress:] 103 | // seek to names table 104 | dnn := d[dt.NameTableAddr-ds.VirtualAddress:] 105 | 106 | // build whole ordinal->name table 107 | for n := uint32(0); n < dt.NumberOfNames; n++ { 108 | ord := binary.LittleEndian.Uint16(dno[n*2 : (n*2)+2]) 109 | nameRVA := binary.LittleEndian.Uint32(dnn[n*4 : (n*4)+4]) 110 | ordinalTable[ord] = nameRVA 111 | } 112 | dno = nil 113 | dnn = nil 114 | } 115 | 116 | // seek to ordinal table 117 | dna := d[dt.AddressTableAddr-ds.VirtualAddress:] 118 | 119 | var exports []Export 120 | for i := uint32(0); i < dt.NumberOfFunctions; i++ { 121 | var export Export 122 | export.VirtualAddress = 123 | binary.LittleEndian.Uint32(dna[i*4 : (i*4)+4]) 124 | export.Ordinal = dt.OrdinalBase + i 125 | 126 | // if this address is inside the export section, this export is a Forwarder RVA 127 | if ds.VirtualAddress <= export.VirtualAddress && 128 | export.VirtualAddress < ds.VirtualAddress+ds.VirtualSize { 129 | export.Forward, _ = getString(d, int(export.VirtualAddress-ds.VirtualAddress)) 130 | } 131 | 132 | // check the entire ordinal table looking for this index to see if we have a name 133 | _, ok := ordinalTable[uint16(i)] 134 | if ok { // a name exists for this exported function 135 | nameRVA, _ := ordinalTable[uint16(i)] 136 | export.Name, _ = getString(d, int(nameRVA-ds.VirtualAddress)) 137 | } 138 | exports = append(exports, export) 139 | } 140 | return exports, nil 141 | } 142 | -------------------------------------------------------------------------------- /pe/file_cgo_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build cgo 6 | 7 | package pe 8 | 9 | import ( 10 | "os/exec" 11 | "testing" 12 | ) 13 | 14 | func testCgoDWARF(t *testing.T, linktype int) { 15 | if _, err := exec.LookPath("gcc"); err != nil { 16 | t.Skip("skipping test: gcc is missing") 17 | } 18 | testDWARF(t, linktype) 19 | } 20 | 21 | func TestDefaultLinkerDWARF(t *testing.T) { 22 | testCgoDWARF(t, linkCgoDefault) 23 | } 24 | 25 | func TestInternalLinkerDWARF(t *testing.T) { 26 | testCgoDWARF(t, linkCgoInternal) 27 | } 28 | 29 | func TestExternalLinkerDWARF(t *testing.T) { 30 | testCgoDWARF(t, linkCgoExternal) 31 | } 32 | -------------------------------------------------------------------------------- /pe/imports.go: -------------------------------------------------------------------------------- 1 | package pe 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | ) 7 | 8 | // ImportDirectory entry 9 | type ImportDirectory struct { 10 | OriginalFirstThunk uint32 11 | TimeDateStamp uint32 12 | ForwarderChain uint32 13 | NameRVA uint32 14 | FirstThunk uint32 15 | 16 | DllName string 17 | } 18 | 19 | // ImgDelayDescr entry for delayloaded libraries 20 | type ImgDelayDescr struct { 21 | GrAttrs, 22 | RVADLLName, 23 | RVAHmod, 24 | RVAIAT, 25 | RVAINT, 26 | RVABoundIAT, 27 | RVAUnloadIAT, 28 | DwTimeStamp uint32 29 | 30 | DllName string 31 | } 32 | 33 | // IAT returns the DataDirectory for the IAT 34 | func (f *File) IAT() *DataDirectory { 35 | _, idd := f.sectionFromDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IAT) 36 | return &idd 37 | } 38 | 39 | // ImportDirectoryTable - returns the Import Directory Table, a pointer to the section, and the section raw data 40 | func (f *File) ImportDirectoryTable() ([]ImportDirectory, *Section, *[]byte, error) { 41 | 42 | ds, idd := f.sectionFromDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT) 43 | 44 | // didn't find a section, so no import libraries were found 45 | if ds == nil { 46 | return nil, nil, nil, nil 47 | } 48 | 49 | sectionData, err := ds.Data() 50 | if err != nil { 51 | return nil, nil, nil, err 52 | } 53 | 54 | // seek to the virtual address specified in the import data directory 55 | d := sectionData[idd.VirtualAddress-ds.VirtualAddress:] 56 | 57 | // start decoding the import directory 58 | var ida []ImportDirectory 59 | for len(d) > 0 { 60 | var dt ImportDirectory 61 | dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4]) 62 | dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8]) 63 | dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12]) 64 | dt.NameRVA = binary.LittleEndian.Uint32(d[12:16]) 65 | dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20]) 66 | dt.DllName, _ = getString(sectionData, int(dt.NameRVA-ds.VirtualAddress)) 67 | d = d[20:] 68 | if dt.OriginalFirstThunk == 0 { 69 | break 70 | } 71 | ida = append(ida, dt) 72 | } 73 | return ida, ds, §ionData, nil 74 | } 75 | 76 | // ImportedSymbols returns the names of all symbols 77 | // referred to by the binary f that are expected to be 78 | // satisfied by other libraries at dynamic load time. 79 | // It does not return weak symbols. 80 | func (f *File) ImportedSymbols() ([]string, error) { 81 | pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 82 | 83 | ida, ds, sectionData, err := f.ImportDirectoryTable() 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | var all []string 89 | for _, dt := range ida { 90 | d := (*sectionData)[:] 91 | // seek to OriginalFirstThunk 92 | if dt.OriginalFirstThunk-ds.VirtualAddress > uint32(len(d)) { 93 | return all, fmt.Errorf("bad object ref start, got %d maxlen %d", dt.OriginalFirstThunk-ds.VirtualAddress, len(d)) 94 | } 95 | d = d[dt.OriginalFirstThunk-ds.VirtualAddress:] 96 | for len(d) > 0 { 97 | if pe64 { // 64bit 98 | va := binary.LittleEndian.Uint64(d[0:8]) 99 | d = d[8:] 100 | if va == 0 { 101 | break 102 | } 103 | if va&0x8000000000000000 > 0 { // is Ordinal 104 | // TODO add dynimport ordinal support. 105 | } else { 106 | fn, _ := getString(*sectionData, int(uint32(va)-ds.VirtualAddress+2)) 107 | all = append(all, fn+":"+dt.DllName) 108 | } 109 | } else { // 32bit 110 | va := binary.LittleEndian.Uint32(d[0:4]) 111 | d = d[4:] 112 | if va == 0 { 113 | break 114 | } 115 | if va&0x80000000 > 0 { // is Ordinal 116 | // TODO add dynimport ordinal support. 117 | //ord := va&0x0000FFFF 118 | } else { 119 | fn, _ := getString(*sectionData, int(va-ds.VirtualAddress+2)) 120 | all = append(all, fn+":"+dt.DllName) 121 | } 122 | } 123 | } 124 | } 125 | 126 | return all, nil 127 | } 128 | 129 | // ImportedLibraries returns the names of all libraries 130 | // referred to by the binary f that are expected to be 131 | // linked with the binary at dynamic link time. 132 | func (f *File) ImportedLibraries() ([]string, error) { 133 | ida, _, _, err := f.ImportDirectoryTable() 134 | if err != nil { 135 | return nil, err 136 | } 137 | var all []string 138 | for _, dt := range ida { 139 | all = append(all, dt.DllName) 140 | } 141 | return all, nil 142 | } 143 | 144 | func (f File) sectionFromDirectoryEntry(directory uint32) (*Section, DataDirectory) { 145 | pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 146 | 147 | // grab the number of data directory entries 148 | var ddLength uint32 149 | if pe64 { 150 | ddLength = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes 151 | } else { 152 | ddLength = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes 153 | } 154 | 155 | // check that the length of data directory entries is large 156 | // enough to include the directory. 157 | if ddLength < directory+1 { 158 | return nil, DataDirectory{} 159 | } 160 | 161 | // grab the directory entry 162 | var idd DataDirectory 163 | if pe64 { 164 | idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[directory] 165 | } else { 166 | idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[directory] 167 | } 168 | 169 | // figure out which section contains the directory table 170 | var ds *Section 171 | for _, s := range f.Sections { 172 | if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress < s.VirtualAddress+s.VirtualSize { 173 | ds = s 174 | break 175 | } 176 | } 177 | return ds, idd 178 | } 179 | 180 | // ImportDelayDirectoryTable - returns the Import Directory Table, a pointer to the section, and the section raw data 181 | func (f *File) ImportDelayDirectoryTable() ([]ImgDelayDescr, *Section, *[]byte, error) { 182 | 183 | ds, idd := f.sectionFromDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT) 184 | 185 | // didn't find a section, so no import libraries were found 186 | if ds == nil { 187 | return nil, nil, nil, nil 188 | } 189 | 190 | sectionData, err := ds.Data() 191 | if err != nil { 192 | return nil, nil, nil, err 193 | } 194 | 195 | // seek to the virtual address specified in the import data directory 196 | d := sectionData[idd.VirtualAddress-ds.VirtualAddress:] 197 | var dida []ImgDelayDescr 198 | for len(d) > 0 { 199 | var dt ImgDelayDescr 200 | idx := 0 201 | dt.GrAttrs = binary.LittleEndian.Uint32(d[idx*4 : (idx*4)+4]) 202 | idx++ 203 | dt.RVADLLName = binary.LittleEndian.Uint32(d[idx*4 : (idx*4)+4]) 204 | idx++ 205 | dt.RVAHmod = binary.LittleEndian.Uint32(d[idx*4 : (idx*4)+4]) 206 | idx++ 207 | dt.RVAIAT = binary.LittleEndian.Uint32(d[idx*4 : (idx*4)+4]) 208 | idx++ 209 | dt.RVAINT = binary.LittleEndian.Uint32(d[idx*4 : (idx*4)+4]) 210 | idx++ 211 | dt.RVABoundIAT = binary.LittleEndian.Uint32(d[idx*4 : (idx*4)+4]) 212 | idx++ 213 | dt.RVAUnloadIAT = binary.LittleEndian.Uint32(d[idx*4 : (idx*4)+4]) 214 | idx++ 215 | dt.DwTimeStamp = binary.LittleEndian.Uint32(d[idx*4 : (idx*4)+4]) 216 | idx++ 217 | 218 | //check for nulls (termination entry) https://github.com/VirusTotal/yara/blob/master/libyara/modules/pe/pe.c#L1163 219 | if dt.DwTimeStamp|dt.GrAttrs|dt.RVADLLName|dt.RVAHmod|dt.RVAIAT|dt.RVAINT|dt.RVABoundIAT|dt.RVAUnloadIAT|dt.DwTimeStamp == 0 { 220 | break 221 | } 222 | if s, ok := getString(sectionData, int(dt.RVADLLName-ds.VirtualAddress)); ok { 223 | dt.DllName = s 224 | } 225 | d = d[32:] 226 | dida = append(dida, dt) 227 | } 228 | 229 | return dida, ds, §ionData, nil 230 | } 231 | 232 | // ImportedDelayLibraries returns the names of all libraries referred to by the binary f 233 | // that are added to the delay imports directory. These libraries are not loaded at initialisation, 234 | // but may be loaded during runtime. 235 | func (f *File) ImportedDelayLibraries() ([]string, error) { 236 | ida, _, _, err := f.ImportDelayDirectoryTable() 237 | if err != nil { 238 | return nil, err 239 | } 240 | var all []string 241 | for _, dt := range ida { 242 | all = append(all, dt.DllName) 243 | } 244 | return all, nil 245 | } 246 | -------------------------------------------------------------------------------- /pe/net.go: -------------------------------------------------------------------------------- 1 | package pe 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | ) 7 | 8 | type IMAGE_COR20_HEADER struct { 9 | Cb uint32 10 | MajorRuntimeVersion uint16 11 | MinorRuntimeVersion uint16 12 | MetaDataRVA, MetaDataSize uint32 13 | Flags uint32 //todo: define flags 14 | EntryPointToken uint32 15 | ResourcesRVA, ResourcesSize, 16 | StrongNameSignatureRVA, StrongNameSignatureSize, 17 | CodeManagerTableRVA, CodeManagerTableSize, 18 | VTableFixupsRVA, VTableFixupsSize, 19 | ExportAddressTableJumpsRVA, ExportAddressTableJumpsSize, 20 | ManagedNativeHeaderRVA, ManagedNativeHeaderSize uint32 21 | } 22 | 23 | //Net provides a public interface for getting at some net info. 24 | type Net struct { 25 | NetDirectory IMAGE_COR20_HEADER //Net directory information 26 | MetaData NetMetaData //MetaData Header 27 | } 28 | 29 | type NetMetaData struct { 30 | Signature [4]byte //should be 0x424a4542 31 | MajorVersion uint16 32 | MinorVersion uint16 33 | Reserved uint32 34 | VersionLength uint32 35 | VersionString []byte 36 | Flags uint16 //todo: define flags betterer 37 | NumberOfStreams uint16 38 | } 39 | 40 | func newMetadataHeader(i io.Reader) (NetMetaData, error) { 41 | r := NetMetaData{} 42 | 43 | //todo: error checks/sanity checks 44 | binary.Read(i, binary.LittleEndian, &r.Signature) 45 | binary.Read(i, binary.LittleEndian, &r.MajorVersion) 46 | binary.Read(i, binary.LittleEndian, &r.MinorVersion) 47 | binary.Read(i, binary.LittleEndian, &r.Reserved) 48 | 49 | // it appears that this value is terminated by two nulls.. that might be important at some point 50 | binary.Read(i, binary.LittleEndian, &r.VersionLength) 51 | r.VersionString = make([]byte, r.VersionLength) 52 | i.Read(r.VersionString) 53 | 54 | binary.Read(i, binary.LittleEndian, r.Flags) 55 | 56 | return r, nil 57 | } 58 | 59 | //NetCLRVersion returns the CLR version specified by the binary. Returns an empty string if not a net binary. String has had trailing nulls stripped. 60 | func (f File) NetCLRVersion() string { 61 | b := f.Net.MetaData.VersionString 62 | for i, x := range b { 63 | if x == 0x00 { 64 | b = b[:i] 65 | break 66 | } 67 | } 68 | return string(b) 69 | } 70 | -------------------------------------------------------------------------------- /pe/pe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package pe 6 | 7 | type DosHeader struct { 8 | MZSignature uint16 9 | UsedBytesInTheLastPage uint16 10 | FileSizeInPages uint16 11 | NumberOfRelocationItems uint16 12 | HeaderSizeInParagraphs uint16 13 | MinimumExtraParagraphs uint16 14 | MaximumExtraParagraphs uint16 15 | InitialRelativeSS uint16 16 | InitialSP uint16 17 | CheckSum uint16 18 | InitialIP uint16 19 | InitialRelativeCS uint16 20 | AddressOfRelocationTable uint16 21 | OverlayNumber uint16 22 | Reserved [4]uint16 23 | OEMid uint16 24 | OEMinfo uint16 25 | Reserved2 [10]uint16 26 | AddressOfNewExeHeader uint32 27 | } 28 | 29 | type FileHeader struct { 30 | Machine uint16 31 | NumberOfSections uint16 32 | TimeDateStamp uint32 33 | PointerToSymbolTable uint32 34 | NumberOfSymbols uint32 35 | SizeOfOptionalHeader uint16 36 | Characteristics uint16 37 | } 38 | 39 | type DataDirectory struct { 40 | VirtualAddress uint32 41 | Size uint32 42 | } 43 | 44 | type OptionalHeader32 struct { 45 | Magic uint16 46 | MajorLinkerVersion uint8 47 | MinorLinkerVersion uint8 48 | SizeOfCode uint32 49 | SizeOfInitializedData uint32 50 | SizeOfUninitializedData uint32 51 | AddressOfEntryPoint uint32 52 | BaseOfCode uint32 53 | BaseOfData uint32 54 | ImageBase uint32 55 | SectionAlignment uint32 56 | FileAlignment uint32 57 | MajorOperatingSystemVersion uint16 58 | MinorOperatingSystemVersion uint16 59 | MajorImageVersion uint16 60 | MinorImageVersion uint16 61 | MajorSubsystemVersion uint16 62 | MinorSubsystemVersion uint16 63 | Win32VersionValue uint32 64 | SizeOfImage uint32 65 | SizeOfHeaders uint32 66 | CheckSum uint32 67 | Subsystem uint16 68 | DllCharacteristics uint16 69 | SizeOfStackReserve uint32 70 | SizeOfStackCommit uint32 71 | SizeOfHeapReserve uint32 72 | SizeOfHeapCommit uint32 73 | LoaderFlags uint32 74 | NumberOfRvaAndSizes uint32 75 | DataDirectory [16]DataDirectory 76 | } 77 | 78 | type OptionalHeader64 struct { 79 | Magic uint16 80 | MajorLinkerVersion uint8 81 | MinorLinkerVersion uint8 82 | SizeOfCode uint32 83 | SizeOfInitializedData uint32 84 | SizeOfUninitializedData uint32 85 | AddressOfEntryPoint uint32 86 | BaseOfCode uint32 87 | ImageBase uint64 88 | SectionAlignment uint32 89 | FileAlignment uint32 90 | MajorOperatingSystemVersion uint16 91 | MinorOperatingSystemVersion uint16 92 | MajorImageVersion uint16 93 | MinorImageVersion uint16 94 | MajorSubsystemVersion uint16 95 | MinorSubsystemVersion uint16 96 | Win32VersionValue uint32 97 | SizeOfImage uint32 98 | SizeOfHeaders uint32 99 | CheckSum uint32 100 | Subsystem uint16 101 | DllCharacteristics uint16 102 | SizeOfStackReserve uint64 103 | SizeOfStackCommit uint64 104 | SizeOfHeapReserve uint64 105 | SizeOfHeapCommit uint64 106 | LoaderFlags uint32 107 | NumberOfRvaAndSizes uint32 108 | DataDirectory [16]DataDirectory 109 | } 110 | 111 | const ( 112 | IMAGE_FILE_MACHINE_UNKNOWN = 0x0 113 | IMAGE_FILE_MACHINE_AM33 = 0x1d3 114 | IMAGE_FILE_MACHINE_AMD64 = 0x8664 115 | IMAGE_FILE_MACHINE_ARM = 0x1c0 116 | IMAGE_FILE_MACHINE_ARMNT = 0x1c4 117 | IMAGE_FILE_MACHINE_ARM64 = 0xaa64 118 | IMAGE_FILE_MACHINE_EBC = 0xebc 119 | IMAGE_FILE_MACHINE_I386 = 0x14c 120 | IMAGE_FILE_MACHINE_IA64 = 0x200 121 | IMAGE_FILE_MACHINE_M32R = 0x9041 122 | IMAGE_FILE_MACHINE_MIPS16 = 0x266 123 | IMAGE_FILE_MACHINE_MIPSFPU = 0x366 124 | IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466 125 | IMAGE_FILE_MACHINE_POWERPC = 0x1f0 126 | IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1 127 | IMAGE_FILE_MACHINE_R4000 = 0x166 128 | IMAGE_FILE_MACHINE_SH3 = 0x1a2 129 | IMAGE_FILE_MACHINE_SH3DSP = 0x1a3 130 | IMAGE_FILE_MACHINE_SH4 = 0x1a6 131 | IMAGE_FILE_MACHINE_SH5 = 0x1a8 132 | IMAGE_FILE_MACHINE_THUMB = 0x1c2 133 | IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169 134 | ) 135 | 136 | // IMAGE_DIRECTORY_ENTRY constants 137 | const ( 138 | IMAGE_DIRECTORY_ENTRY_EXPORT = 0 139 | IMAGE_DIRECTORY_ENTRY_IMPORT = 1 140 | IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 141 | IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 142 | IMAGE_DIRECTORY_ENTRY_SECURITY = 4 143 | IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 144 | IMAGE_DIRECTORY_ENTRY_DEBUG = 6 145 | IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 146 | IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 147 | IMAGE_DIRECTORY_ENTRY_TLS = 9 148 | IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 149 | IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 150 | IMAGE_DIRECTORY_ENTRY_IAT = 12 151 | IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 152 | IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 153 | ) 154 | -------------------------------------------------------------------------------- /pe/reloc.go: -------------------------------------------------------------------------------- 1 | package pe 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "io" 8 | ) 9 | 10 | // RelocationTable - for base relocation entries 11 | type RelocationTableEntry struct { 12 | RelocationBlock 13 | BlockItems []BlockItem 14 | } 15 | 16 | // RelocationBlock - for base relocation entries 17 | type RelocationBlock struct { 18 | VirtualAddress uint32 19 | SizeOfBlock uint32 20 | } 21 | 22 | // BlockItem - relocation block item 23 | type BlockItem struct { 24 | Type byte // 4 bits 25 | Offset uint16 // 12 bits 26 | } 27 | 28 | // Reloc represents a PE COFF relocation. 29 | // Each section contains its own relocation list. 30 | type Reloc struct { 31 | VirtualAddress uint32 32 | SymbolTableIndex uint32 33 | Type uint16 34 | } 35 | 36 | const ( 37 | //IMAGE_REL_BASED_ABSOLUTE - The base relocation is skipped. This type can be used to pad a block. 38 | IMAGE_REL_BASED_ABSOLUTE = 0 39 | 40 | //IMAGE_REL_BASED_HIGH = 1 41 | //IMAGE_REL_BASED_LOW = 2 42 | 43 | //IMAGE_REL_BASED_HIGHLOW - The base relocation applies all 32 bits of the difference to the 32-bit field at offset. 44 | IMAGE_REL_BASED_HIGHLOW = 3 45 | 46 | //IMAGE_REL_BASED_HIGHADJ = 4 47 | //IMAGE_REL_BASED_MIPS_JMPADDR = 5 48 | //IMAGE_REL_BASED_ARM_MOV32 = 5 49 | //IMAGE_REL_BASED_RISCV_HIGH20 = 5 50 | //IMAGE_REL_BASED_THUMB_MOV32 = 7 51 | //IMAGE_REL_BASED_RISCV_LOW12I = 7 52 | //IMAGE_REL_BASED_RISCV_LOW12S = 8 53 | //IMAGE_REL_BASED_MIPS_JMPADDR16 = 9 54 | IMAGE_REL_BASED_DIR64 = 10 55 | ) 56 | 57 | // readBaseRelocationTable - reads the base relocation table from the file and stores it 58 | func (f *File) readBaseRelocationTable() (*[]RelocationTableEntry, error) { 59 | 60 | if f.OptionalHeader == nil { // Optional header is optional, might not exist 61 | return nil, nil 62 | } 63 | 64 | var dd DataDirectory 65 | if f.Machine == IMAGE_FILE_MACHINE_AMD64 { 66 | dd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC] 67 | } else { 68 | dd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC] 69 | } 70 | var sectionData []byte 71 | var err error 72 | for _, section := range f.Sections { 73 | if section.VirtualAddress == dd.VirtualAddress { 74 | sectionData, err = section.Data() 75 | if err != nil { 76 | return nil, err 77 | } 78 | break 79 | } 80 | } 81 | r := bytes.NewReader(sectionData) 82 | var reloBlocks []RelocationTableEntry 83 | bytesRead := uint32(0) 84 | for bytesRead < dd.Size { 85 | var reloBlock RelocationBlock 86 | err = binary.Read(r, binary.LittleEndian, &reloBlock) 87 | bytesRead += 8 88 | if err != nil { 89 | return nil, fmt.Errorf("fail to read relocation block: %v", err) 90 | } 91 | numBlocks := (reloBlock.SizeOfBlock - 8) / 2 92 | blocks := make([]BlockItem, numBlocks) 93 | for i := uint32(0); i < numBlocks; i++ { 94 | var buf [2]byte 95 | err = binary.Read(r, binary.LittleEndian, &buf) 96 | bytesRead += 2 97 | if err != nil { 98 | return nil, fmt.Errorf("fail to read relocation block item %d: %v", i, err) 99 | } 100 | var item BlockItem 101 | val := binary.LittleEndian.Uint16(buf[:2]) 102 | item.Type = byte(val >> 12) 103 | item.Offset = val & 0x0fff 104 | blocks[i] = item 105 | } 106 | reloBlocks = append(reloBlocks, RelocationTableEntry{reloBlock, blocks}) 107 | } 108 | return &reloBlocks, nil 109 | } 110 | 111 | // Relocate - performs base relocations on this image to the given offset 112 | func (f *File) Relocate(baseAddr uint64, image *[]byte) { 113 | var imageBase uint64 114 | pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 115 | if pe64 { 116 | imageBase = f.OptionalHeader.(*OptionalHeader64).ImageBase 117 | } else { 118 | imageBase = uint64(f.OptionalHeader.(*OptionalHeader32).ImageBase) 119 | } 120 | for _, block := range *f.BaseRelocationTable { 121 | pageRVA := block.VirtualAddress 122 | for _, item := range block.BlockItems { 123 | if item.Type == IMAGE_REL_BASED_HIGHLOW { // 32 bit 124 | delta := uint32(baseAddr - imageBase) 125 | fileOffset := f.RVAToFileOffset(pageRVA) 126 | idx := fileOffset + uint32(item.Offset) 127 | originalAddress := binary.LittleEndian.Uint32((*image)[idx : idx+4]) 128 | b := make([]byte, 4) 129 | binary.LittleEndian.PutUint32(b, originalAddress+delta) 130 | copy((*image)[idx:idx+4], b) 131 | } else if item.Type == IMAGE_REL_BASED_DIR64 { // 64 bit 132 | delta := baseAddr - imageBase 133 | fileOffset := f.RVAToFileOffset(pageRVA) 134 | idx := fileOffset + uint32(item.Offset) 135 | originalAddress := binary.LittleEndian.Uint64((*image)[idx : idx+8]) 136 | b := make([]byte, 8) 137 | binary.LittleEndian.PutUint64(b, originalAddress+delta) 138 | copy((*image)[idx:idx+8], b) 139 | } 140 | } 141 | } 142 | 143 | // update imageBase in the optional header 144 | if pe64 { 145 | b := make([]byte, 8) 146 | binary.LittleEndian.PutUint64(b, baseAddr) 147 | idx := f.OptionalHeaderOffset + 24 148 | copy((*image)[idx:idx+8], b) 149 | } else { 150 | b := make([]byte, 4) 151 | binary.LittleEndian.PutUint32(b, uint32(baseAddr)) 152 | idx := f.OptionalHeaderOffset + 28 153 | copy((*image)[idx:idx+4], b) 154 | } 155 | } 156 | 157 | func readRelocs(sh *SectionHeader, r io.ReadSeeker) ([]Reloc, error) { 158 | if sh.NumberOfRelocations <= 0 { 159 | return nil, nil 160 | } 161 | _, err := r.Seek(int64(sh.PointerToRelocations), seekStart) 162 | if err != nil { 163 | return nil, fmt.Errorf("fail to seek to %q section relocations: %v", sh.Name, err) 164 | } 165 | relocs := make([]Reloc, sh.NumberOfRelocations) 166 | err = binary.Read(r, binary.LittleEndian, relocs) 167 | if err != nil { 168 | return nil, fmt.Errorf("fail to read section relocations: %v", err) 169 | } 170 | return relocs, nil 171 | } 172 | -------------------------------------------------------------------------------- /pe/section.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package pe 6 | 7 | import ( 8 | "io" 9 | "strconv" 10 | ) 11 | 12 | // SectionHeader32 represents real PE COFF section header. 13 | type SectionHeader32 struct { 14 | Name [8]uint8 15 | VirtualSize uint32 16 | VirtualAddress uint32 17 | SizeOfRawData uint32 18 | PointerToRawData uint32 19 | PointerToRelocations uint32 20 | PointerToLineNumbers uint32 21 | NumberOfRelocations uint16 22 | NumberOfLineNumbers uint16 23 | Characteristics uint32 24 | } 25 | 26 | // fullName finds real name of section sh. Normally name is stored 27 | // in sh.Name, but if it is longer then 8 characters, it is stored 28 | // in COFF string table st instead. 29 | func (sh *SectionHeader32) fullName(st StringTable) (string, error) { 30 | if sh.Name[0] != '/' { 31 | return cstring(sh.Name[:]), nil 32 | } 33 | i, err := strconv.Atoi(cstring(sh.Name[1:])) 34 | if err != nil { 35 | return "", err 36 | } 37 | return st.String(uint32(i)) 38 | } 39 | 40 | // SectionHeader is similar to SectionHeader32 with Name 41 | // field replaced by Go string. OriginalName is the 42 | // original name of the section on disk. 43 | type SectionHeader struct { 44 | Name string 45 | OriginalName [8]uint8 46 | VirtualSize uint32 47 | VirtualAddress uint32 48 | Size uint32 49 | Offset uint32 50 | PointerToRelocations uint32 51 | PointerToLineNumbers uint32 52 | NumberOfRelocations uint16 53 | NumberOfLineNumbers uint16 54 | Characteristics uint32 55 | } 56 | 57 | // Section provides access to PE COFF section. 58 | type Section struct { 59 | SectionHeader 60 | Relocs []Reloc 61 | 62 | // Embed ReaderAt for ReadAt method. 63 | // Do not embed SectionReader directly 64 | // to avoid having Read and Seek. 65 | // If a client wants Read and Seek it must use 66 | // Open() to avoid fighting over the seek offset 67 | // with other clients. 68 | io.ReaderAt 69 | sr *io.SectionReader 70 | } 71 | 72 | // Data reads and returns the contents of the PE section s. 73 | func (s *Section) Data() ([]byte, error) { 74 | 75 | if s.sr == nil { // This section was added from code, the internal SectionReader is nil 76 | return nil, nil 77 | } 78 | 79 | dat := make([]byte, s.sr.Size()) 80 | n, err := s.sr.ReadAt(dat, 0) 81 | if n == len(dat) { 82 | err = nil 83 | } 84 | return dat[0:n], err 85 | } 86 | 87 | // Open returns a new ReadSeeker reading the PE section s. 88 | func (s *Section) Open() io.ReadSeeker { 89 | return io.NewSectionReader(s.sr, 0, 1<<63-1) 90 | } 91 | 92 | // Replace Section's Data 93 | func (s *Section) Replace(reader io.ReaderAt, length int64) { 94 | s.sr = io.NewSectionReader(reader, 0, length) 95 | s.ReaderAt = s.sr 96 | } 97 | 98 | // Section Flags (Characteristics field) 99 | const ( 100 | IMAGE_SCN_CNT_CODE = 0x00000020 // Section contains code 101 | IMAGE_SCN_MEM_EXECUTE = 0x20000000 // Section is executable 102 | IMAGE_SCN_MEM_READ = 0x40000000 // Section is readable 103 | 104 | IMAGE_FILE_RELOCS_STRIPPED = 0x0001 // Relocation info stripped from file 105 | 106 | IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100 // Image is NX compatable 107 | 108 | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040 // DLL can move 109 | ) 110 | -------------------------------------------------------------------------------- /pe/string.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package pe 6 | 7 | import ( 8 | "bytes" 9 | "encoding/binary" 10 | "fmt" 11 | "io" 12 | ) 13 | 14 | // cstring converts ASCII byte sequence b to string. 15 | // It stops once it finds 0 or reaches end of b. 16 | func cstring(b []byte) string { 17 | i := bytes.IndexByte(b, 0) 18 | if i == -1 { 19 | i = len(b) 20 | } 21 | return string(b[:i]) 22 | } 23 | 24 | // StringTable is a COFF string table. 25 | type StringTable []byte 26 | 27 | func readStringTable(fh *FileHeader, r io.ReadSeeker) (StringTable, error) { 28 | // COFF string table is located right after COFF symbol table. 29 | if fh.PointerToSymbolTable <= 0 { 30 | return nil, nil 31 | } 32 | offset := fh.PointerToSymbolTable + COFFSymbolSize*fh.NumberOfSymbols 33 | _, err := r.Seek(int64(offset), seekStart) 34 | if err != nil { 35 | return nil, fmt.Errorf("fail to seek to string table: %v", err) 36 | } 37 | var l uint32 38 | err = binary.Read(r, binary.LittleEndian, &l) 39 | if err != nil { 40 | return nil, fmt.Errorf("fail to read string table length: %v", err) 41 | } 42 | // string table length includes itself 43 | if l <= 4 { 44 | return nil, nil 45 | } 46 | l -= 4 47 | buf := make([]byte, l) 48 | _, err = io.ReadFull(r, buf) 49 | if err != nil { 50 | return nil, fmt.Errorf("fail to read string table: %v", err) 51 | } 52 | // re-add the length to the first four bytes of the string table 53 | lbuf := make([]byte, 4) 54 | binary.LittleEndian.PutUint32(lbuf, l) 55 | 56 | return StringTable(append(lbuf, buf...)), nil 57 | } 58 | 59 | // String extracts string from COFF string table st at offset start. 60 | func (st StringTable) String(start uint32) (string, error) { 61 | // start includes 4 bytes of string table length 62 | if start < 4 { 63 | return "", fmt.Errorf("offset %d is before the start of string table", start) 64 | } 65 | //start -= 4 // we are now including the uint32 length in the StringTable buffer as a prefix, this might change 66 | if int(start) > len(st) { 67 | return "", fmt.Errorf("offset %d is beyond the end of string table", start) 68 | } 69 | return cstring(st[start:]), nil 70 | } 71 | -------------------------------------------------------------------------------- /pe/symbol.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package pe 6 | 7 | import ( 8 | "encoding/binary" 9 | "fmt" 10 | "io" 11 | ) 12 | 13 | const COFFSymbolSize = 18 14 | 15 | // COFFSymbol represents single COFF symbol table record. 16 | type COFFSymbol struct { 17 | Name [8]uint8 18 | Value uint32 19 | SectionNumber int16 20 | Type uint16 21 | StorageClass uint8 22 | NumberOfAuxSymbols uint8 23 | } 24 | 25 | func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) { 26 | if fh.PointerToSymbolTable == 0 { 27 | return nil, nil 28 | } 29 | if fh.NumberOfSymbols <= 0 { 30 | return nil, nil 31 | } 32 | _, err := r.Seek(int64(fh.PointerToSymbolTable), seekStart) 33 | if err != nil { 34 | return nil, fmt.Errorf("fail to seek to symbol table: %v", err) 35 | } 36 | syms := make([]COFFSymbol, fh.NumberOfSymbols) 37 | err = binary.Read(r, binary.LittleEndian, syms) 38 | if err != nil { 39 | return nil, fmt.Errorf("fail to read symbol table: %v", err) 40 | } 41 | return syms, nil 42 | } 43 | 44 | // isSymNameOffset checks symbol name if it is encoded as offset into string table. 45 | func isSymNameOffset(name [8]byte) (bool, uint32) { 46 | if name[0] == 0 && name[1] == 0 && name[2] == 0 && name[3] == 0 { 47 | return true, binary.LittleEndian.Uint32(name[4:]) 48 | } 49 | return false, 0 50 | } 51 | 52 | // FullName finds real name of symbol sym. Normally name is stored 53 | // in sym.Name, but if it is longer then 8 characters, it is stored 54 | // in COFF string table st instead. 55 | func (sym *COFFSymbol) FullName(st StringTable) (string, error) { 56 | if ok, offset := isSymNameOffset(sym.Name); ok { 57 | return st.String(offset) 58 | } 59 | return cstring(sym.Name[:]), nil 60 | } 61 | 62 | func removeAuxSymbols(allsyms []COFFSymbol, st StringTable) ([]*Symbol, error) { 63 | if len(allsyms) == 0 { 64 | return nil, nil 65 | } 66 | syms := make([]*Symbol, 0) 67 | aux := uint8(0) 68 | for _, sym := range allsyms { 69 | if aux > 0 { 70 | aux-- 71 | continue 72 | } 73 | name, err := sym.FullName(st) 74 | if err != nil { 75 | return nil, err 76 | } 77 | aux = sym.NumberOfAuxSymbols 78 | s := &Symbol{ 79 | Name: name, 80 | Value: sym.Value, 81 | SectionNumber: sym.SectionNumber, 82 | Type: sym.Type, 83 | StorageClass: sym.StorageClass, 84 | } 85 | syms = append(syms, s) 86 | } 87 | return syms, nil 88 | } 89 | 90 | // Symbol is similar to COFFSymbol with Name field replaced 91 | // by Go string. Symbol also does not have NumberOfAuxSymbols. 92 | type Symbol struct { 93 | Name string 94 | Value uint32 95 | SectionNumber int16 96 | Type uint16 97 | StorageClass uint8 98 | } 99 | -------------------------------------------------------------------------------- /pe/testdata/gcc-386-mingw-exec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/pe/testdata/gcc-386-mingw-exec -------------------------------------------------------------------------------- /pe/testdata/gcc-386-mingw-no-symbols-exec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/pe/testdata/gcc-386-mingw-no-symbols-exec -------------------------------------------------------------------------------- /pe/testdata/gcc-386-mingw-obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/pe/testdata/gcc-386-mingw-obj -------------------------------------------------------------------------------- /pe/testdata/gcc-amd64-mingw-exec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/pe/testdata/gcc-amd64-mingw-exec -------------------------------------------------------------------------------- /pe/testdata/gcc-amd64-mingw-obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/pe/testdata/gcc-amd64-mingw-obj -------------------------------------------------------------------------------- /pe/testdata/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int 4 | main(void) 5 | { 6 | printf("hello, world\n"); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /pe/write.go: -------------------------------------------------------------------------------- 1 | package pe 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "os" 8 | ) 9 | 10 | func (peFile *File) Bytes() ([]byte, error) { 11 | var bytesWritten uint64 12 | peBuf := bytes.NewBuffer(nil) 13 | 14 | // write DOS header and stub 15 | binary.Write(peBuf, binary.LittleEndian, peFile.DosHeader) 16 | bytesWritten += uint64(binary.Size(peFile.DosHeader)) 17 | if peFile.DosExists { 18 | binary.Write(peBuf, binary.LittleEndian, peFile.DosStub) 19 | bytesWritten += uint64(binary.Size(peFile.DosStub)) 20 | } 21 | 22 | // write Rich header 23 | if peFile.RichHeader != nil { 24 | binary.Write(peBuf, binary.LittleEndian, peFile.RichHeader) 25 | bytesWritten += uint64(len(peFile.RichHeader)) 26 | } 27 | 28 | // apply padding before PE header if necessary 29 | if uint32(bytesWritten) != peFile.DosHeader.AddressOfNewExeHeader { 30 | padding := make([]byte, peFile.DosHeader.AddressOfNewExeHeader-uint32(bytesWritten)) 31 | binary.Write(peBuf, binary.LittleEndian, padding) 32 | bytesWritten += uint64(len(padding)) 33 | } 34 | 35 | // write PE header 36 | peMagic := []byte{'P', 'E', 0x00, 0x00} 37 | binary.Write(peBuf, binary.LittleEndian, peMagic) 38 | binary.Write(peBuf, binary.LittleEndian, peFile.FileHeader) 39 | bytesWritten += uint64(binary.Size(peFile.FileHeader) + len(peMagic)) 40 | 41 | var ( 42 | is32bit bool 43 | oldCertTableOffset, oldCertTableSize uint32 44 | ) 45 | 46 | switch peFile.FileHeader.Machine { 47 | case IMAGE_FILE_MACHINE_I386: 48 | is32bit = true 49 | optionalHeader := peFile.OptionalHeader.(*OptionalHeader32) 50 | binary.Write(peBuf, binary.LittleEndian, peFile.OptionalHeader.(*OptionalHeader32)) 51 | bytesWritten += uint64(binary.Size(optionalHeader)) 52 | 53 | oldCertTableOffset = optionalHeader.DataDirectory[CERTIFICATE_TABLE].VirtualAddress 54 | oldCertTableSize = optionalHeader.DataDirectory[CERTIFICATE_TABLE].Size 55 | case IMAGE_FILE_MACHINE_AMD64: 56 | is32bit = false 57 | optionalHeader := peFile.OptionalHeader.(*OptionalHeader64) 58 | binary.Write(peBuf, binary.LittleEndian, optionalHeader) 59 | bytesWritten += uint64(binary.Size(optionalHeader)) 60 | 61 | oldCertTableOffset = optionalHeader.DataDirectory[CERTIFICATE_TABLE].VirtualAddress 62 | oldCertTableSize = optionalHeader.DataDirectory[CERTIFICATE_TABLE].Size 63 | default: 64 | return nil, errors.New("architecture not supported") 65 | } 66 | 67 | // write section headers 68 | sectionHeaders := make([]SectionHeader32, len(peFile.Sections)) 69 | for idx, section := range peFile.Sections { 70 | // write section header 71 | sectionHeader := SectionHeader32{ 72 | Name: section.OriginalName, 73 | VirtualSize: section.VirtualSize, 74 | VirtualAddress: section.VirtualAddress, 75 | SizeOfRawData: section.Size, 76 | PointerToRawData: section.Offset, 77 | PointerToRelocations: section.PointerToRelocations, 78 | PointerToLineNumbers: section.PointerToLineNumbers, 79 | NumberOfRelocations: section.NumberOfRelocations, 80 | NumberOfLineNumbers: section.NumberOfLineNumbers, 81 | Characteristics: section.Characteristics, 82 | } 83 | 84 | // if the PE file was pulled from memory, the symbol table offset in the header will be wrong. 85 | // Fix it up by picking the section that lines up, and use the raw offset instead. 86 | if peFile.FileHeader.PointerToSymbolTable == sectionHeader.VirtualAddress { 87 | peFile.FileHeader.PointerToSymbolTable = sectionHeader.PointerToRawData 88 | } 89 | 90 | sectionHeaders[idx] = sectionHeader 91 | 92 | //log.Printf("section: %+v\nsectionHeader: %+v\n", section, sectionHeader) 93 | 94 | binary.Write(peBuf, binary.LittleEndian, sectionHeader) 95 | bytesWritten += uint64(binary.Size(sectionHeader)) 96 | } 97 | 98 | // write sections' data 99 | for idx, sectionHeader := range sectionHeaders { 100 | section := peFile.Sections[idx] 101 | sectionData, err := section.Data() 102 | if err != nil { 103 | return nil, err 104 | } 105 | if sectionData == nil { // for sections that weren't in the original file 106 | sectionData = []byte{} 107 | } 108 | if section.Offset != 0 && bytesWritten < uint64(section.Offset) { 109 | pad := make([]byte, uint64(section.Offset)-bytesWritten) 110 | peBuf.Write(pad) 111 | //log.Printf("Padding before section %s at %x: length:%x to:%x\n", section.Name, bytesWritten, len(pad), section.Offset) 112 | bytesWritten += uint64(len(pad)) 113 | } 114 | // if our shellcode insertion address is inside this section, insert it at the correct offset in sectionData 115 | if peFile.InsertionAddr >= section.Offset && int64(peFile.InsertionAddr) < (int64(section.Offset)+int64(section.Size)-int64(len(peFile.InsertionBytes))) { 116 | sectionData = append(sectionData, peFile.InsertionBytes[:]...) 117 | datalen := len(sectionData) 118 | if sectionHeader.SizeOfRawData > uint32(datalen) { 119 | paddingSize := sectionHeader.SizeOfRawData - uint32(datalen) 120 | padding := make([]byte, paddingSize, paddingSize) 121 | sectionData = append(sectionData, padding...) 122 | //log.Printf("Padding after section %s: length:%d\n", section.Name, paddingSize) 123 | } 124 | } 125 | 126 | binary.Write(peBuf, binary.LittleEndian, sectionData) 127 | bytesWritten += uint64(len(sectionData)) 128 | } 129 | 130 | // write symbols 131 | binary.Write(peBuf, binary.LittleEndian, peFile.COFFSymbols) 132 | bytesWritten += uint64(binary.Size(peFile.COFFSymbols)) 133 | 134 | // write the string table 135 | binary.Write(peBuf, binary.LittleEndian, peFile.StringTable) 136 | bytesWritten += uint64(binary.Size(peFile.StringTable)) 137 | 138 | var newCertTableOffset, newCertTableSize uint32 139 | 140 | // write the certificate table 141 | if peFile.CertificateTable != nil { 142 | newCertTableOffset = uint32(bytesWritten) 143 | newCertTableSize = uint32(len(peFile.CertificateTable)) 144 | } else { 145 | newCertTableOffset = 0 146 | newCertTableSize = 0 147 | } 148 | 149 | binary.Write(peBuf, binary.LittleEndian, peFile.CertificateTable) 150 | bytesWritten += uint64(len(peFile.CertificateTable)) 151 | 152 | peData := peBuf.Bytes() 153 | 154 | // write the offset and size of the new Certificate Table if it changed 155 | if newCertTableOffset != oldCertTableOffset || newCertTableSize != oldCertTableSize { 156 | certTableInfo := &DataDirectory{ 157 | VirtualAddress: newCertTableOffset, 158 | Size: newCertTableSize, 159 | } 160 | 161 | var certTableInfoBuf bytes.Buffer 162 | binary.Write(&certTableInfoBuf, binary.LittleEndian, certTableInfo) 163 | 164 | var certTableLoc int64 165 | if is32bit { 166 | certTableLoc = int64(peFile.DosHeader.AddressOfNewExeHeader) + 24 + 128 167 | } else { 168 | certTableLoc = int64(peFile.DosHeader.AddressOfNewExeHeader) + 24 + 144 169 | } 170 | 171 | peData = append(peData[:certTableLoc], append(certTableInfoBuf.Bytes(), peData[int(certTableLoc)+binary.Size(certTableInfo):]...)...) 172 | } 173 | 174 | return peData, nil 175 | } 176 | 177 | func (peFile *File) WriteFile(destFile string) error { 178 | f, err := os.Create(destFile) 179 | if err != nil { 180 | return err 181 | } 182 | defer f.Close() 183 | 184 | peData, err := peFile.Bytes() 185 | if err != nil { 186 | return err 187 | } 188 | 189 | _, err = f.Write(peData) 190 | if err != nil { 191 | return err 192 | } 193 | 194 | return nil 195 | } 196 | -------------------------------------------------------------------------------- /plan9obj/file.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package plan9obj implements access to Plan 9 a.out object files. 6 | package plan9obj 7 | 8 | import ( 9 | "encoding/binary" 10 | "errors" 11 | "fmt" 12 | "io" 13 | "os" 14 | ) 15 | 16 | // A FileHeader represents a Plan 9 a.out file header. 17 | type FileHeader struct { 18 | Magic uint32 19 | Bss uint32 20 | Entry uint64 21 | PtrSize int 22 | LoadAddress uint64 23 | HdrSize uint64 24 | } 25 | 26 | // A File represents an open Plan 9 a.out file. 27 | type File struct { 28 | FileHeader 29 | Sections []*Section 30 | closer io.Closer 31 | } 32 | 33 | // A SectionHeader represents a single Plan 9 a.out section header. 34 | // This structure doesn't exist on-disk, but eases navigation 35 | // through the object file. 36 | type SectionHeader struct { 37 | Name string 38 | Size uint32 39 | Offset uint32 40 | } 41 | 42 | // A Section represents a single section in a Plan 9 a.out file. 43 | type Section struct { 44 | SectionHeader 45 | 46 | // Embed ReaderAt for ReadAt method. 47 | // Do not embed SectionReader directly 48 | // to avoid having Read and Seek. 49 | // If a client wants Read and Seek it must use 50 | // Open() to avoid fighting over the seek offset 51 | // with other clients. 52 | io.ReaderAt 53 | sr *io.SectionReader 54 | } 55 | 56 | // Data reads and returns the contents of the Plan 9 a.out section. 57 | func (s *Section) Data() ([]byte, error) { 58 | dat := make([]byte, s.sr.Size()) 59 | n, err := s.sr.ReadAt(dat, 0) 60 | if n == len(dat) { 61 | err = nil 62 | } 63 | return dat[0:n], err 64 | } 65 | 66 | // Open returns a new ReadSeeker reading the Plan 9 a.out section. 67 | func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } 68 | 69 | // A Symbol represents an entry in a Plan 9 a.out symbol table section. 70 | type Sym struct { 71 | Value uint64 72 | Type rune 73 | Name string 74 | } 75 | 76 | /* 77 | * Plan 9 a.out reader 78 | */ 79 | 80 | // formatError is returned by some operations if the data does 81 | // not have the correct format for an object file. 82 | type formatError struct { 83 | off int 84 | msg string 85 | val interface{} 86 | } 87 | 88 | func (e *formatError) Error() string { 89 | msg := e.msg 90 | if e.val != nil { 91 | msg += fmt.Sprintf(" '%v'", e.val) 92 | } 93 | msg += fmt.Sprintf(" in record at byte %#x", e.off) 94 | return msg 95 | } 96 | 97 | // Open opens the named file using os.Open and prepares it for use as a Plan 9 a.out binary. 98 | func Open(name string) (*File, error) { 99 | f, err := os.Open(name) 100 | if err != nil { 101 | return nil, err 102 | } 103 | ff, err := NewFile(f) 104 | if err != nil { 105 | f.Close() 106 | return nil, err 107 | } 108 | ff.closer = f 109 | return ff, nil 110 | } 111 | 112 | // Close closes the File. 113 | // If the File was created using NewFile directly instead of Open, 114 | // Close has no effect. 115 | func (f *File) Close() error { 116 | var err error 117 | if f.closer != nil { 118 | err = f.closer.Close() 119 | f.closer = nil 120 | } 121 | return err 122 | } 123 | 124 | func parseMagic(magic []byte) (uint32, error) { 125 | m := binary.BigEndian.Uint32(magic) 126 | switch m { 127 | case Magic386, MagicAMD64, MagicARM: 128 | return m, nil 129 | } 130 | return 0, &formatError{0, "bad magic number", magic} 131 | } 132 | 133 | // NewFile creates a new File for accessing a Plan 9 binary in an underlying reader. 134 | // The Plan 9 binary is expected to start at position 0 in the ReaderAt. 135 | func NewFile(r io.ReaderAt) (*File, error) { 136 | sr := io.NewSectionReader(r, 0, 1<<63-1) 137 | // Read and decode Plan 9 magic 138 | var magic [4]byte 139 | if _, err := r.ReadAt(magic[:], 0); err != nil { 140 | return nil, err 141 | } 142 | _, err := parseMagic(magic[:]) 143 | if err != nil { 144 | return nil, err 145 | } 146 | 147 | ph := new(prog) 148 | if err := binary.Read(sr, binary.BigEndian, ph); err != nil { 149 | return nil, err 150 | } 151 | 152 | f := &File{FileHeader: FileHeader{ 153 | Magic: ph.Magic, 154 | Bss: ph.Bss, 155 | Entry: uint64(ph.Entry), 156 | PtrSize: 4, 157 | LoadAddress: 0x1000, 158 | HdrSize: 4 * 8, 159 | }} 160 | 161 | if ph.Magic&Magic64 != 0 { 162 | if err := binary.Read(sr, binary.BigEndian, &f.Entry); err != nil { 163 | return nil, err 164 | } 165 | f.PtrSize = 8 166 | f.LoadAddress = 0x200000 167 | f.HdrSize += 8 168 | } 169 | 170 | var sects = []struct { 171 | name string 172 | size uint32 173 | }{ 174 | {"text", ph.Text}, 175 | {"data", ph.Data}, 176 | {"syms", ph.Syms}, 177 | {"spsz", ph.Spsz}, 178 | {"pcsz", ph.Pcsz}, 179 | } 180 | 181 | f.Sections = make([]*Section, 5) 182 | 183 | off := uint32(f.HdrSize) 184 | 185 | for i, sect := range sects { 186 | s := new(Section) 187 | s.SectionHeader = SectionHeader{ 188 | Name: sect.name, 189 | Size: sect.size, 190 | Offset: off, 191 | } 192 | off += sect.size 193 | s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Size)) 194 | s.ReaderAt = s.sr 195 | f.Sections[i] = s 196 | } 197 | 198 | return f, nil 199 | } 200 | 201 | func walksymtab(data []byte, ptrsz int, fn func(sym) error) error { 202 | var order binary.ByteOrder = binary.BigEndian 203 | var s sym 204 | p := data 205 | for len(p) >= 4 { 206 | // Symbol type, value. 207 | if len(p) < ptrsz { 208 | return &formatError{len(data), "unexpected EOF", nil} 209 | } 210 | // fixed-width value 211 | if ptrsz == 8 { 212 | s.value = order.Uint64(p[0:8]) 213 | p = p[8:] 214 | } else { 215 | s.value = uint64(order.Uint32(p[0:4])) 216 | p = p[4:] 217 | } 218 | 219 | var typ byte 220 | typ = p[0] & 0x7F 221 | s.typ = typ 222 | p = p[1:] 223 | 224 | // Name. 225 | var i int 226 | var nnul int 227 | for i = 0; i < len(p); i++ { 228 | if p[i] == 0 { 229 | nnul = 1 230 | break 231 | } 232 | } 233 | switch typ { 234 | case 'z', 'Z': 235 | p = p[i+nnul:] 236 | for i = 0; i+2 <= len(p); i += 2 { 237 | if p[i] == 0 && p[i+1] == 0 { 238 | nnul = 2 239 | break 240 | } 241 | } 242 | } 243 | if len(p) < i+nnul { 244 | return &formatError{len(data), "unexpected EOF", nil} 245 | } 246 | s.name = p[0:i] 247 | i += nnul 248 | p = p[i:] 249 | 250 | fn(s) 251 | } 252 | return nil 253 | } 254 | 255 | // NewTable decodes the Go symbol table in data, 256 | // returning an in-memory representation. 257 | func newTable(symtab []byte, ptrsz int) ([]Sym, error) { 258 | var n int 259 | err := walksymtab(symtab, ptrsz, func(s sym) error { 260 | n++ 261 | return nil 262 | }) 263 | if err != nil { 264 | return nil, err 265 | } 266 | 267 | fname := make(map[uint16]string) 268 | syms := make([]Sym, 0, n) 269 | err = walksymtab(symtab, ptrsz, func(s sym) error { 270 | n := len(syms) 271 | syms = syms[0 : n+1] 272 | ts := &syms[n] 273 | ts.Type = rune(s.typ) 274 | ts.Value = s.value 275 | switch s.typ { 276 | default: 277 | ts.Name = string(s.name) 278 | case 'z', 'Z': 279 | for i := 0; i < len(s.name); i += 2 { 280 | eltIdx := binary.BigEndian.Uint16(s.name[i : i+2]) 281 | elt, ok := fname[eltIdx] 282 | if !ok { 283 | return &formatError{-1, "bad filename code", eltIdx} 284 | } 285 | if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' { 286 | ts.Name += "/" 287 | } 288 | ts.Name += elt 289 | } 290 | } 291 | switch s.typ { 292 | case 'f': 293 | fname[uint16(s.value)] = ts.Name 294 | } 295 | return nil 296 | }) 297 | if err != nil { 298 | return nil, err 299 | } 300 | 301 | return syms, nil 302 | } 303 | 304 | // Symbols returns the symbol table for f. 305 | func (f *File) Symbols() ([]Sym, error) { 306 | symtabSection := f.Section("syms") 307 | if symtabSection == nil { 308 | return nil, errors.New("no symbol section") 309 | } 310 | 311 | symtab, err := symtabSection.Data() 312 | if err != nil { 313 | return nil, errors.New("cannot load symbol section") 314 | } 315 | 316 | return newTable(symtab, f.PtrSize) 317 | } 318 | 319 | // Section returns a section with the given name, or nil if no such 320 | // section exists. 321 | func (f *File) Section(name string) *Section { 322 | for _, s := range f.Sections { 323 | if s.Name == name { 324 | return s 325 | } 326 | } 327 | return nil 328 | } 329 | -------------------------------------------------------------------------------- /plan9obj/file_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package plan9obj 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | ) 11 | 12 | type fileTest struct { 13 | file string 14 | hdr FileHeader 15 | sections []*SectionHeader 16 | } 17 | 18 | var fileTests = []fileTest{ 19 | { 20 | "testdata/386-plan9-exec", 21 | FileHeader{Magic386, 0x324, 0x14, 4, 0x1000, 32}, 22 | []*SectionHeader{ 23 | {"text", 0x4c5f, 0x20}, 24 | {"data", 0x94c, 0x4c7f}, 25 | {"syms", 0x2c2b, 0x55cb}, 26 | {"spsz", 0x0, 0x81f6}, 27 | {"pcsz", 0xf7a, 0x81f6}, 28 | }, 29 | }, 30 | { 31 | "testdata/amd64-plan9-exec", 32 | FileHeader{MagicAMD64, 0x618, 0x13, 8, 0x200000, 40}, 33 | []*SectionHeader{ 34 | {"text", 0x4213, 0x28}, 35 | {"data", 0xa80, 0x423b}, 36 | {"syms", 0x2c8c, 0x4cbb}, 37 | {"spsz", 0x0, 0x7947}, 38 | {"pcsz", 0xca0, 0x7947}, 39 | }, 40 | }, 41 | } 42 | 43 | func TestOpen(t *testing.T) { 44 | for i := range fileTests { 45 | tt := &fileTests[i] 46 | 47 | f, err := Open(tt.file) 48 | if err != nil { 49 | t.Error(err) 50 | continue 51 | } 52 | if !reflect.DeepEqual(f.FileHeader, tt.hdr) { 53 | t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr) 54 | continue 55 | } 56 | 57 | for i, sh := range f.Sections { 58 | if i >= len(tt.sections) { 59 | break 60 | } 61 | have := &sh.SectionHeader 62 | want := tt.sections[i] 63 | if !reflect.DeepEqual(have, want) { 64 | t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want) 65 | } 66 | } 67 | tn := len(tt.sections) 68 | fn := len(f.Sections) 69 | if tn != fn { 70 | t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn) 71 | } 72 | } 73 | } 74 | 75 | func TestOpenFailure(t *testing.T) { 76 | filename := "file.go" // not a Plan 9 a.out file 77 | _, err := Open(filename) // don't crash 78 | if err == nil { 79 | t.Errorf("open %s: succeeded unexpectedly", filename) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /plan9obj/plan9obj.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | * Plan 9 a.out constants and data structures 7 | */ 8 | 9 | package plan9obj 10 | 11 | // Plan 9 Program header. 12 | type prog struct { 13 | Magic uint32 /* magic number */ 14 | Text uint32 /* size of text segment */ 15 | Data uint32 /* size of initialized data */ 16 | Bss uint32 /* size of uninitialized data */ 17 | Syms uint32 /* size of symbol table */ 18 | Entry uint32 /* entry point */ 19 | Spsz uint32 /* size of pc/sp offset table */ 20 | Pcsz uint32 /* size of pc/line number table */ 21 | } 22 | 23 | // Plan 9 symbol table entries. 24 | type sym struct { 25 | value uint64 26 | typ byte 27 | name []byte 28 | } 29 | 30 | const ( 31 | Magic64 = 0x8000 // 64-bit expanded header 32 | 33 | Magic386 = (4*11+0)*11 + 7 34 | MagicAMD64 = (4*26+0)*26 + 7 + Magic64 35 | MagicARM = (4*20+0)*20 + 7 36 | ) 37 | -------------------------------------------------------------------------------- /plan9obj/testdata/386-plan9-exec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/plan9obj/testdata/386-plan9-exec -------------------------------------------------------------------------------- /plan9obj/testdata/amd64-plan9-exec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binject/debug/26db73212a7af7e35ad01ad63061fbc6503ca9d4/plan9obj/testdata/amd64-plan9-exec -------------------------------------------------------------------------------- /plan9obj/testdata/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void 5 | main(void) 6 | { 7 | print("hello, world\n"); 8 | } 9 | --------------------------------------------------------------------------------