diff options
author | Russ Cox <rsc@golang.org> | 2017-03-29 20:53:32 -0400 |
---|---|---|
committer | Austin Clements <austin@google.com> | 2017-04-05 16:58:33 +0000 |
commit | 3ca0d34fa12abad8fc5ca8b8fabec0c1b2c0d288 (patch) | |
tree | 5630c56db37c25aef9dbae61114ccd2e94dde8d3 | |
parent | 84192f2734c276baa3f2814d465eadc8eda82ce5 (diff) | |
download | go-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-x | misc/cgo/testcshared/test.bash | 7 | ||||
-rw-r--r-- | src/cmd/link/internal/ld/macho_combine_dwarf.go | 52 |
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 { |