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  }