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 | } |
152 |
Members