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