xref: /aosp_15_r20/external/boringssl/src/util/ar/ar.go (revision 8fb009dc861624b67b6cdb62ea21f0f22d0c584b)
1*8fb009dcSAndroid Build Coastguard Worker// Copyright (c) 2017, Google Inc.
2*8fb009dcSAndroid Build Coastguard Worker//
3*8fb009dcSAndroid Build Coastguard Worker// Permission to use, copy, modify, and/or distribute this software for any
4*8fb009dcSAndroid Build Coastguard Worker// purpose with or without fee is hereby granted, provided that the above
5*8fb009dcSAndroid Build Coastguard Worker// copyright notice and this permission notice appear in all copies.
6*8fb009dcSAndroid Build Coastguard Worker//
7*8fb009dcSAndroid Build Coastguard Worker// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8*8fb009dcSAndroid Build Coastguard Worker// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9*8fb009dcSAndroid Build Coastguard Worker// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10*8fb009dcSAndroid Build Coastguard Worker// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11*8fb009dcSAndroid Build Coastguard Worker// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12*8fb009dcSAndroid Build Coastguard Worker// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13*8fb009dcSAndroid Build Coastguard Worker// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14*8fb009dcSAndroid Build Coastguard Worker
15*8fb009dcSAndroid Build Coastguard Worker// ar.go contains functions for parsing .a archive files.
16*8fb009dcSAndroid Build Coastguard Worker
17*8fb009dcSAndroid Build Coastguard Workerpackage ar
18*8fb009dcSAndroid Build Coastguard Worker
19*8fb009dcSAndroid Build Coastguard Workerimport (
20*8fb009dcSAndroid Build Coastguard Worker	"bytes"
21*8fb009dcSAndroid Build Coastguard Worker	"errors"
22*8fb009dcSAndroid Build Coastguard Worker	"fmt"
23*8fb009dcSAndroid Build Coastguard Worker	"io"
24*8fb009dcSAndroid Build Coastguard Worker	"strconv"
25*8fb009dcSAndroid Build Coastguard Worker	"strings"
26*8fb009dcSAndroid Build Coastguard Worker)
27*8fb009dcSAndroid Build Coastguard Worker
28*8fb009dcSAndroid Build Coastguard Worker// ParseAR parses an archive file from r and returns a map from filename to
29*8fb009dcSAndroid Build Coastguard Worker// contents, or else an error.
30*8fb009dcSAndroid Build Coastguard Workerfunc ParseAR(r io.Reader) (map[string][]byte, error) {
31*8fb009dcSAndroid Build Coastguard Worker	// See https://en.wikipedia.org/wiki/Ar_(Unix)#File_format_details
32*8fb009dcSAndroid Build Coastguard Worker	const expectedMagic = "!<arch>\n"
33*8fb009dcSAndroid Build Coastguard Worker	var magic [len(expectedMagic)]byte
34*8fb009dcSAndroid Build Coastguard Worker	if _, err := io.ReadFull(r, magic[:]); err != nil {
35*8fb009dcSAndroid Build Coastguard Worker		return nil, err
36*8fb009dcSAndroid Build Coastguard Worker	}
37*8fb009dcSAndroid Build Coastguard Worker	if string(magic[:]) != expectedMagic {
38*8fb009dcSAndroid Build Coastguard Worker		return nil, errors.New("ar: not an archive file")
39*8fb009dcSAndroid Build Coastguard Worker	}
40*8fb009dcSAndroid Build Coastguard Worker
41*8fb009dcSAndroid Build Coastguard Worker	const filenameTableName = "//"
42*8fb009dcSAndroid Build Coastguard Worker	const symbolTableName = "/"
43*8fb009dcSAndroid Build Coastguard Worker	var longFilenameTable []byte
44*8fb009dcSAndroid Build Coastguard Worker	ret := make(map[string][]byte)
45*8fb009dcSAndroid Build Coastguard Worker
46*8fb009dcSAndroid Build Coastguard Worker	for {
47*8fb009dcSAndroid Build Coastguard Worker		var header [60]byte
48*8fb009dcSAndroid Build Coastguard Worker		if _, err := io.ReadFull(r, header[:]); err != nil {
49*8fb009dcSAndroid Build Coastguard Worker			if err == io.EOF {
50*8fb009dcSAndroid Build Coastguard Worker				break
51*8fb009dcSAndroid Build Coastguard Worker			}
52*8fb009dcSAndroid Build Coastguard Worker			return nil, errors.New("ar: error reading file header: " + err.Error())
53*8fb009dcSAndroid Build Coastguard Worker		}
54*8fb009dcSAndroid Build Coastguard Worker
55*8fb009dcSAndroid Build Coastguard Worker		name := strings.TrimRight(string(header[:16]), " ")
56*8fb009dcSAndroid Build Coastguard Worker		sizeStr := strings.TrimRight(string(header[48:58]), "\x00 ")
57*8fb009dcSAndroid Build Coastguard Worker		size, err := strconv.ParseUint(sizeStr, 10, 64)
58*8fb009dcSAndroid Build Coastguard Worker		if err != nil {
59*8fb009dcSAndroid Build Coastguard Worker			return nil, errors.New("ar: failed to parse file size: " + err.Error())
60*8fb009dcSAndroid Build Coastguard Worker		}
61*8fb009dcSAndroid Build Coastguard Worker
62*8fb009dcSAndroid Build Coastguard Worker		// File contents are padded to a multiple of two bytes
63*8fb009dcSAndroid Build Coastguard Worker		storedSize := size
64*8fb009dcSAndroid Build Coastguard Worker		if storedSize%2 == 1 {
65*8fb009dcSAndroid Build Coastguard Worker			storedSize++
66*8fb009dcSAndroid Build Coastguard Worker		}
67*8fb009dcSAndroid Build Coastguard Worker
68*8fb009dcSAndroid Build Coastguard Worker		contents := make([]byte, storedSize)
69*8fb009dcSAndroid Build Coastguard Worker		if _, err := io.ReadFull(r, contents); err != nil {
70*8fb009dcSAndroid Build Coastguard Worker			return nil, errors.New("ar: error reading file contents: " + err.Error())
71*8fb009dcSAndroid Build Coastguard Worker		}
72*8fb009dcSAndroid Build Coastguard Worker		contents = contents[:size]
73*8fb009dcSAndroid Build Coastguard Worker
74*8fb009dcSAndroid Build Coastguard Worker		switch {
75*8fb009dcSAndroid Build Coastguard Worker		case name == filenameTableName:
76*8fb009dcSAndroid Build Coastguard Worker			if longFilenameTable != nil {
77*8fb009dcSAndroid Build Coastguard Worker				return nil, errors.New("ar: two filename tables found")
78*8fb009dcSAndroid Build Coastguard Worker			}
79*8fb009dcSAndroid Build Coastguard Worker			longFilenameTable = contents
80*8fb009dcSAndroid Build Coastguard Worker			continue
81*8fb009dcSAndroid Build Coastguard Worker
82*8fb009dcSAndroid Build Coastguard Worker		case name == symbolTableName:
83*8fb009dcSAndroid Build Coastguard Worker			continue
84*8fb009dcSAndroid Build Coastguard Worker
85*8fb009dcSAndroid Build Coastguard Worker		case len(name) > 1 && name[0] == '/':
86*8fb009dcSAndroid Build Coastguard Worker			if longFilenameTable == nil {
87*8fb009dcSAndroid Build Coastguard Worker				return nil, errors.New("ar: long filename reference found before filename table")
88*8fb009dcSAndroid Build Coastguard Worker			}
89*8fb009dcSAndroid Build Coastguard Worker
90*8fb009dcSAndroid Build Coastguard Worker			// A long filename is stored as "/" followed by a
91*8fb009dcSAndroid Build Coastguard Worker			// base-10 offset in the filename table.
92*8fb009dcSAndroid Build Coastguard Worker			offset, err := strconv.ParseUint(name[1:], 10, 64)
93*8fb009dcSAndroid Build Coastguard Worker			if err != nil {
94*8fb009dcSAndroid Build Coastguard Worker				return nil, errors.New("ar: failed to parse filename offset: " + err.Error())
95*8fb009dcSAndroid Build Coastguard Worker			}
96*8fb009dcSAndroid Build Coastguard Worker			if offset > uint64((^uint(0))>>1) {
97*8fb009dcSAndroid Build Coastguard Worker				return nil, errors.New("ar: filename offset overflow")
98*8fb009dcSAndroid Build Coastguard Worker			}
99*8fb009dcSAndroid Build Coastguard Worker
100*8fb009dcSAndroid Build Coastguard Worker			if int(offset) > len(longFilenameTable) {
101*8fb009dcSAndroid Build Coastguard Worker				return nil, errors.New("ar: filename offset out of bounds")
102*8fb009dcSAndroid Build Coastguard Worker			}
103*8fb009dcSAndroid Build Coastguard Worker
104*8fb009dcSAndroid Build Coastguard Worker			filename := longFilenameTable[offset:]
105*8fb009dcSAndroid Build Coastguard Worker			// Windows terminates filenames with NUL characters,
106*8fb009dcSAndroid Build Coastguard Worker			// while sysv/GNU uses /.
107*8fb009dcSAndroid Build Coastguard Worker			if i := bytes.IndexAny(filename, "/\x00"); i < 0 {
108*8fb009dcSAndroid Build Coastguard Worker				return nil, errors.New("ar: unterminated filename in table")
109*8fb009dcSAndroid Build Coastguard Worker			} else {
110*8fb009dcSAndroid Build Coastguard Worker				filename = filename[:i]
111*8fb009dcSAndroid Build Coastguard Worker			}
112*8fb009dcSAndroid Build Coastguard Worker
113*8fb009dcSAndroid Build Coastguard Worker			name = string(filename)
114*8fb009dcSAndroid Build Coastguard Worker
115*8fb009dcSAndroid Build Coastguard Worker		default:
116*8fb009dcSAndroid Build Coastguard Worker			name = strings.TrimRight(name, "/")
117*8fb009dcSAndroid Build Coastguard Worker		}
118*8fb009dcSAndroid Build Coastguard Worker
119*8fb009dcSAndroid Build Coastguard Worker		// Post-processing for BSD:
120*8fb009dcSAndroid Build Coastguard Worker		// https://en.wikipedia.org/wiki/Ar_(Unix)#BSD_variant
121*8fb009dcSAndroid Build Coastguard Worker		//
122*8fb009dcSAndroid Build Coastguard Worker		// If the name is of the form #1/XXX, XXX identifies the length of the
123*8fb009dcSAndroid Build Coastguard Worker		// name, and the name itself is stored as a prefix of the data, possibly
124*8fb009dcSAndroid Build Coastguard Worker		// null-padded.
125*8fb009dcSAndroid Build Coastguard Worker
126*8fb009dcSAndroid Build Coastguard Worker		var namelen uint
127*8fb009dcSAndroid Build Coastguard Worker		n, err := fmt.Sscanf(name, "#1/%d", &namelen)
128*8fb009dcSAndroid Build Coastguard Worker		if err == nil && n == 1 && len(contents) >= int(namelen) {
129*8fb009dcSAndroid Build Coastguard Worker			name = string(contents[:namelen])
130*8fb009dcSAndroid Build Coastguard Worker			contents = contents[namelen:]
131*8fb009dcSAndroid Build Coastguard Worker
132*8fb009dcSAndroid Build Coastguard Worker			// Names can be null padded; find the first null (if any). Note that
133*8fb009dcSAndroid Build Coastguard Worker			// this also handles the case of a null followed by non-null
134*8fb009dcSAndroid Build Coastguard Worker			// characters. It's not clear whether those can ever show up in
135*8fb009dcSAndroid Build Coastguard Worker			// practice, but we might as well handle them in case they can show
136*8fb009dcSAndroid Build Coastguard Worker			// up.
137*8fb009dcSAndroid Build Coastguard Worker			var null int
138*8fb009dcSAndroid Build Coastguard Worker			for ; null < len(name); null++ {
139*8fb009dcSAndroid Build Coastguard Worker				if name[null] == 0 {
140*8fb009dcSAndroid Build Coastguard Worker					break
141*8fb009dcSAndroid Build Coastguard Worker				}
142*8fb009dcSAndroid Build Coastguard Worker			}
143*8fb009dcSAndroid Build Coastguard Worker			name = name[:null]
144*8fb009dcSAndroid Build Coastguard Worker		}
145*8fb009dcSAndroid Build Coastguard Worker
146*8fb009dcSAndroid Build Coastguard Worker		if name == "__.SYMDEF" || name == "__.SYMDEF SORTED" {
147*8fb009dcSAndroid Build Coastguard Worker			continue
148*8fb009dcSAndroid Build Coastguard Worker		}
149*8fb009dcSAndroid Build Coastguard Worker
150*8fb009dcSAndroid Build Coastguard Worker		ret[name] = contents
151*8fb009dcSAndroid Build Coastguard Worker	}
152*8fb009dcSAndroid Build Coastguard Worker
153*8fb009dcSAndroid Build Coastguard Worker	return ret, nil
154*8fb009dcSAndroid Build Coastguard Worker}
155