mirror of
https://github.com/golang/go
synced 2024-11-18 11:04:42 -07:00
cmd/link: set the ELF headers of ARM executables that use cgo correctly
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>
This commit is contained in:
parent
007ee631d6
commit
3e6334e2e0
@ -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
|
||||
@ -781,6 +782,14 @@ func Elfinit() {
|
||||
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
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user