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