aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2017-03-29 20:53:32 -0400
committerAustin Clements <austin@google.com>2017-04-05 16:58:33 +0000
commit3ca0d34fa12abad8fc5ca8b8fabec0c1b2c0d288 (patch)
tree5630c56db37c25aef9dbae61114ccd2e94dde8d3
parent84192f2734c276baa3f2814d465eadc8eda82ce5 (diff)
downloadgo-3ca0d34fa12abad8fc5ca8b8fabec0c1b2c0d288.tar.gz
go-3ca0d34fa12abad8fc5ca8b8fabec0c1b2c0d288.zip
[release-branch.go1.8] cmd/link: make mach-o dwarf segment properly aligned
Without this, the load fails during kernel exec, which results in the mysterious and completely uninformative "Killed: 9" error. It appears that the stars (or at least the inputs) were properly aligned with earlier versions of Xcode so that this happened accidentally. Make it happen on purpose. Gregory Man bisected the breakage to this change in LLVM, which fits the theory nicely: https://github.com/llvm-mirror/llvm/commit/9a41e59c Fixes #19734. Change-Id: Ice67a09af2de29d3c0d5e3fcde6a769580897c95 Reviewed-on: https://go-review.googlesource.com/39603 Run-TryBot: Austin Clements <austin@google.com> Reviewed-by: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
-rwxr-xr-xmisc/cgo/testcshared/test.bash7
-rw-r--r--src/cmd/link/internal/ld/macho_combine_dwarf.go52
2 files changed, 41 insertions, 18 deletions
diff --git a/misc/cgo/testcshared/test.bash b/misc/cgo/testcshared/test.bash
index 052ee0e758..0315fb07f5 100755
--- a/misc/cgo/testcshared/test.bash
+++ b/misc/cgo/testcshared/test.bash
@@ -179,6 +179,13 @@ if test "$output" != "PASS"; then
status=1
fi
+if test "$libext" = "dylib"; then
+ # make sure dylibs are well-formed
+ if ! otool -l libgo*.dylib >/dev/null; then
+ status=1
+ fi
+fi
+
if test $status = 0; then
echo "ok"
fi
diff --git a/src/cmd/link/internal/ld/macho_combine_dwarf.go b/src/cmd/link/internal/ld/macho_combine_dwarf.go
index dcc371ec05..8c6c4a86ac 100644
--- a/src/cmd/link/internal/ld/macho_combine_dwarf.go
+++ b/src/cmd/link/internal/ld/macho_combine_dwarf.go
@@ -17,6 +17,7 @@ import (
var realdwarf, linkseg *macho.Segment
var dwarfstart, linkstart int64
+var dwarfaddr, linkaddr int64
var linkoffset uint32
const (
@@ -41,8 +42,7 @@ const (
LC_DYLIB_CODE_SIGN_DRS = 0x2B
LC_ENCRYPTION_INFO_64 = 0x2C
- dwarfMinAlign = 6 // 64 = 1 << 6
- pageAlign = 12 // 4096 = 1 << 12
+ pageAlign = 12 // 4096 = 1 << 12
)
type loadCmd struct {
@@ -157,16 +157,13 @@ func machoCombineDwarf(inexe, dsym, outexe string) error {
}
// Now copy the dwarf data into the output.
- maxalign := uint32(dwarfMinAlign) //
- for _, sect := range dwarfm.Sections {
- if sect.Align > maxalign {
- maxalign = sect.Align
- }
- }
- dwarfstart = machoCalcStart(realdwarf.Offset, linkseg.Offset, maxalign)
+ // Kernel requires all loaded segments to be page-aligned in the file,
+ // even though we mark this one as being 0 bytes of virtual address space.
+ dwarfstart = machoCalcStart(realdwarf.Offset, linkseg.Offset, pageAlign)
if _, err = outf.Seek(dwarfstart, 0); err != nil {
return err
}
+ dwarfaddr = int64((linkseg.Addr + linkseg.Memsz + 1<<pageAlign - 1) &^ (1<<pageAlign - 1))
if _, err = dwarff.Seek(int64(realdwarf.Offset), 0); err != nil {
return err
@@ -277,10 +274,10 @@ func machoUpdateSegment(r loadCmdReader, seg, sect interface{}) error {
return err
}
// There shouldn't be any sections, but just to make sure...
- return machoUpdateSections(r, segValue, reflect.ValueOf(sect), uint64(linkoffset))
+ return machoUpdateSections(r, segValue, reflect.ValueOf(sect), uint64(linkoffset), 0)
}
-func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, delta uint64) error {
+func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, deltaOffset, deltaAddr uint64) error {
iseg := reflect.Indirect(seg)
nsect := iseg.FieldByName("Nsect").Uint()
if nsect == 0 {
@@ -291,16 +288,20 @@ func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, delta uint64)
isect := reflect.Indirect(sect)
offsetField := isect.FieldByName("Offset")
reloffField := isect.FieldByName("Reloff")
+ addrField := isect.FieldByName("Addr")
sectSize := int64(isect.Type().Size())
for i := uint64(0); i < nsect; i++ {
if err := r.ReadAt(sectOffset, sect.Interface()); err != nil {
return err
}
if offsetField.Uint() != 0 {
- offsetField.SetUint(offsetField.Uint() + delta)
+ offsetField.SetUint(offsetField.Uint() + deltaOffset)
}
if reloffField.Uint() != 0 {
- reloffField.SetUint(reloffField.Uint() + delta)
+ reloffField.SetUint(reloffField.Uint() + deltaOffset)
+ }
+ if addrField.Uint() != 0 {
+ addrField.SetUint(addrField.Uint() + deltaAddr)
}
if err := r.WriteAt(sectOffset, sect.Interface()); err != nil {
return err
@@ -327,15 +328,30 @@ func machoUpdateDwarfHeader(r *loadCmdReader) error {
if err := r.ReadAt(0, seg); err != nil {
return err
}
- segValue := reflect.ValueOf(seg)
- offset := reflect.Indirect(segValue).FieldByName("Offset")
+ segv := reflect.ValueOf(seg).Elem()
+
+ segv.FieldByName("Offset").SetUint(uint64(dwarfstart))
+ segv.FieldByName("Addr").SetUint(uint64(dwarfaddr))
+
+ deltaOffset := uint64(dwarfstart) - realdwarf.Offset
+ deltaAddr := uint64(dwarfaddr) - realdwarf.Addr
+
+ // If we set Memsz to 0 (and might as well set Addr too),
+ // then the xnu kernel will bail out halfway through load_segment
+ // and not apply further sanity checks that we might fail in the future.
+ // We don't need the DWARF information actually available in memory.
+ // But if we do this for buildmode=c-shared then the user-space
+ // dynamic loader complains about memsz < filesz. Sigh.
+ if Buildmode != BuildmodeCShared {
+ segv.FieldByName("Addr").SetUint(0)
+ segv.FieldByName("Memsz").SetUint(0)
+ deltaAddr = 0
+ }
- delta := uint64(dwarfstart) - realdwarf.Offset
- offset.SetUint(offset.Uint() + delta)
if err := r.WriteAt(0, seg); err != nil {
return err
}
- return machoUpdateSections(*r, segValue, reflect.ValueOf(sect), delta)
+ return machoUpdateSections(*r, segv, reflect.ValueOf(sect), deltaOffset, deltaAddr)
}
func machoUpdateLoadCommand(r loadCmdReader, cmd interface{}, fields ...string) error {