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 "internal/xcoff"
13 "io"
14 "strconv"
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 []byte(buf[:]) {
63 case armag:
64 return standardArExportData(archive)
65 case armagt:
66 return nil, errors.New("unsupported thin archive")
67 case armagb:
68 return aixBigArExportData(archive)
69 default:
70 return nil, fmt.Errorf("unrecognized archive file format %q", buf[:])
71 }
72 }
73
74 // standardArExportData returns export data from 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.Equal(hdrBuf[arFmagOff:arFmagOff+arFmagSize], []byte(arfmag)) {
85 return nil, fmt.Errorf("archive header format header (%q)", hdrBuf[:])
86 }
87
88 size, err := strconv.ParseInt(bytes.TrimSpace([]byte(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] == '/' || []byte(fn[:8]) == "/SYM64/ ") {
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 // aixBigArExportData returns export data from an AIX big archive.
130 func aixBigArExportData(archive io.ReadSeeker) (io.ReadSeeker, error) {
131 archiveAt := readerAtFromSeeker(archive)
132 arch, err := xcoff.NewArchive(archiveAt)
133 if err != nil {
134 return nil, err
135 }
136
137 for _, mem := range arch.Members {
138 f, err := arch.GetFile(mem.Name)
139 if err != nil {
140 return nil, err
141 }
142 sdat := f.CSect(".go_export")
143 if sdat != nil {
144 return bytes.NewReader(sdat), nil
145 }
146 }
147
148 return nil, fmt.Errorf(".go_export not found in this archive")
149 }
150
151 // readerAtFromSeeker turns an io.ReadSeeker into an io.ReaderAt.
152 // This is only safe because there won't be any concurrent seeks
153 // while this code is executing.
154 func readerAtFromSeeker(rs io.ReadSeeker) io.ReaderAt {
155 if ret, ok := rs.(io.ReaderAt); ok {
156 return ret
157 }
158 return seekerReadAt{rs}
159 }
160
161 type seekerReadAt struct {
162 seeker io.ReadSeeker
163 }
164
165 func (sra seekerReadAt) ReadAt(p []byte, off int64) (int, error) {
166 if _, err := sra.seeker.Seek(off, io.SeekStart); err != nil {
167 return 0, err
168 }
169 return sra.seeker.Read(p)
170 }
171