github.com/v2fly/tools@v0.100.0/go/internal/gccgoimporter/ar.go (about) 1 // Copyright 2018 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 // Except for this comment, this file is a verbatim copy of the file 6 // with the same name in $GOROOT/src/go/internal/gccgoimporter. 7 8 package gccgoimporter 9 10 import ( 11 "bytes" 12 "debug/elf" 13 "errors" 14 "fmt" 15 "io" 16 "strconv" 17 "strings" 18 ) 19 20 // Magic strings for different archive file formats. 21 const ( 22 armag = "!<arch>\n" 23 armagt = "!<thin>\n" 24 armagb = "<bigaf>\n" 25 ) 26 27 // Offsets and sizes for fields in a standard archive header. 28 const ( 29 arNameOff = 0 30 arNameSize = 16 31 arDateOff = arNameOff + arNameSize 32 arDateSize = 12 33 arUIDOff = arDateOff + arDateSize 34 arUIDSize = 6 35 arGIDOff = arUIDOff + arUIDSize 36 arGIDSize = 6 37 arModeOff = arGIDOff + arGIDSize 38 arModeSize = 8 39 arSizeOff = arModeOff + arModeSize 40 arSizeSize = 10 41 arFmagOff = arSizeOff + arSizeSize 42 arFmagSize = 2 43 44 arHdrSize = arFmagOff + arFmagSize 45 ) 46 47 // The contents of the fmag field of a standard archive header. 48 const arfmag = "`\n" 49 50 // arExportData takes an archive file and returns a ReadSeeker for the 51 // export data in that file. This assumes that there is only one 52 // object in the archive containing export data, which is not quite 53 // what gccgo does; gccgo concatenates together all the export data 54 // for all the objects in the file. In practice that case does not arise. 55 func arExportData(archive io.ReadSeeker) (io.ReadSeeker, error) { 56 if _, err := archive.Seek(0, io.SeekStart); err != nil { 57 return nil, err 58 } 59 60 var buf [len(armag)]byte 61 if _, err := archive.Read(buf[:]); err != nil { 62 return nil, err 63 } 64 65 switch string(buf[:]) { 66 case armag: 67 return standardArExportData(archive) 68 case armagt: 69 return nil, errors.New("unsupported thin archive") 70 case armagb: 71 return nil, errors.New("unsupported AIX big archive") 72 default: 73 return nil, fmt.Errorf("unrecognized archive file format %q", buf[:]) 74 } 75 } 76 77 // standardArExportData returns export data form a standard archive. 78 func standardArExportData(archive io.ReadSeeker) (io.ReadSeeker, error) { 79 off := int64(len(armag)) 80 for { 81 var hdrBuf [arHdrSize]byte 82 if _, err := archive.Read(hdrBuf[:]); err != nil { 83 return nil, err 84 } 85 off += arHdrSize 86 87 if !bytes.Equal(hdrBuf[arFmagOff:arFmagOff+arFmagSize], []byte(arfmag)) { 88 return nil, fmt.Errorf("archive header format header (%q)", hdrBuf[:]) 89 } 90 91 size, err := strconv.ParseInt(strings.TrimSpace(string(hdrBuf[arSizeOff:arSizeOff+arSizeSize])), 10, 64) 92 if err != nil { 93 return nil, fmt.Errorf("error parsing size in archive header (%q): %v", hdrBuf[:], err) 94 } 95 96 fn := hdrBuf[arNameOff : arNameOff+arNameSize] 97 if fn[0] == '/' && (fn[1] == ' ' || fn[1] == '/' || bytes.Equal(fn[:8], []byte("/SYM64/ "))) { 98 // Archive symbol table or extended name table, 99 // which we don't care about. 100 } else { 101 archiveAt := readerAtFromSeeker(archive) 102 ret, err := elfFromAr(io.NewSectionReader(archiveAt, off, size)) 103 if ret != nil || err != nil { 104 return ret, err 105 } 106 } 107 108 if size&1 != 0 { 109 size++ 110 } 111 off += size 112 if _, err := archive.Seek(off, io.SeekStart); err != nil { 113 return nil, err 114 } 115 } 116 } 117 118 // elfFromAr tries to get export data from an archive member as an ELF file. 119 // If there is no export data, this returns nil, nil. 120 func elfFromAr(member *io.SectionReader) (io.ReadSeeker, error) { 121 ef, err := elf.NewFile(member) 122 if err != nil { 123 return nil, err 124 } 125 sec := ef.Section(".go_export") 126 if sec == nil { 127 return nil, nil 128 } 129 return sec.Open(), nil 130 } 131 132 // readerAtFromSeeker turns an io.ReadSeeker into an io.ReaderAt. 133 // This is only safe because there won't be any concurrent seeks 134 // while this code is executing. 135 func readerAtFromSeeker(rs io.ReadSeeker) io.ReaderAt { 136 if ret, ok := rs.(io.ReaderAt); ok { 137 return ret 138 } 139 return seekerReadAt{rs} 140 } 141 142 type seekerReadAt struct { 143 seeker io.ReadSeeker 144 } 145 146 func (sra seekerReadAt) ReadAt(p []byte, off int64) (int, error) { 147 if _, err := sra.seeker.Seek(off, io.SeekStart); err != nil { 148 return 0, err 149 } 150 return sra.seeker.Read(p) 151 }