• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5package gccgoimporter
6
7import (
8	"bytes"
9	"debug/elf"
10	"errors"
11	"fmt"
12	"internal/xcoff"
13	"io"
14	"strconv"
15	"strings"
16)
17
18// Magic strings for different archive file formats.
19const (
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.
26const (
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.
46const 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.
53func 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.
76func 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.
118func 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.
131func 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.
155func readerAtFromSeeker(rs io.ReadSeeker) io.ReaderAt {
156	if ret, ok := rs.(io.ReaderAt); ok {
157		return ret
158	}
159	return seekerReadAt{rs}
160}
161
162type seekerReadAt struct {
163	seeker io.ReadSeeker
164}
165
166func (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}
172