Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/link: ELF `e_flags` does not contain `EF_ARM_VFP_FLOAT` on armv7l when statically linked #44002

Open
staticfloat opened this issue Jan 30, 2021 · 3 comments
Milestone

Comments

@staticfloat
Copy link

@staticfloat staticfloat commented Jan 30, 2021

What version of Go are you using (go version)?

go version go1.15.7 linux/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
go env
GO111MODULE=""
GOARCH="arm"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
GOARM="7"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="0"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -marm -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build305676071=/tmp/go-build -gno-record-gcc-switches"

What did you do?

I have go code that involves vectorizeable floating point instructions. When compiling for armv7l, go correctly uses vector floating-point instructions, but does not mark the resultant executable as a 'hard float' executable:

float_abi.go source
package main

import "fmt"
import "math/rand"

func average(xs []float64) float64 {
total := 0.0
for _, v := range xs {
total += v
}
return total / float64(len(xs))
}

func main() {
rand.Seed(0x6a756c6961)
xs := make([]float64, 1000)
for n := 0; n < 1000; n++ {
xs[n] = rand.Float64()
}
fmt.Println("average: ", average(xs))
}

Compiling:

$ docker run -ti -v $(pwd):/app -w /app -e GOARCH=arm -e GOARM=7 golang go build -o float_abi float_abi.go

Inspecting ELF headers:

$ readelf -h ./float_abi
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x6e800
  Start of program headers:          52 (bytes into file)
  Start of section headers:          276 (bytes into file)
  Flags:                             0x5000002, Version5 EABI, <unknown>
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         7
  Size of section headers:           40 (bytes)
  Number of section headers:         25
  Section header string table index: 3

Disassembly of average() showcasing vector instructions:

$ arm-linux-gnueabihf-objdump -d ./float_abi | grep 'main.average>:' -A 18
0009daf8 <main.average>:
   9daf8:       e59d0008        ldr     r0, [sp, #8]
   9dafc:       e59d1004        ldr     r1, [sp, #4]
   9db00:       e3a02000        mov     r2, #0
   9db04:       eeb70b00        vmov.f64        d0, #112        ; 0x3f800000  1.0
   9db08:       ee300b40        vsub.f64        d0, d0, d0
   9db0c:       ea000003        b       9db20 <main.average+0x28>
   9db10:       e0813182        add     r3, r1, r2, lsl #3
   9db14:       ed931b00        vldr    d1, [r3]
   9db18:       e2822001        add     r2, r2, #1
   9db1c:       ee300b01        vadd.f64        d0, d0, d1
   9db20:       e1520000        cmp     r2, r0
   9db24:       bafffff9        blt     9db10 <main.average+0x18>
   9db28:       ee0f0b10        vmov.32 d15[0], r0
   9db2c:       eeb81bcf        vcvt.f64.s32    d1, s30
   9db30:       ee800b01        vdiv.f64        d0, d0, d1
   9db34:       ed8d0b04        vstr    d0, [sp, #16]
   9db38:       e28ef000        add     pc, lr, #0

Note the vadd.f64 and the use of dX registers.

What did you expect to see?

I expect to see the Flags field above to have 0x400 set, marking this executable as using the "hard float" ABI. See the definitions in glibc which correspond to the values in the table on page 16 of this pdf.

What did you see instead?

Neither the hard nor soft float ABI flags are set, which I believe denotes that this program does not use any floating point arithmetic at all, which is clearly incorrect.

@ianlancetaylor ianlancetaylor changed the title ELF `e_flags` does not contain `EF_ARM_VFP_FLOAT` on armv7l cmd/link: ELF `e_flags` does not contain `EF_ARM_VFP_FLOAT` on armv7l Jan 30, 2021
@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Jan 30, 2021

@ianlancetaylor ianlancetaylor added this to the Go1.17 milestone Jan 30, 2021
@cherrymui
Copy link
Contributor

@cherrymui cherrymui commented Feb 1, 2021

https://go.googlesource.com/go/+/refs/heads/master/src/cmd/link/internal/ld/elf.go#244

It seems this is intentional. The comment says it only matters for programs that link against C code, for which we actually set the flag. Is there any problem with that?

@staticfloat
Copy link
Author

@staticfloat staticfloat commented Feb 1, 2021

There are binary analysis tools that expect this information to be accurate. One such tool is the BinaryBuilder.jl cross-compiler suite that checks to ensure that the compilers its using are following the appropriate specs. While the kernel doesn't really care about these flags (it's just going to run the executable, and if your instructions can't run you'll just die with SIGILL) it's useful to be able to check at a glance which ABI a binary is compiled for, not from a linking perspective but from a CPU capabilities perspective.

I understand that go code is not generally linked dynamically so you don't need to worry so much about declaring your calling convention as other programs aren't expected to call into a go-generated object. I also see that if you generate a shared library, the flags are correctly set. There's still value in declaring the usage of hard-float instructions within statically linked executables so that tools searching for e.g. executables that use hardfloat instructions don't have to disassemble them but can simply scan the ELF headers.

@staticfloat staticfloat changed the title cmd/link: ELF `e_flags` does not contain `EF_ARM_VFP_FLOAT` on armv7l cmd/link: ELF `e_flags` does not contain `EF_ARM_VFP_FLOAT` on armv7l when statically linked Feb 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants