Skip to content

Commit

Permalink
[release-branch.go1.16] cmd/link: use SHT_INIT_ARRAY for .init_array …
Browse files Browse the repository at this point in the history
…section

For #50295
Fixes #50296

Change-Id: If55ebcd5f2af724da7c9c744458a56d21a7ddde7
Reviewed-on: https://go-review.googlesource.com/c/go/+/373734
Trust: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
(cherry picked from commit cfb0cc3)
Reviewed-on: https://go-review.googlesource.com/c/go/+/374234
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
  • Loading branch information
ianlancetaylor authored and cagedmantis committed Jan 5, 2022
1 parent 169be8e commit a845a56
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 1 deletion.
177 changes: 177 additions & 0 deletions misc/cgo/testcarchive/carchive_test.go
Expand Up @@ -10,13 +10,15 @@ import (
"debug/elf"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"syscall"
"testing"
Expand Down Expand Up @@ -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 = "!<arch>\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"))
Expand Down Expand Up @@ -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" {
Expand Down Expand Up @@ -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" {
Expand Down Expand Up @@ -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" {
Expand Down Expand Up @@ -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" {
Expand Down Expand Up @@ -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" {
Expand Down Expand Up @@ -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" {
Expand Down Expand Up @@ -796,6 +971,7 @@ func TestCompileWithoutShared(t *testing.T) {
t.Fatal(err)
}
checkLineComments(t, "libgo2.h")
checkArchive(t, "libgo2.a")

exe := "./testnoshared" + exeSuffix

Expand Down Expand Up @@ -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" {
Expand Down
7 changes: 6 additions & 1 deletion src/cmd/link/internal/ld/elf.go
Expand Up @@ -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)
}
Expand Down

0 comments on commit a845a56

Please sign in to comment.