]> Cypherpunks repositories - gostls13.git/commitdiff
internal/xcoff: add big archive support
authorClément Chigot <clement.chigot@atos.net>
Mon, 10 Dec 2018 09:53:39 +0000 (10:53 +0100)
committerIan Lance Taylor <iant@golang.org>
Tue, 11 Dec 2018 16:15:10 +0000 (16:15 +0000)
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: Ian Lance Taylor <iant@golang.org>
src/internal/xcoff/ar.go [new file with mode: 0644]
src/internal/xcoff/ar_test.go [new file with mode: 0644]
src/internal/xcoff/testdata/bigar-empty [new file with mode: 0644]
src/internal/xcoff/testdata/bigar-ppc64 [new file with mode: 0644]
src/internal/xcoff/testdata/printbye.c [new file with mode: 0644]
src/internal/xcoff/testdata/printhello.c [new file with mode: 0644]

diff --git a/src/internal/xcoff/ar.go b/src/internal/xcoff/ar.go
new file mode 100644 (file)
index 0000000..0fb410f
--- /dev/null
@@ -0,0 +1,228 @@
+// 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)
+
+}
diff --git a/src/internal/xcoff/ar_test.go b/src/internal/xcoff/ar_test.go
new file mode 100644 (file)
index 0000000..03c2fd1
--- /dev/null
@@ -0,0 +1,79 @@
+// 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)
+               }
+
+       }
+}
diff --git a/src/internal/xcoff/testdata/bigar-empty b/src/internal/xcoff/testdata/bigar-empty
new file mode 100644 (file)
index 0000000..851ccc5
--- /dev/null
@@ -0,0 +1,2 @@
+<bigaf>
+0                   0                   0                   0                   0                   0                   
\ No newline at end of file
diff --git a/src/internal/xcoff/testdata/bigar-ppc64 b/src/internal/xcoff/testdata/bigar-ppc64
new file mode 100644 (file)
index 0000000..a8d4979
Binary files /dev/null and b/src/internal/xcoff/testdata/bigar-ppc64 differ
diff --git a/src/internal/xcoff/testdata/printbye.c b/src/internal/xcoff/testdata/printbye.c
new file mode 100644 (file)
index 0000000..9045079
--- /dev/null
@@ -0,0 +1,5 @@
+#include <stdio.h>
+
+void printbye(){
+       printf("Goodbye\n");
+}
diff --git a/src/internal/xcoff/testdata/printhello.c b/src/internal/xcoff/testdata/printhello.c
new file mode 100644 (file)
index 0000000..182aa09
--- /dev/null
@@ -0,0 +1,5 @@
+#include <stdio.h>
+
+void printhello(){
+       printf("Helloworld\n");
+}