]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: set the ELF headers of ARM executables that use cgo correctly
authorMichael Hudson-Doyle <michael.hudson@canonical.com>
Wed, 23 Sep 2015 03:46:00 +0000 (15:46 +1200)
committerMichael Hudson-Doyle <michael.hudson@canonical.com>
Tue, 6 Oct 2015 07:05:51 +0000 (07:05 +0000)
It is generally expected that the ELF flags of a dynamically linked executable
and the libraries it links against match. Go's linker currently always produces
executables with flags that do not declare a float abi (hard, soft) at all, but
when cgo is involved it is unlikely that this matches the system libraries
being linked against -- really the decision about ABI is made by the C compiler
during the invocation of cgo.

This change is basically a port of the code from binutils that parses the
".ARM.attributes" section to check for the tag that declares that the code is
built for the hard-float ABI.

Fixes #7094

Change-Id: I737c8f3b5ed4af545cfc3e86722d03eb83083402
Reviewed-on: https://go-review.googlesource.com/14860
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Minux Ma <minux@golang.org>
src/cmd/link/internal/ld/elf.go
src/cmd/link/internal/ld/ldelf.go

index 8287d2714e3bd1a82fef589996c336e6a7367ff7..19865a15bf2600a7639cf47e95462ba8576a24b8 100644 (file)
@@ -206,6 +206,7 @@ const (
        SHT_GNU_VERNEED      = 0x6ffffffe
        SHT_GNU_VERSYM       = 0x6fffffff
        SHT_LOPROC           = 0x70000000
+       SHT_ARM_ATTRIBUTES   = 0x70000003
        SHT_HIPROC           = 0x7fffffff
        SHT_LOUSER           = 0x80000000
        SHT_HIUSER           = 0xffffffff
@@ -776,11 +777,19 @@ func Elfinit() {
                ehdr.phentsize = ELF64PHDRSIZE /* Must be ELF64PHDRSIZE */
                ehdr.shentsize = ELF64SHDRSIZE /* Must be ELF64SHDRSIZE */
 
-               // we use EABI on both linux/arm and freebsd/arm.
+       // we use EABI on both linux/arm and freebsd/arm.
        // 32-bit architectures
        case '5':
                // we use EABI on both linux/arm and freebsd/arm.
                if HEADTYPE == obj.Hlinux || HEADTYPE == obj.Hfreebsd {
+                       // We set a value here that makes no indication of which
+                       // float ABI the object uses, because this is information
+                       // used by the dynamic linker to compare executables and
+                       // shared libraries -- so it only matters for cgo calls, and
+                       // the information properly comes from the object files
+                       // produced by the host C compiler. parseArmAttributes in
+                       // ldelf.go reads that information and updates this field as
+                       // appropriate.
                        ehdr.flags = 0x5000002 // has entry point, Version5 EABI
                }
                fallthrough
index 3efdb75b89fb048367f636aba3245227ecf71560..20e23117ad00c7b74bc47671cda5a77dbbf470b6 100644 (file)
@@ -5,6 +5,7 @@ import (
        "cmd/internal/obj"
        "encoding/binary"
        "fmt"
+       "io"
        "log"
        "sort"
        "strings"
@@ -315,6 +316,135 @@ func valuecmp(a *LSym, b *LSym) int {
        return 0
 }
 
+const (
+       Tag_file                 = 1
+       Tag_CPU_name             = 4
+       Tag_CPU_raw_name         = 5
+       Tag_compatibility        = 32
+       Tag_nodefaults           = 64
+       Tag_also_compatible_with = 65
+       Tag_ABI_VFP_args         = 28
+)
+
+type elfAttribute struct {
+       tag  uint64
+       sval string
+       ival uint64
+}
+
+type elfAttributeList struct {
+       data []byte
+       err  error
+}
+
+func (a *elfAttributeList) string() string {
+       if a.err != nil {
+               return ""
+       }
+       nul := bytes.IndexByte(a.data, 0)
+       if nul < 0 {
+               a.err = io.EOF
+               return ""
+       }
+       s := string(a.data[:nul])
+       a.data = a.data[nul+1:]
+       return s
+}
+
+func (a *elfAttributeList) uleb128() uint64 {
+       if a.err != nil {
+               return 0
+       }
+       v, size := binary.Uvarint(a.data)
+       a.data = a.data[size:]
+       return v
+}
+
+// Read an elfAttribute from the list following the rules used on ARM systems.
+func (a *elfAttributeList) armAttr() elfAttribute {
+       attr := elfAttribute{tag: a.uleb128()}
+       switch {
+       case attr.tag == Tag_compatibility:
+               attr.ival = a.uleb128()
+               attr.sval = a.string()
+
+       case attr.tag == 64: // Tag_nodefaults has no argument
+
+       case attr.tag == 65: // Tag_also_compatible_with
+               // Not really, but we don't actually care about this tag.
+               attr.sval = a.string()
+
+       // Tag with string argument
+       case attr.tag == Tag_CPU_name || attr.tag == Tag_CPU_raw_name || (attr.tag >= 32 && attr.tag&1 != 0):
+               attr.sval = a.string()
+
+       default: // Tag with integer argument
+               attr.ival = a.uleb128()
+       }
+       return attr
+}
+
+func (a *elfAttributeList) done() bool {
+       if a.err != nil || len(a.data) == 0 {
+               return true
+       }
+       return false
+}
+
+// Look for the attribute that indicates the object uses the hard-float ABI (a
+// file-level attribute with tag Tag_VFP_arch and value 1). Unfortunately the
+// format used means that we have to parse all of the file-level attributes to
+// find the one we are looking for. This format is slightly documented in "ELF
+// for the ARM Architecture" but mostly this is derived from reading the source
+// to gold and readelf.
+func parseArmAttributes(e binary.ByteOrder, data []byte) {
+       // We assume the soft-float ABI unless we see a tag indicating otherwise.
+       if ehdr.flags == 0x5000002 {
+               ehdr.flags = 0x5000202
+       }
+       if data[0] != 'A' {
+               fmt.Fprintf(&Bso, ".ARM.attributes has unexpected format %c\n", data[0])
+               return
+       }
+       data = data[1:]
+       for len(data) != 0 {
+               sectionlength := e.Uint32(data)
+               sectiondata := data[4:sectionlength]
+               data = data[sectionlength:]
+
+               nulIndex := bytes.IndexByte(sectiondata, 0)
+               if nulIndex < 0 {
+                       fmt.Fprintf(&Bso, "corrupt .ARM.attributes (section name not NUL-terminated)\n")
+                       return
+               }
+               name := string(sectiondata[:nulIndex])
+               sectiondata = sectiondata[nulIndex+1:]
+
+               if name != "aeabi" {
+                       continue
+               }
+               for len(sectiondata) != 0 {
+                       subsectiontag, sz := binary.Uvarint(sectiondata)
+                       subsectionsize := e.Uint32(sectiondata[sz:])
+                       subsectiondata := sectiondata[sz+4 : subsectionsize]
+                       sectiondata = sectiondata[subsectionsize:]
+
+                       if subsectiontag == Tag_file {
+                               attrList := elfAttributeList{data: subsectiondata}
+                               for !attrList.done() {
+                                       attr := attrList.armAttr()
+                                       if attr.tag == Tag_ABI_VFP_args && attr.ival == 1 {
+                                               ehdr.flags = 0x5000402 // has entry point, Version5 EABI, hard-float ABI
+                                       }
+                               }
+                               if attrList.err != nil {
+                                       fmt.Fprintf(&Bso, "could not parse .ARM.attributes\n")
+                               }
+                       }
+               }
+       }
+}
+
 func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) {
        if Debug['v'] != 0 {
                fmt.Fprintf(&Bso, "%5.2f ldelf %s\n", obj.Cputime(), pn)
@@ -549,6 +679,12 @@ func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) {
        // create symbols for elfmapped sections
        for i := 0; uint(i) < elfobj.nsect; i++ {
                sect = &elfobj.sect[i]
+               if sect.type_ == SHT_ARM_ATTRIBUTES && sect.name == ".ARM.attributes" {
+                       if err = elfmap(elfobj, sect); err != nil {
+                               goto bad
+                       }
+                       parseArmAttributes(e, sect.base[:sect.size])
+               }
                if (sect.type_ != ElfSectProgbits && sect.type_ != ElfSectNobits) || sect.flags&ElfSectFlagAlloc == 0 {
                        continue
                }