github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/xcoff/ar.go (about) 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 xcoff 6 7 import ( 8 "encoding/binary" 9 "fmt" 10 "io" 11 "os" 12 "strconv" 13 "strings" 14 ) 15 16 const ( 17 SAIAMAG = 0x8 18 AIAFMAG = "`\n" 19 AIAMAG = "<aiaff>\n" 20 AIAMAGBIG = "<bigaf>\n" 21 22 // Sizeof 23 FL_HSZ_BIG = 0x80 24 AR_HSZ_BIG = 0x70 25 ) 26 27 type bigarFileHeader struct { 28 Flmagic [SAIAMAG]byte // Archive magic string 29 Flmemoff [20]byte // Member table offset 30 Flgstoff [20]byte // 32-bits global symtab offset 31 Flgst64off [20]byte // 64-bits global symtab offset 32 Flfstmoff [20]byte // First member offset 33 Fllstmoff [20]byte // Last member offset 34 Flfreeoff [20]byte // First member on free list offset 35 } 36 37 type bigarMemberHeader struct { 38 Arsize [20]byte // File member size 39 Arnxtmem [20]byte // Next member pointer 40 Arprvmem [20]byte // Previous member pointer 41 Ardate [12]byte // File member date 42 Aruid [12]byte // File member uid 43 Argid [12]byte // File member gid 44 Armode [12]byte // File member mode (octal) 45 Arnamlen [4]byte // File member name length 46 // _ar_nam is removed because it's easier to get name without it. 47 } 48 49 // Archive represents an open AIX big archive. 50 type Archive struct { 51 ArchiveHeader 52 Members []*Member 53 54 closer io.Closer 55 } 56 57 // MemberHeader holds information about a big archive file header 58 type ArchiveHeader struct { 59 magic string 60 } 61 62 // Member represents a member of an AIX big archive. 63 type Member struct { 64 MemberHeader 65 sr *io.SectionReader 66 } 67 68 // MemberHeader holds information about a big archive member 69 type MemberHeader struct { 70 Name string 71 Size uint64 72 } 73 74 // OpenArchive opens the named archive using os.Open and prepares it for use 75 // as an AIX big archive. 76 func OpenArchive(name string) (*Archive, error) { 77 f, err := os.Open(name) 78 if err != nil { 79 return nil, err 80 } 81 arch, err := NewArchive(f) 82 if err != nil { 83 f.Close() 84 return nil, err 85 } 86 arch.closer = f 87 return arch, nil 88 } 89 90 // Close closes the Archive. 91 // If the Archive was created using NewArchive directly instead of OpenArchive, 92 // Close has no effect. 93 func (a *Archive) Close() error { 94 var err error 95 if a.closer != nil { 96 err = a.closer.Close() 97 a.closer = nil 98 } 99 return err 100 } 101 102 // NewArchive creates a new Archive for accessing an AIX big archive in an underlying reader. 103 func NewArchive(r io.ReaderAt) (*Archive, error) { 104 parseDecimalBytes := func(b []byte) (int64, error) { 105 return strconv.ParseInt(strings.TrimSpace(string(b)), 10, 64) 106 } 107 sr := io.NewSectionReader(r, 0, 1<<63-1) 108 109 // Read File Header 110 var magic [SAIAMAG]byte 111 if _, err := sr.ReadAt(magic[:], 0); err != nil { 112 return nil, err 113 } 114 115 arch := new(Archive) 116 switch string(magic[:]) { 117 case AIAMAGBIG: 118 arch.magic = string(magic[:]) 119 case AIAMAG: 120 return nil, fmt.Errorf("small AIX archive not supported") 121 default: 122 return nil, fmt.Errorf("unrecognised archive magic: 0x%x", magic) 123 } 124 125 var fhdr bigarFileHeader 126 if _, err := sr.Seek(0, io.SeekStart); err != nil { 127 return nil, err 128 } 129 if err := binary.Read(sr, binary.BigEndian, &fhdr); err != nil { 130 return nil, err 131 } 132 133 off, err := parseDecimalBytes(fhdr.Flfstmoff[:]) 134 if err != nil { 135 return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err) 136 } 137 138 if off == 0 { 139 // Occurs if the archive is empty. 140 return arch, nil 141 } 142 143 lastoff, err := parseDecimalBytes(fhdr.Fllstmoff[:]) 144 if err != nil { 145 return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err) 146 } 147 148 // Read members 149 for { 150 // Read Member Header 151 // The member header is normally 2 bytes larger. But it's easier 152 // to read the name if the header is read without _ar_nam. 153 // However, AIAFMAG must be read afterward. 154 if _, err := sr.Seek(off, io.SeekStart); err != nil { 155 return nil, err 156 } 157 158 var mhdr bigarMemberHeader 159 if err := binary.Read(sr, binary.BigEndian, &mhdr); err != nil { 160 return nil, err 161 } 162 163 member := new(Member) 164 arch.Members = append(arch.Members, member) 165 166 size, err := parseDecimalBytes(mhdr.Arsize[:]) 167 if err != nil { 168 return nil, fmt.Errorf("error parsing size in member header(%q); %v", mhdr, err) 169 } 170 member.Size = uint64(size) 171 172 // Read name 173 namlen, err := parseDecimalBytes(mhdr.Arnamlen[:]) 174 if err != nil { 175 return nil, fmt.Errorf("error parsing name length in member header(%q); %v", mhdr, err) 176 } 177 name := make([]byte, namlen) 178 if err := binary.Read(sr, binary.BigEndian, name); err != nil { 179 return nil, err 180 } 181 member.Name = string(name) 182 183 fileoff := off + AR_HSZ_BIG + namlen 184 if fileoff&1 != 0 { 185 fileoff++ 186 if _, err := sr.Seek(1, io.SeekCurrent); err != nil { 187 return nil, err 188 } 189 } 190 191 // Read AIAFMAG string 192 var fmag [2]byte 193 if err := binary.Read(sr, binary.BigEndian, &fmag); err != nil { 194 return nil, err 195 } 196 if string(fmag[:]) != AIAFMAG { 197 return nil, fmt.Errorf("AIAFMAG not found after member header") 198 } 199 200 fileoff += 2 // Add the two bytes of AIAFMAG 201 member.sr = io.NewSectionReader(sr, fileoff, size) 202 203 if off == lastoff { 204 break 205 } 206 off, err = parseDecimalBytes(mhdr.Arnxtmem[:]) 207 if err != nil { 208 return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err) 209 } 210 211 } 212 213 return arch, nil 214 } 215 216 // GetFile returns the XCOFF file defined by member name. 217 // FIXME: This doesn't work if an archive has two members with the same 218 // name which can occur if an archive has both 32-bits and 64-bits files. 219 func (arch *Archive) GetFile(name string) (*File, error) { 220 for _, mem := range arch.Members { 221 if mem.Name == name { 222 return NewFile(mem.sr) 223 } 224 } 225 return nil, fmt.Errorf("unknown member %s in archive", name) 226 }