mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.link] cmd: support large function alignment
This ports CL 226997 to the dev.link branch. - The assembler part and old object file writing are unchanged. - Changes to cmd/link are applied to cmd/oldlink. - Add alignment field to new object files for the new linker. Change-Id: Id00f323ae5bdd86b2709a702ee28bcaa9ba962f8 Reviewed-on: https://go-review.googlesource.com/c/go/+/227025 Reviewed-by: Than McIntosh <thanm@google.com>
This commit is contained in:
parent
8cc515ad3f
commit
d92a5a80b5
12 changed files with 144 additions and 40 deletions
|
|
@ -96,6 +96,7 @@ type Var struct {
|
||||||
type Func struct {
|
type Func struct {
|
||||||
Args int64 // size in bytes of argument frame: inputs and outputs
|
Args int64 // size in bytes of argument frame: inputs and outputs
|
||||||
Frame int64 // size in bytes of local variable frame
|
Frame int64 // size in bytes of local variable frame
|
||||||
|
Align uint32 // alignment requirement in bytes for the address of the function
|
||||||
Leaf bool // function omits save of link register (ARM)
|
Leaf bool // function omits save of link register (ARM)
|
||||||
NoSplit bool // function omits stack split prologue
|
NoSplit bool // function omits stack split prologue
|
||||||
TopFrame bool // function is the top of the call stack
|
TopFrame bool // function is the top of the call stack
|
||||||
|
|
@ -591,6 +592,7 @@ func (r *objReader) parseObject(prefix []byte) error {
|
||||||
s.Func = f
|
s.Func = f
|
||||||
f.Args = r.readInt()
|
f.Args = r.readInt()
|
||||||
f.Frame = r.readInt()
|
f.Frame = r.readInt()
|
||||||
|
f.Align = uint32(r.readInt())
|
||||||
flags := r.readInt()
|
flags := r.readInt()
|
||||||
f.Leaf = flags&(1<<0) != 0
|
f.Leaf = flags&(1<<0) != 0
|
||||||
f.TopFrame = flags&(1<<4) != 0
|
f.TopFrame = flags&(1<<4) != 0
|
||||||
|
|
|
||||||
|
|
@ -186,11 +186,12 @@ func (h *Header) Size() int {
|
||||||
|
|
||||||
// Symbol definition.
|
// Symbol definition.
|
||||||
type Sym struct {
|
type Sym struct {
|
||||||
Name string
|
Name string
|
||||||
ABI uint16
|
ABI uint16
|
||||||
Type uint8
|
Type uint8
|
||||||
Flag uint8
|
Flag uint8
|
||||||
Siz uint32
|
Siz uint32
|
||||||
|
Align uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
const SymABIstatic = ^uint16(0)
|
const SymABIstatic = ^uint16(0)
|
||||||
|
|
@ -216,9 +217,10 @@ func (s *Sym) Write(w *Writer) {
|
||||||
w.Uint8(s.Type)
|
w.Uint8(s.Type)
|
||||||
w.Uint8(s.Flag)
|
w.Uint8(s.Flag)
|
||||||
w.Uint32(s.Siz)
|
w.Uint32(s.Siz)
|
||||||
|
w.Uint32(s.Align)
|
||||||
}
|
}
|
||||||
|
|
||||||
const SymSize = stringRefSize + 2 + 1 + 1 + 4
|
const SymSize = stringRefSize + 2 + 1 + 1 + 4 + 4
|
||||||
|
|
||||||
type Sym2 [SymSize]byte
|
type Sym2 [SymSize]byte
|
||||||
|
|
||||||
|
|
@ -228,10 +230,11 @@ func (s *Sym2) Name(r *Reader) string {
|
||||||
return r.StringAt(off, len)
|
return r.StringAt(off, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sym2) ABI() uint16 { return binary.LittleEndian.Uint16(s[8:]) }
|
func (s *Sym2) ABI() uint16 { return binary.LittleEndian.Uint16(s[8:]) }
|
||||||
func (s *Sym2) Type() uint8 { return s[10] }
|
func (s *Sym2) Type() uint8 { return s[10] }
|
||||||
func (s *Sym2) Flag() uint8 { return s[11] }
|
func (s *Sym2) Flag() uint8 { return s[11] }
|
||||||
func (s *Sym2) Siz() uint32 { return binary.LittleEndian.Uint32(s[12:]) }
|
func (s *Sym2) Siz() uint32 { return binary.LittleEndian.Uint32(s[12:]) }
|
||||||
|
func (s *Sym2) Align() uint32 { return binary.LittleEndian.Uint32(s[16:]) }
|
||||||
|
|
||||||
func (s *Sym2) Dupok() bool { return s.Flag()&SymFlagDupok != 0 }
|
func (s *Sym2) Dupok() bool { return s.Flag()&SymFlagDupok != 0 }
|
||||||
func (s *Sym2) Local() bool { return s.Flag()&SymFlagLocal != 0 }
|
func (s *Sym2) Local() bool { return s.Flag()&SymFlagLocal != 0 }
|
||||||
|
|
|
||||||
|
|
@ -886,25 +886,10 @@ const OP_NOOP = 0xd503201f
|
||||||
|
|
||||||
// align code to a certain length by padding bytes.
|
// align code to a certain length by padding bytes.
|
||||||
func pcAlignPadLength(pc int64, alignedValue int64, ctxt *obj.Link) int {
|
func pcAlignPadLength(pc int64, alignedValue int64, ctxt *obj.Link) int {
|
||||||
switch alignedValue {
|
if !((alignedValue&(alignedValue-1) == 0) && 8 <= alignedValue && alignedValue <= 2048) {
|
||||||
case 8:
|
ctxt.Diag("alignment value of an instruction must be a power of two and in the range [8, 2048], got %d\n", alignedValue)
|
||||||
if pc%8 == 4 {
|
|
||||||
return 4
|
|
||||||
}
|
|
||||||
case 16:
|
|
||||||
switch pc % 16 {
|
|
||||||
case 4:
|
|
||||||
return 12
|
|
||||||
case 8:
|
|
||||||
return 8
|
|
||||||
case 12:
|
|
||||||
return 4
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
ctxt.Diag("Unexpected alignment: %d for PCALIGN directive\n", alignedValue)
|
|
||||||
}
|
}
|
||||||
|
return int(-pc & (alignedValue - 1))
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||||
|
|
@ -940,8 +925,12 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||||
if m == 0 {
|
if m == 0 {
|
||||||
switch p.As {
|
switch p.As {
|
||||||
case obj.APCALIGN:
|
case obj.APCALIGN:
|
||||||
a := p.From.Offset
|
alignedValue := p.From.Offset
|
||||||
m = pcAlignPadLength(pc, a, ctxt)
|
m = pcAlignPadLength(pc, alignedValue, ctxt)
|
||||||
|
// Update the current text symbol alignment value.
|
||||||
|
if int32(alignedValue) > cursym.Func.Align {
|
||||||
|
cursym.Func.Align = int32(alignedValue)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
|
case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
|
||||||
continue
|
continue
|
||||||
|
|
@ -1017,8 +1006,8 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||||
if m == 0 {
|
if m == 0 {
|
||||||
switch p.As {
|
switch p.As {
|
||||||
case obj.APCALIGN:
|
case obj.APCALIGN:
|
||||||
a := p.From.Offset
|
alignedValue := p.From.Offset
|
||||||
m = pcAlignPadLength(pc, a, ctxt)
|
m = pcAlignPadLength(pc, alignedValue, ctxt)
|
||||||
break
|
break
|
||||||
case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
|
case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@ import (
|
||||||
|
|
||||||
// TestLarge generates a very large file to verify that large
|
// TestLarge generates a very large file to verify that large
|
||||||
// program builds successfully, in particular, too-far
|
// program builds successfully, in particular, too-far
|
||||||
// conditional branches are fixed.
|
// conditional branches are fixed, and also verify that the
|
||||||
|
// instruction's pc can be correctly aligned even when branches
|
||||||
|
// need to be fixed.
|
||||||
func TestLarge(t *testing.T) {
|
func TestLarge(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("Skip in short mode")
|
t.Skip("Skip in short mode")
|
||||||
|
|
@ -41,10 +43,27 @@ func TestLarge(t *testing.T) {
|
||||||
t.Fatalf("can't write output: %v\n", err)
|
t.Fatalf("can't write output: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// build generated file
|
pattern := `0x0080\s00128\s\(.*\)\tMOVD\t\$3,\sR3`
|
||||||
cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
|
|
||||||
|
// assemble generated file
|
||||||
|
cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-S", "-o", filepath.Join(dir, "test.o"), tmpfile)
|
||||||
cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
|
cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Assemble failed: %v, output: %s", err, out)
|
||||||
|
}
|
||||||
|
matched, err := regexp.MatchString(pattern, string(out))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
t.Errorf("The alignment is not correct: %t, output:%s\n", matched, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// build generated file
|
||||||
|
cmd = exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
|
||||||
|
cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
|
||||||
|
out, err = cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Build failed: %v, output: %s", err, out)
|
t.Errorf("Build failed: %v, output: %s", err, out)
|
||||||
}
|
}
|
||||||
|
|
@ -56,6 +75,8 @@ func gen(buf *bytes.Buffer) {
|
||||||
fmt.Fprintln(buf, "TBZ $5, R0, label")
|
fmt.Fprintln(buf, "TBZ $5, R0, label")
|
||||||
fmt.Fprintln(buf, "CBZ R0, label")
|
fmt.Fprintln(buf, "CBZ R0, label")
|
||||||
fmt.Fprintln(buf, "BEQ label")
|
fmt.Fprintln(buf, "BEQ label")
|
||||||
|
fmt.Fprintln(buf, "PCALIGN $128")
|
||||||
|
fmt.Fprintln(buf, "MOVD $3, R3")
|
||||||
for i := 0; i < 1<<19; i++ {
|
for i := 0; i < 1<<19; i++ {
|
||||||
fmt.Fprintln(buf, "MOVD R0, R1")
|
fmt.Fprintln(buf, "MOVD R0, R1")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -398,6 +398,7 @@ type LSym struct {
|
||||||
type FuncInfo struct {
|
type FuncInfo struct {
|
||||||
Args int32
|
Args int32
|
||||||
Locals int32
|
Locals int32
|
||||||
|
Align int32
|
||||||
Text *Prog
|
Text *Prog
|
||||||
Autot map[*LSym]struct{}
|
Autot map[*LSym]struct{}
|
||||||
Pcln Pcln
|
Pcln Pcln
|
||||||
|
|
|
||||||
|
|
@ -350,6 +350,7 @@ func (w *objWriter) writeSym(s *LSym) {
|
||||||
|
|
||||||
w.writeInt(int64(s.Func.Args))
|
w.writeInt(int64(s.Func.Args))
|
||||||
w.writeInt(int64(s.Func.Locals))
|
w.writeInt(int64(s.Func.Locals))
|
||||||
|
w.writeInt(int64(s.Func.Align))
|
||||||
w.writeBool(s.NoSplit())
|
w.writeBool(s.NoSplit())
|
||||||
flags = int64(0)
|
flags = int64(0)
|
||||||
if s.Leaf() {
|
if s.Leaf() {
|
||||||
|
|
|
||||||
|
|
@ -244,12 +244,17 @@ func (w *writer) Sym(s *LSym) {
|
||||||
if strings.HasPrefix(name, "gofile..") {
|
if strings.HasPrefix(name, "gofile..") {
|
||||||
name = filepath.ToSlash(name)
|
name = filepath.ToSlash(name)
|
||||||
}
|
}
|
||||||
|
var align uint32
|
||||||
|
if s.Func != nil {
|
||||||
|
align = uint32(s.Func.Align)
|
||||||
|
}
|
||||||
o := goobj2.Sym{
|
o := goobj2.Sym{
|
||||||
Name: name,
|
Name: name,
|
||||||
ABI: abi,
|
ABI: abi,
|
||||||
Type: uint8(s.Type),
|
Type: uint8(s.Type),
|
||||||
Flag: flag,
|
Flag: flag,
|
||||||
Siz: uint32(s.Size),
|
Siz: uint32(s.Size),
|
||||||
|
Align: align,
|
||||||
}
|
}
|
||||||
o.Write(w.Writer)
|
o.Write(w.Writer)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2256,6 +2256,10 @@ func assignAddress(ctxt *Link, sect *sym.Section, n int, s *sym.Symbol, va uint6
|
||||||
funcsize = uint64(s.Size)
|
funcsize = uint64(s.Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sect.Align < s.Align {
|
||||||
|
sect.Align = s.Align
|
||||||
|
}
|
||||||
|
|
||||||
// On ppc64x a text section should not be larger than 2^26 bytes due to the size of
|
// On ppc64x a text section should not be larger than 2^26 bytes due to the size of
|
||||||
// call target offset field in the bl instruction. Splitting into smaller text
|
// call target offset field in the bl instruction. Splitting into smaller text
|
||||||
// sections smaller than this limit allows the GNU linker to modify the long calls
|
// sections smaller than this limit allows the GNU linker to modify the long calls
|
||||||
|
|
|
||||||
|
|
@ -1616,6 +1616,9 @@ func (l *Loader) preloadSyms(r *oReader, kind int) {
|
||||||
strings.HasPrefix(name, "runtime.gcbits.") {
|
strings.HasPrefix(name, "runtime.gcbits.") {
|
||||||
l.SetAttrNotInSymbolTable(gi, true)
|
l.SetAttrNotInSymbolTable(gi, true)
|
||||||
}
|
}
|
||||||
|
if a := osym.Align(); a != 0 {
|
||||||
|
l.SetSymAlign(gi, int32(a))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -471,3 +471,73 @@ func TestOldLink(t *testing.T) {
|
||||||
t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
|
t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const testFuncAlignSrc = `
|
||||||
|
package main
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
func alignPc()
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
addr := reflect.ValueOf(alignPc).Pointer()
|
||||||
|
if (addr % 512) != 0 {
|
||||||
|
fmt.Printf("expected 512 bytes alignment, got %v\n", addr)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("PASS")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const testFuncAlignAsmSrc = `
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·alignPc(SB),NOSPLIT, $0-0
|
||||||
|
MOVD $2, R0
|
||||||
|
PCALIGN $512
|
||||||
|
MOVD $3, R1
|
||||||
|
RET
|
||||||
|
`
|
||||||
|
|
||||||
|
// TestFuncAlign verifies that the address of a function can be aligned
|
||||||
|
// with a specfic value on arm64.
|
||||||
|
func TestFuncAlign(t *testing.T) {
|
||||||
|
if runtime.GOARCH != "arm64" || runtime.GOOS != "linux" {
|
||||||
|
t.Skip("skipping on non-linux/arm64 platform")
|
||||||
|
}
|
||||||
|
testenv.MustHaveGoBuild(t)
|
||||||
|
|
||||||
|
tmpdir, err := ioutil.TempDir("", "TestFuncAlign")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
|
||||||
|
src := filepath.Join(tmpdir, "falign.go")
|
||||||
|
err = ioutil.WriteFile(src, []byte(testFuncAlignSrc), 0666)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
src = filepath.Join(tmpdir, "falign.s")
|
||||||
|
err = ioutil.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build and run with old object file format.
|
||||||
|
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "falign")
|
||||||
|
cmd.Dir = tmpdir
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("build failed: %v", err)
|
||||||
|
}
|
||||||
|
cmd = exec.Command(tmpdir + "/falign")
|
||||||
|
out, err = cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to run with err %v, output: %s", err, out)
|
||||||
|
}
|
||||||
|
if string(out) != "PASS" {
|
||||||
|
t.Errorf("unexpected output: %s\n", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2119,6 +2119,10 @@ func assignAddress(ctxt *Link, sect *sym.Section, n int, s *sym.Symbol, va uint6
|
||||||
funcsize = uint64(s.Size)
|
funcsize = uint64(s.Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sect.Align < s.Align {
|
||||||
|
sect.Align = s.Align
|
||||||
|
}
|
||||||
|
|
||||||
// On ppc64x a text section should not be larger than 2^26 bytes due to the size of
|
// On ppc64x a text section should not be larger than 2^26 bytes due to the size of
|
||||||
// call target offset field in the bl instruction. Splitting into smaller text
|
// call target offset field in the bl instruction. Splitting into smaller text
|
||||||
// sections smaller than this limit allows the GNU linker to modify the long calls
|
// sections smaller than this limit allows the GNU linker to modify the long calls
|
||||||
|
|
|
||||||
|
|
@ -316,6 +316,7 @@ overwrite:
|
||||||
|
|
||||||
pc.Args = r.readInt32()
|
pc.Args = r.readInt32()
|
||||||
pc.Locals = r.readInt32()
|
pc.Locals = r.readInt32()
|
||||||
|
s.Align = r.readInt32()
|
||||||
if r.readUint8() != 0 {
|
if r.readUint8() != 0 {
|
||||||
s.Attr |= sym.AttrNoSplit
|
s.Attr |= sym.AttrNoSplit
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue