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