From 5061f921c7c3e66b68ad903adf57da380c327b8c Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Wed, 9 Nov 2016 12:56:38 -0500 Subject: [PATCH] go/gccgoexportdata: correctly handle archive files containing string tables If the name of an archive member is longer than 16 bytes, an ELF archive file contains a string table of file names under the name "//", and this member has no mode. Skip such members. + Test. Change-Id: Ib10db1cc42816c9002433be6176240e490678560 Reviewed-on: https://go-review.googlesource.com/32973 Reviewed-by: Ian Lance Taylor --- go/gccgoexportdata/gccgoexportdata.go | 28 ++++---- go/gccgoexportdata/gccgoexportdata_test.go | 71 +++++++++++++++------ go/gccgoexportdata/testdata/long.a | Bin 0 -> 802 bytes go/gccgoexportdata/testdata/short.a | Bin 0 -> 710 bytes 4 files changed, 68 insertions(+), 31 deletions(-) create mode 100644 go/gccgoexportdata/testdata/long.a create mode 100644 go/gccgoexportdata/testdata/short.a diff --git a/go/gccgoexportdata/gccgoexportdata.go b/go/gccgoexportdata/gccgoexportdata.go index ca5f75f0..30ed521e 100644 --- a/go/gccgoexportdata/gccgoexportdata.go +++ b/go/gccgoexportdata/gccgoexportdata.go @@ -74,17 +74,14 @@ func NewReader(r io.Reader) (io.Reader, error) { return sec.Open(), nil } -// firstSection returns the contents of the first non-empty section of the archive file. +// firstSection returns the contents of the first regular file in an ELF +// archive (http://www.sco.com/developers/devspecs/gabi41.pdf, ยง7.2). func firstSection(a []byte) ([]byte, error) { for len(a) >= 60 { var hdr []byte hdr, a = a[:60], a[60:] - modeStr := string(string(hdr[40:48])) - mode, err := strconv.Atoi(strings.TrimSpace(modeStr)) - if err != nil { - return nil, fmt.Errorf("invalid mode: %q", modeStr) - } + name := strings.TrimSpace(string(hdr[:16])) sizeStr := string(hdr[48:58]) size, err := strconv.Atoi(strings.TrimSpace(sizeStr)) @@ -92,16 +89,25 @@ func firstSection(a []byte) ([]byte, error) { return nil, fmt.Errorf("invalid size: %q", sizeStr) } - var payload []byte - payload, a = a[:size], a[size:] + if len(a) < size { + return nil, fmt.Errorf("invalid section size: %d", size) + } - if mode == 0 { - continue // skip "/" + // The payload is padded to an even number of bytes. + var payload []byte + payload, a = a[:size], a[size+size&1:] + + // Skip special files: + // "/" archive symbol table + // "/SYM64/" archive symbol table on e.g. s390x + // "//" archive string table (if any filename is >15 bytes) + if name == "/" || name == "/SYM64/" || name == "//" { + continue } return payload, nil } - return nil, fmt.Errorf("archive has no non-empty sections") + return nil, fmt.Errorf("archive has no regular sections") } // Read reads export data from in, decodes it, and returns type diff --git a/go/gccgoexportdata/gccgoexportdata_test.go b/go/gccgoexportdata/gccgoexportdata_test.go index 131f4135..1aa114ca 100644 --- a/go/gccgoexportdata/gccgoexportdata_test.go +++ b/go/gccgoexportdata/gccgoexportdata_test.go @@ -9,28 +9,59 @@ import ( ) // Test ensures this package can read gccgo export data from the -// .go_export section of an ELF file. +// .go_export from a standalone ELF file or such a file in an archive +// library. +// +// The testdata/{short,long}.a ELF archive files were produced by: +// +// $ echo 'package foo; func F()' > foo.go +// $ gccgo -c -fgo-pkgpath blah foo.go +// $ objcopy -j .go_export foo.o foo.gox +// $ ar q short.a foo.gox +// $ objcopy -j .go_export foo.o name-longer-than-16-bytes.gox +// $ ar q long.a name-longer-than-16-bytes.gox +// +// The file long.a contains an archive string table. +// +// The errors.gox file (an ELF object file) comes from the toolchain's +// standard library. func Test(t *testing.T) { - f, err := os.Open("testdata/errors.gox") - if err != nil { - t.Fatal(err) - } - defer f.Close() - r, err := gccgoexportdata.NewReader(f) - if err != nil { - t.Fatal(err) - } - imports := make(map[string]*types.Package) - pkg, err := gccgoexportdata.Read(r, nil, imports, "errors") - if err != nil { + for _, test := range []struct { + filename, path, member, wantType string + }{ + {"testdata/errors.gox", "errors", "New", "func(text string) error"}, + {"testdata/short.a", "short", "F", "func()"}, + {"testdata/long.a", "long", "F", "func()"}, + } { + t.Logf("filename = %s", test.filename) + f, err := os.Open(test.filename) + if err != nil { + t.Error(err) + continue + } + defer f.Close() + r, err := gccgoexportdata.NewReader(f) + if err != nil { + t.Error(err) + continue + } - t.Fatal(err) - } + imports := make(map[string]*types.Package) + pkg, err := gccgoexportdata.Read(r, nil, imports, test.path) + if err != nil { + t.Error(err) + continue + } - // Check type of errors.New. - got := pkg.Scope().Lookup("New").Type().String() - want := "func(text string) error" - if got != want { - t.Errorf("New.Type = %s, want %s", got, want) + // Check type of designated package member. + obj := pkg.Scope().Lookup(test.member) + if obj == nil { + t.Errorf("%s.%s not found", test.path, test.member) + continue + } + if obj.Type().String() != test.wantType { + t.Errorf("%s.%s.Type = %s, want %s", + test.path, test.member, obj.Type(), test.wantType) + } } } diff --git a/go/gccgoexportdata/testdata/long.a b/go/gccgoexportdata/testdata/long.a new file mode 100644 index 0000000000000000000000000000000000000000..c1504473118ea54cb7d2210dc94b4d5a8b3701b2 GIT binary patch literal 802 zcmbVI!ES;;5M8xR+C8_O0_5z&y%F7|GD<1CIUXq$lSdVuVw% zh_rc@#!;>niAXidwDGoxR^2$;=ossV)s+FN+YASQ63b#>5h68|5XR(TmTezr@BH_m z-D;I5?xTeU=)|fjLf@wpx%=Ejm#B?CX&)~|I1_OMlPr^D7B5ASz<4ea$?|EIPm3)` z*<_uD5Wv%OABRa4&Q|LM1Uz6vC-BLT`@Ny(S+9h92ItH%?KjisoEx4)snf@(yV@=a zF_u{52TZCg&OV~eGRupzBmbG+OJCFXyK3IFHn*<5mHkdlQ_?S0d#v^9{*t$Anv&kA fB2m2yiDl_Z@jHdr5}WqB%HOn4S^tDi)1SdVqUcy( literal 0 HcmV?d00001 diff --git a/go/gccgoexportdata/testdata/short.a b/go/gccgoexportdata/testdata/short.a new file mode 100644 index 0000000000000000000000000000000000000000..3cd756e4f77919a9505a64ae538d018c06036f41 GIT binary patch literal 710 zcmbVI!A^rf5M5eLbWeIS@iJbFUg|D|g-uO^7C8A2c1tKUwbD>(+Ec&BzwwhCaA_+c zF>wz2=FPnKcH!nx@Dg?mql~G_AVYFs^^}mWLrc7>_sA*fkPkkfOBKyk5(B;i z$vj!AB8ADDN|EKWET0uyK-qNt9zh6q_X8TGaWr487ZA!&jNC9_BN_BZzHdJ<>6=mt z*K(e%KuT%)F6ZumlHO{&DAWY8$bT@cyCi#!H_I$9jvo2X_8NU{-|y+XStGZpy~KUD wp(*L7p4~QfO@GNd4NXZe^pNS@31T@q)BH-~Ibz%Wp7yuhr>%cNx9!i+Z?M}(X#fBK literal 0 HcmV?d00001