From a845a562a0f2abd96c448a1ab3effe65d154353a Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 21 Dec 2021 10:00:23 -0800 Subject: [release-branch.go1.16] cmd/link: use SHT_INIT_ARRAY for .init_array section For #50295 Fixes #50296 Change-Id: If55ebcd5f2af724da7c9c744458a56d21a7ddde7 Reviewed-on: https://go-review.googlesource.com/c/go/+/373734 Trust: Ian Lance Taylor Reviewed-by: Cherry Mui (cherry picked from commit cfb0cc355233d4367b188b23a3bc143985a28b8c) Reviewed-on: https://go-review.googlesource.com/c/go/+/374234 Run-TryBot: Ian Lance Taylor TryBot-Result: Gopher Robot Reviewed-by: Austin Clements --- misc/cgo/testcarchive/carchive_test.go | 177 +++++++++++++++++++++++++++++++++ src/cmd/link/internal/ld/elf.go | 7 +- 2 files changed, 183 insertions(+), 1 deletion(-) diff --git a/misc/cgo/testcarchive/carchive_test.go b/misc/cgo/testcarchive/carchive_test.go index 6a5adf79ca..e81eefafdf 100644 --- a/misc/cgo/testcarchive/carchive_test.go +++ b/misc/cgo/testcarchive/carchive_test.go @@ -10,6 +10,7 @@ import ( "debug/elf" "flag" "fmt" + "io" "io/ioutil" "log" "os" @@ -17,6 +18,7 @@ import ( "path/filepath" "regexp" "runtime" + "strconv" "strings" "syscall" "testing" @@ -264,6 +266,173 @@ func checkLineComments(t *testing.T, hdrname string) { } } +// checkArchive verifies that the created library looks OK. +// We just check a couple of things now, we can add more checks as needed. +func checkArchive(t *testing.T, arname string) { + t.Helper() + + switch GOOS { + case "aix", "darwin", "ios", "windows": + // We don't have any checks for non-ELF libraries yet. + if _, err := os.Stat(arname); err != nil { + t.Errorf("archive %s does not exist: %v", arname, err) + } + default: + checkELFArchive(t, arname) + } +} + +// checkELFArchive checks an ELF archive. +func checkELFArchive(t *testing.T, arname string) { + t.Helper() + + f, err := os.Open(arname) + if err != nil { + t.Errorf("archive %s does not exist: %v", arname, err) + return + } + defer f.Close() + + // TODO(iant): put these in a shared package? But where? + const ( + magic = "!\n" + fmag = "`\n" + + namelen = 16 + datelen = 12 + uidlen = 6 + gidlen = 6 + modelen = 8 + sizelen = 10 + fmaglen = 2 + hdrlen = namelen + datelen + uidlen + gidlen + modelen + sizelen + fmaglen + ) + + type arhdr struct { + name string + date string + uid string + gid string + mode string + size string + fmag string + } + + var magbuf [len(magic)]byte + if _, err := io.ReadFull(f, magbuf[:]); err != nil { + t.Errorf("%s: archive too short", arname) + return + } + if string(magbuf[:]) != magic { + t.Errorf("%s: incorrect archive magic string %q", arname, magbuf) + } + + off := int64(len(magic)) + for { + if off&1 != 0 { + var b [1]byte + if _, err := f.Read(b[:]); err != nil { + if err == io.EOF { + break + } + t.Errorf("%s: error skipping alignment byte at %d: %v", arname, off, err) + } + off++ + } + + var hdrbuf [hdrlen]byte + if _, err := io.ReadFull(f, hdrbuf[:]); err != nil { + if err == io.EOF { + break + } + t.Errorf("%s: error reading archive header at %d: %v", arname, off, err) + return + } + + var hdr arhdr + hdrslice := hdrbuf[:] + set := func(len int, ps *string) { + *ps = string(bytes.TrimSpace(hdrslice[:len])) + hdrslice = hdrslice[len:] + } + set(namelen, &hdr.name) + set(datelen, &hdr.date) + set(uidlen, &hdr.uid) + set(gidlen, &hdr.gid) + set(modelen, &hdr.mode) + set(sizelen, &hdr.size) + hdr.fmag = string(hdrslice[:fmaglen]) + hdrslice = hdrslice[fmaglen:] + if len(hdrslice) != 0 { + t.Fatalf("internal error: len(hdrslice) == %d", len(hdrslice)) + } + + if hdr.fmag != fmag { + t.Errorf("%s: invalid fmagic value %q at %d", arname, hdr.fmag, off) + return + } + + size, err := strconv.ParseInt(hdr.size, 10, 64) + if err != nil { + t.Errorf("%s: error parsing size %q at %d: %v", arname, hdr.size, off, err) + return + } + + off += hdrlen + + switch hdr.name { + case "__.SYMDEF", "/", "/SYM64/": + // The archive symbol map. + case "//", "ARFILENAMES/": + // The extended name table. + default: + // This should be an ELF object. + checkELFArchiveObject(t, arname, off, io.NewSectionReader(f, off, size)) + } + + off += size + if _, err := f.Seek(off, os.SEEK_SET); err != nil { + t.Errorf("%s: failed to seek to %d: %v", arname, off, err) + } + } +} + +// checkELFArchiveObject checks an object in an ELF archive. +func checkELFArchiveObject(t *testing.T, arname string, off int64, obj io.ReaderAt) { + t.Helper() + + ef, err := elf.NewFile(obj) + if err != nil { + t.Errorf("%s: failed to open ELF file at %d: %v", arname, off, err) + return + } + defer ef.Close() + + // Verify section types. + for _, sec := range ef.Sections { + want := elf.SHT_NULL + switch sec.Name { + case ".text", ".data": + want = elf.SHT_PROGBITS + case ".bss": + want = elf.SHT_NOBITS + case ".symtab": + want = elf.SHT_SYMTAB + case ".strtab": + want = elf.SHT_STRTAB + case ".init_array": + want = elf.SHT_INIT_ARRAY + case ".fini_array": + want = elf.SHT_FINI_ARRAY + case ".preinit_array": + want = elf.SHT_PREINIT_ARRAY + } + if want != elf.SHT_NULL && sec.Type != want { + t.Errorf("%s: incorrect section type in elf file at %d for section %q: got %v want %v", arname, off, sec.Name, sec.Type, want) + } + } +} + func TestInstall(t *testing.T) { if !testWork { defer os.RemoveAll(filepath.Join(GOPATH, "pkg")) @@ -322,6 +491,7 @@ func TestEarlySignalHandler(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo2.h") + checkArchive(t, "libgo2.a") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a") if runtime.Compiler == "gccgo" { @@ -362,6 +532,7 @@ func TestSignalForwarding(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo2.h") + checkArchive(t, "libgo2.a") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") if runtime.Compiler == "gccgo" { @@ -412,6 +583,7 @@ func TestSignalForwardingExternal(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo2.h") + checkArchive(t, "libgo2.a") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") if runtime.Compiler == "gccgo" { @@ -529,6 +701,7 @@ func TestOsSignal(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo3.h") + checkArchive(t, "libgo3.a") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a") if runtime.Compiler == "gccgo" { @@ -566,6 +739,7 @@ func TestSigaltstack(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo4.h") + checkArchive(t, "libgo4.a") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a") if runtime.Compiler == "gccgo" { @@ -753,6 +927,7 @@ func TestSIGPROF(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo6.h") + checkArchive(t, "libgo6.a") ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a") if runtime.Compiler == "gccgo" { @@ -796,6 +971,7 @@ func TestCompileWithoutShared(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo2.h") + checkArchive(t, "libgo2.a") exe := "./testnoshared" + exeSuffix @@ -900,6 +1076,7 @@ func TestManyCalls(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo7.h") + checkArchive(t, "libgo7.a") ccArgs := append(cc, "-o", "testp7"+exeSuffix, "main7.c", "libgo7.a") if runtime.Compiler == "gccgo" { diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index f5823a8fbf..0f2ef0df43 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -993,7 +993,12 @@ func elfshbits(linkmode LinkMode, sect *sym.Section) *ElfShdr { } if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen { - sh.Type = uint32(elf.SHT_PROGBITS) + switch sect.Name { + case ".init_array": + sh.Type = uint32(elf.SHT_INIT_ARRAY) + default: + sh.Type = uint32(elf.SHT_PROGBITS) + } } else { sh.Type = uint32(elf.SHT_NOBITS) } -- cgit v1.2.3-54-g00ecf