Commit 21da8e60 authored by Clément Chigot's avatar Clément Chigot Committed by Ian Lance Taylor

internal/xcoff: add big archive support

This commit adds support to read AIX big archive inside internal/xcoff
package.

Change-Id: I4317b40824b24312a69c918dfc6438dc3aff7be7
Reviewed-on: https://go-review.googlesource.com/c/153398
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent 47a71d9d
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xcoff
import (
"encoding/binary"
"fmt"
"io"
"os"
"strconv"
"strings"
)
const (
SAIAMAG = 0x8
AIAFMAG = "`\n"
AIAMAG = "<aiaff>\n"
AIAMAGBIG = "<bigaf>\n"
// Sizeof
FL_HSZ_BIG = 0x80
AR_HSZ_BIG = 0x70
)
type bigarFileHeader struct {
Flmagic [SAIAMAG]byte // Archive magic string
Flmemoff [20]byte // Member table offset
Flgstoff [20]byte // 32-bits global symtab offset
Flgst64off [20]byte // 64-bits global symtab offset
Flfstmoff [20]byte // First member offset
Fllstmoff [20]byte // Last member offset
Flfreeoff [20]byte // First member on free list offset
}
type bigarMemberHeader struct {
Arsize [20]byte // File member size
Arnxtmem [20]byte // Next member pointer
Arprvmem [20]byte // Previous member pointer
Ardate [12]byte // File member date
Aruid [12]byte // File member uid
Argid [12]byte // File member gid
Armode [12]byte // File member mode (octal)
Arnamlen [4]byte // File member name length
// _ar_nam is removed because it's easier to get name without it.
}
// Archive represents an open AIX big archive.
type Archive struct {
ArchiveHeader
Members []*Member
closer io.Closer
}
// MemberHeader holds information about a big archive file header
type ArchiveHeader struct {
magic string
}
// Member represents a member of an AIX big archive.
type Member struct {
MemberHeader
sr *io.SectionReader
}
// MemberHeader holds information about a big archive member
type MemberHeader struct {
Name string
Size uint64
}
// OpenArchive opens the named archive using os.Open and prepares it for use
// as an AIX big archive.
func OpenArchive(name string) (*Archive, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
arch, err := NewArchive(f)
if err != nil {
f.Close()
return nil, err
}
arch.closer = f
return arch, nil
}
// Close closes the Archive.
// If the Archive was created using NewArchive directly instead of OpenArchive,
// Close has no effect.
func (a *Archive) Close() error {
var err error
if a.closer != nil {
err = a.closer.Close()
a.closer = nil
}
return err
}
// NewArchive creates a new Archive for accessing an AIX big archive in an underlying reader.
func NewArchive(r io.ReaderAt) (*Archive, error) {
parseDecimalBytes := func(b []byte) (int64, error) {
return strconv.ParseInt(strings.TrimSpace(string(b)), 10, 64)
}
sr := io.NewSectionReader(r, 0, 1<<63-1)
// Read File Header
var magic [SAIAMAG]byte
if _, err := sr.ReadAt(magic[:], 0); err != nil {
return nil, err
}
arch := new(Archive)
switch string(magic[:]) {
case AIAMAGBIG:
arch.magic = string(magic[:])
case AIAMAG:
return nil, fmt.Errorf("small AIX archive not supported")
default:
return nil, fmt.Errorf("unrecognised archive magic: 0x%x", magic)
}
var fhdr bigarFileHeader
if _, err := sr.Seek(0, os.SEEK_SET); err != nil {
return nil, err
}
if err := binary.Read(sr, binary.BigEndian, &fhdr); err != nil {
return nil, err
}
off, err := parseDecimalBytes(fhdr.Flfstmoff[:])
if err != nil {
return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err)
}
if off == 0 {
// Occurs if the archive is empty.
return arch, nil
}
lastoff, err := parseDecimalBytes(fhdr.Fllstmoff[:])
if err != nil {
return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err)
}
// Read members
for {
// Read Member Header
// The member header is normally 2 bytes larger. But it's easier
// to read the name if the header is read without _ar_nam.
// However, AIAFMAG must be read afterward.
if _, err := sr.Seek(off, os.SEEK_SET); err != nil {
return nil, err
}
var mhdr bigarMemberHeader
if err := binary.Read(sr, binary.BigEndian, &mhdr); err != nil {
return nil, err
}
member := new(Member)
arch.Members = append(arch.Members, member)
size, err := parseDecimalBytes(mhdr.Arsize[:])
if err != nil {
return nil, fmt.Errorf("error parsing size in member header(%q); %v", mhdr, err)
}
member.Size = uint64(size)
// Read name
namlen, err := parseDecimalBytes(mhdr.Arnamlen[:])
if err != nil {
return nil, fmt.Errorf("error parsing name length in member header(%q); %v", mhdr, err)
}
name := make([]byte, namlen)
if err := binary.Read(sr, binary.BigEndian, name); err != nil {
return nil, err
}
member.Name = string(name)
fileoff := off + AR_HSZ_BIG + namlen
if fileoff&1 != 0 {
fileoff++
if _, err := sr.Seek(1, os.SEEK_CUR); err != nil {
return nil, err
}
}
// Read AIAFMAG string
var fmag [2]byte
if err := binary.Read(sr, binary.BigEndian, &fmag); err != nil {
return nil, err
}
if string(fmag[:]) != AIAFMAG {
return nil, fmt.Errorf("AIAFMAG not found after member header")
}
fileoff += 2 // Add the two bytes of AIAFMAG
member.sr = io.NewSectionReader(sr, fileoff, size)
if off == lastoff {
break
}
off, err = parseDecimalBytes(mhdr.Arnxtmem[:])
if err != nil {
return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err)
}
}
return arch, nil
}
// GetFile returns the XCOFF file defined by member name.
// FIXME: This doesn't work if an archive has two members with the same
// name which can occur if a archive has both 32-bits and 64-bits files.
func (arch *Archive) GetFile(name string) (*File, error) {
for _, mem := range arch.Members {
if mem.Name == name {
return NewFile(mem.sr)
}
}
return nil, fmt.Errorf("unknown member %s in archive", name)
}
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xcoff
import (
"reflect"
"testing"
)
type archiveTest struct {
file string
hdr ArchiveHeader
members []*MemberHeader
membersFileHeader []FileHeader
}
var archTest = []archiveTest{
{
"testdata/bigar-ppc64",
ArchiveHeader{AIAMAGBIG},
[]*MemberHeader{
{"printbye.o", 836},
{"printhello.o", 860},
},
[]FileHeader{
FileHeader{U64_TOCMAGIC},
FileHeader{U64_TOCMAGIC},
},
},
{
"testdata/bigar-empty",
ArchiveHeader{AIAMAGBIG},
[]*MemberHeader{},
[]FileHeader{},
},
}
func TestOpenArchive(t *testing.T) {
for i := range archTest {
tt := &archTest[i]
arch, err := OpenArchive(tt.file)
if err != nil {
t.Error(err)
continue
}
if !reflect.DeepEqual(arch.ArchiveHeader, tt.hdr) {
t.Errorf("open archive %s:\n\thave %#v\n\twant %#v\n", tt.file, arch.ArchiveHeader, tt.hdr)
continue
}
for i, mem := range arch.Members {
if i >= len(tt.members) {
break
}
have := &mem.MemberHeader
want := tt.members[i]
if !reflect.DeepEqual(have, want) {
t.Errorf("open %s, member %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
}
f, err := arch.GetFile(mem.Name)
if err != nil {
t.Error(err)
continue
}
if !reflect.DeepEqual(f.FileHeader, tt.membersFileHeader[i]) {
t.Errorf("open %s, member file header %d:\n\thave %#v\n\twant %#v\n", tt.file, i, f.FileHeader, tt.membersFileHeader[i])
}
}
tn := len(tt.members)
an := len(arch.Members)
if tn != an {
t.Errorf("open %s: len(Members) = %d, want %d", tt.file, an, tn)
}
}
}
<bigaf>
0 0 0 0 0 0
\ No newline at end of file
#include <stdio.h>
void printbye(){
printf("Goodbye\n");
}
#include <stdio.h>
void printhello(){
printf("Helloworld\n");
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment