// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ld import ( "cmd/internal/obj" "cmd/internal/sys" "sort" "strings" ) type MachoHdr struct { cpu uint32 subcpu uint32 } type MachoSect struct { name string segname string addr uint64 size uint64 off uint32 align uint32 reloc uint32 nreloc uint32 flag uint32 res1 uint32 res2 uint32 } type MachoSeg struct { name string vsize uint64 vaddr uint64 fileoffset uint64 filesize uint64 prot1 uint32 prot2 uint32 nsect uint32 msect uint32 sect []MachoSect flag uint32 } type MachoLoad struct { type_ uint32 data []uint32 } /* * Total amount of space to reserve at the start of the file * for Header, PHeaders, and SHeaders. * May waste some. */ const ( INITIAL_MACHO_HEADR = 4 * 1024 ) const ( MACHO_CPU_AMD64 = 1<<24 | 7 MACHO_CPU_386 = 7 MACHO_SUBCPU_X86 = 3 MACHO_CPU_ARM = 12 MACHO_SUBCPU_ARM = 0 MACHO_SUBCPU_ARMV7 = 9 MACHO_CPU_ARM64 = 1<<24 | 12 MACHO_SUBCPU_ARM64_ALL = 0 MACHO32SYMSIZE = 12 MACHO64SYMSIZE = 16 MACHO_X86_64_RELOC_UNSIGNED = 0 MACHO_X86_64_RELOC_SIGNED = 1 MACHO_X86_64_RELOC_BRANCH = 2 MACHO_X86_64_RELOC_GOT_LOAD = 3 MACHO_X86_64_RELOC_GOT = 4 MACHO_X86_64_RELOC_SUBTRACTOR = 5 MACHO_X86_64_RELOC_SIGNED_1 = 6 MACHO_X86_64_RELOC_SIGNED_2 = 7 MACHO_X86_64_RELOC_SIGNED_4 = 8 MACHO_ARM_RELOC_VANILLA = 0 MACHO_ARM_RELOC_PAIR = 1 MACHO_ARM_RELOC_SECTDIFF = 2 MACHO_ARM_RELOC_BR24 = 5 MACHO_ARM64_RELOC_UNSIGNED = 0 MACHO_ARM64_RELOC_BRANCH26 = 2 MACHO_ARM64_RELOC_PAGE21 = 3 MACHO_ARM64_RELOC_PAGEOFF12 = 4 MACHO_ARM64_RELOC_ADDEND = 10 MACHO_GENERIC_RELOC_VANILLA = 0 MACHO_FAKE_GOTPCREL = 100 ) // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Mach-O file writing // http://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html var macho64 bool var machohdr MachoHdr var load []MachoLoad var seg [16]MachoSeg var nseg int var ndebug int var nsect int const ( SymKindLocal = 0 + iota SymKindExtdef SymKindUndef NumSymKind ) var nkind [NumSymKind]int var sortsym []*Symbol var nsortsym int // Amount of space left for adding load commands // that refer to dynamic libraries. Because these have // to go in the Mach-O header, we can't just pick a // "big enough" header size. The initial header is // one page, the non-dynamic library stuff takes // up about 1300 bytes; we overestimate that as 2k. var loadBudget int = INITIAL_MACHO_HEADR - 2*1024 func Machoinit() { macho64 = SysArch.RegSize == 8 } func getMachoHdr() *MachoHdr { return &machohdr } func newMachoLoad(type_ uint32, ndata uint32) *MachoLoad { if macho64 && (ndata&1 != 0) { ndata++ } load = append(load, MachoLoad{}) l := &load[len(load)-1] l.type_ = type_ l.data = make([]uint32, ndata) return l } func newMachoSeg(name string, msect int) *MachoSeg { if nseg >= len(seg) { Exitf("too many segs") } s := &seg[nseg] nseg++ s.name = name s.msect = uint32(msect) s.sect = make([]MachoSect, msect) return s } func newMachoSect(seg *MachoSeg, name string, segname string) *MachoSect { if seg.nsect >= seg.msect { Exitf("too many sects in segment %s", seg.name) } s := &seg.sect[seg.nsect] seg.nsect++ s.name = name s.segname = segname nsect++ return s } // Generic linking code. var dylib []string var linkoff int64 func machowrite() int { o1 := coutbuf.Offset() loadsize := 4 * 4 * ndebug for i := 0; i < len(load); i++ { loadsize += 4 * (len(load[i].data) + 2) } if macho64 { loadsize += 18 * 4 * nseg loadsize += 20 * 4 * nsect } else { loadsize += 14 * 4 * nseg loadsize += 17 * 4 * nsect } if macho64 { Thearch.Lput(0xfeedfacf) } else { Thearch.Lput(0xfeedface) } Thearch.Lput(machohdr.cpu) Thearch.Lput(machohdr.subcpu) if Linkmode == LinkExternal { Thearch.Lput(1) /* file type - mach object */ } else { Thearch.Lput(2) /* file type - mach executable */ } Thearch.Lput(uint32(len(load)) + uint32(nseg) + uint32(ndebug)) Thearch.Lput(uint32(loadsize)) Thearch.Lput(1) /* flags - no undefines */ if macho64 { Thearch.Lput(0) /* reserved */ } var j int var s *MachoSeg var t *MachoSect for i := 0; i < nseg; i++ { s = &seg[i] if macho64 { Thearch.Lput(25) /* segment 64 */ Thearch.Lput(72 + 80*s.nsect) strnput(s.name, 16) Thearch.Vput(s.vaddr) Thearch.Vput(s.vsize) Thearch.Vput(s.fileoffset) Thearch.Vput(s.filesize) Thearch.Lput(s.prot1) Thearch.Lput(s.prot2) Thearch.Lput(s.nsect) Thearch.Lput(s.flag) } else { Thearch.Lput(1) /* segment 32 */ Thearch.Lput(56 + 68*s.nsect) strnput(s.name, 16) Thearch.Lput(uint32(s.vaddr)) Thearch.Lput(uint32(s.vsize)) Thearch.Lput(uint32(s.fileoffset)) Thearch.Lput(uint32(s.filesize)) Thearch.Lput(s.prot1) Thearch.Lput(s.prot2) Thearch.Lput(s.nsect) Thearch.Lput(s.flag) } for j = 0; uint32(j) < s.nsect; j++ { t = &s.sect[j] if macho64 { strnput(t.name, 16) strnput(t.segname, 16) Thearch.Vput(t.addr) Thearch.Vput(t.size) Thearch.Lput(t.off) Thearch.Lput(t.align) Thearch.Lput(t.reloc) Thearch.Lput(t.nreloc) Thearch.Lput(t.flag) Thearch.Lput(t.res1) /* reserved */ Thearch.Lput(t.res2) /* reserved */ Thearch.Lput(0) /* reserved */ } else { strnput(t.name, 16) strnput(t.segname, 16) Thearch.Lput(uint32(t.addr)) Thearch.Lput(uint32(t.size)) Thearch.Lput(t.off) Thearch.Lput(t.align) Thearch.Lput(t.reloc) Thearch.Lput(t.nreloc) Thearch.Lput(t.flag) Thearch.Lput(t.res1) /* reserved */ Thearch.Lput(t.res2) /* reserved */ } } } var l *MachoLoad for i := 0; i < len(load); i++ { l = &load[i] Thearch.Lput(l.type_) Thearch.Lput(4 * (uint32(len(l.data)) + 2)) for j = 0; j < len(l.data); j++ { Thearch.Lput(l.data[j]) } } return int(coutbuf.Offset() - o1) } func (ctxt *Link) domacho() { if *FlagD { return } // empirically, string table must begin with " \x00". s := ctxt.Syms.Lookup(".machosymstr", 0) s.Type = obj.SMACHOSYMSTR s.Attr |= AttrReachable Adduint8(ctxt, s, ' ') Adduint8(ctxt, s, '\x00') s = ctxt.Syms.Lookup(".machosymtab", 0) s.Type = obj.SMACHOSYMTAB s.Attr |= AttrReachable if Linkmode != LinkExternal { s := ctxt.Syms.Lookup(".plt", 0) // will be __symbol_stub s.Type = obj.SMACHOPLT s.Attr |= AttrReachable s = ctxt.Syms.Lookup(".got", 0) // will be __nl_symbol_ptr s.Type = obj.SMACHOGOT s.Attr |= AttrReachable s.Align = 4 s = ctxt.Syms.Lookup(".linkedit.plt", 0) // indirect table for .plt s.Type = obj.SMACHOINDIRECTPLT s.Attr |= AttrReachable s = ctxt.Syms.Lookup(".linkedit.got", 0) // indirect table for .got s.Type = obj.SMACHOINDIRECTGOT s.Attr |= AttrReachable } } func Machoadddynlib(lib string) { // Will need to store the library name rounded up // and 24 bytes of header metadata. If not enough // space, grab another page of initial space at the // beginning of the output file. loadBudget -= (len(lib)+7)/8*8 + 24 if loadBudget < 0 { HEADR += 4096 *FlagTextAddr += 4096 loadBudget += 4096 } dylib = append(dylib, lib) } func machoshbits(ctxt *Link, mseg *MachoSeg, sect *Section, segname string) { buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1) var msect *MachoSect if sect.Rwx&1 == 0 && segname != "__DWARF" && (SysArch.Family == sys.ARM64 || (SysArch.Family == sys.AMD64 && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive || Buildmode == BuildmodePlugin)) || (SysArch.Family == sys.ARM && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive || Buildmode == BuildmodePlugin))) { // Darwin external linker on arm64 and on amd64 and arm in c-shared/c-archive buildmode // complains about absolute relocs in __TEXT, so if the section is not // executable, put it in __DATA segment. msect = newMachoSect(mseg, buf, "__DATA") } else { msect = newMachoSect(mseg, buf, segname) } if sect.Rellen > 0 { msect.reloc = uint32(sect.Reloff) msect.nreloc = uint32(sect.Rellen / 8) } for 1<<msect.align < sect.Align { msect.align++ } msect.addr = sect.Vaddr msect.size = sect.Length if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen { // data in file if sect.Length > sect.Seg.Vaddr+sect.Seg.Filelen-sect.Vaddr { Errorf(nil, "macho cannot represent section %s crossing data and bss", sect.Name) } msect.off = uint32(sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr) } else { // zero fill msect.off = 0 msect.flag |= 1 } if sect.Rwx&1 != 0 { msect.flag |= 0x400 /* has instructions */ } if sect.Name == ".plt" { msect.name = "__symbol_stub1" msect.flag = 0x80000408 /* only instructions, code, symbol stubs */ msect.res1 = 0 //nkind[SymKindLocal]; msect.res2 = 6 } if sect.Name == ".got" { msect.name = "__nl_symbol_ptr" msect.flag = 6 /* section with nonlazy symbol pointers */ msect.res1 = uint32(ctxt.Syms.Lookup(".linkedit.plt", 0).Size / 4) /* offset into indirect symbol table */ } if sect.Name == ".init_array" { msect.name = "__mod_init_func" msect.flag = 9 // S_MOD_INIT_FUNC_POINTERS } if segname == "__DWARF" { msect.flag |= 0x02000000 } } func Asmbmacho(ctxt *Link) { /* apple MACH */ va := *FlagTextAddr - int64(HEADR) mh := getMachoHdr() switch SysArch.Family { default: Exitf("unknown macho architecture: %v", SysArch.Family) case sys.ARM: mh.cpu = MACHO_CPU_ARM mh.subcpu = MACHO_SUBCPU_ARMV7 case sys.AMD64: mh.cpu = MACHO_CPU_AMD64 mh.subcpu = MACHO_SUBCPU_X86 case sys.ARM64: mh.cpu = MACHO_CPU_ARM64 mh.subcpu = MACHO_SUBCPU_ARM64_ALL case sys.I386: mh.cpu = MACHO_CPU_386 mh.subcpu = MACHO_SUBCPU_X86 } var ms *MachoSeg if Linkmode == LinkExternal { /* segment for entire file */ ms = newMachoSeg("", 40) ms.fileoffset = Segtext.Fileoff if SysArch.Family == sys.ARM || Buildmode == BuildmodeCArchive { ms.filesize = Segdata.Fileoff + Segdata.Filelen - Segtext.Fileoff } else { ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff ms.vsize = ms.filesize } } /* segment for zero page */ if Linkmode != LinkExternal { ms = newMachoSeg("__PAGEZERO", 0) ms.vsize = uint64(va) } /* text */ v := Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) if Linkmode != LinkExternal { ms = newMachoSeg("__TEXT", 20) ms.vaddr = uint64(va) ms.vsize = uint64(v) ms.fileoffset = 0 ms.filesize = uint64(v) ms.prot1 = 7 ms.prot2 = 5 } for sect := Segtext.Sect; sect != nil; sect = sect.Next { machoshbits(ctxt, ms, sect, "__TEXT") } /* data */ if Linkmode != LinkExternal { w := int64(Segdata.Length) ms = newMachoSeg("__DATA", 20) ms.vaddr = uint64(va) + uint64(v) ms.vsize = uint64(w) ms.fileoffset = uint64(v) ms.filesize = Segdata.Filelen ms.prot1 = 3 ms.prot2 = 3 } for sect := Segdata.Sect; sect != nil; sect = sect.Next { machoshbits(ctxt, ms, sect, "__DATA") } /* dwarf */ if !*FlagW { if Linkmode != LinkExternal { ms = newMachoSeg("__DWARF", 20) ms.vaddr = Segdwarf.Vaddr ms.vsize = 0 ms.fileoffset = Segdwarf.Fileoff ms.filesize = Segdwarf.Filelen } for sect := Segdwarf.Sect; sect != nil; sect = sect.Next { machoshbits(ctxt, ms, sect, "__DWARF") } } if Linkmode != LinkExternal { switch SysArch.Family { default: Exitf("unknown macho architecture: %v", SysArch.Family) case sys.ARM: ml := newMachoLoad(5, 17+2) /* unix thread */ ml.data[0] = 1 /* thread type */ ml.data[1] = 17 /* word count */ ml.data[2+15] = uint32(Entryvalue(ctxt)) /* start pc */ case sys.AMD64: ml := newMachoLoad(5, 42+2) /* unix thread */ ml.data[0] = 4 /* thread type */ ml.data[1] = 42 /* word count */ ml.data[2+32] = uint32(Entryvalue(ctxt)) /* start pc */ ml.data[2+32+1] = uint32(Entryvalue(ctxt) >> 32) case sys.ARM64: ml := newMachoLoad(5, 68+2) /* unix thread */ ml.data[0] = 6 /* thread type */ ml.data[1] = 68 /* word count */ ml.data[2+64] = uint32(Entryvalue(ctxt)) /* start pc */ ml.data[2+64+1] = uint32(Entryvalue(ctxt) >> 32) case sys.I386: ml := newMachoLoad(5, 16+2) /* unix thread */ ml.data[0] = 1 /* thread type */ ml.data[1] = 16 /* word count */ ml.data[2+10] = uint32(Entryvalue(ctxt)) /* start pc */ } } if !*FlagD { // must match domacholink below s1 := ctxt.Syms.Lookup(".machosymtab", 0) s2 := ctxt.Syms.Lookup(".linkedit.plt", 0) s3 := ctxt.Syms.Lookup(".linkedit.got", 0) s4 := ctxt.Syms.Lookup(".machosymstr", 0) if Linkmode != LinkExternal { ms := newMachoSeg("__LINKEDIT", 0) ms.vaddr = uint64(va) + uint64(v) + uint64(Rnd(int64(Segdata.Length), int64(*FlagRound))) ms.vsize = uint64(s1.Size) + uint64(s2.Size) + uint64(s3.Size) + uint64(s4.Size) ms.fileoffset = uint64(linkoff) ms.filesize = ms.vsize ms.prot1 = 7 ms.prot2 = 3 } ml := newMachoLoad(2, 4) /* LC_SYMTAB */ ml.data[0] = uint32(linkoff) /* symoff */ ml.data[1] = uint32(nsortsym) /* nsyms */ ml.data[2] = uint32(linkoff + s1.Size + s2.Size + s3.Size) /* stroff */ ml.data[3] = uint32(s4.Size) /* strsize */ machodysymtab(ctxt) if Linkmode != LinkExternal { ml := newMachoLoad(14, 6) /* LC_LOAD_DYLINKER */ ml.data[0] = 12 /* offset to string */ stringtouint32(ml.data[1:], "/usr/lib/dyld") for i := 0; i < len(dylib); i++ { ml = newMachoLoad(12, 4+(uint32(len(dylib[i]))+1+7)/8*2) /* LC_LOAD_DYLIB */ ml.data[0] = 24 /* offset of string from beginning of load */ ml.data[1] = 0 /* time stamp */ ml.data[2] = 0 /* version */ ml.data[3] = 0 /* compatibility version */ stringtouint32(ml.data[4:], dylib[i]) } } } if Linkmode == LinkInternal { // For lldb, must say LC_VERSION_MIN_MACOSX or else // it won't know that this Mach-O binary is from OS X // (could be iOS or WatchOS instead). // Go on iOS uses linkmode=external, and linkmode=external // adds this itself. So we only need this code for linkmode=internal // and we can assume OS X. // // See golang.org/issues/12941. const LC_VERSION_MIN_MACOSX = 0x24 ml := newMachoLoad(LC_VERSION_MIN_MACOSX, 2) ml.data[0] = 10<<16 | 7<<8 | 0<<0 // OS X version 10.7.0 ml.data[1] = 10<<16 | 7<<8 | 0<<0 // SDK 10.7.0 } a := machowrite() if int32(a) > HEADR { Exitf("HEADR too small: %d > %d", a, HEADR) } } func symkind(s *Symbol) int { if s.Type == obj.SDYNIMPORT { return SymKindUndef } if s.Attr.CgoExport() { return SymKindExtdef } return SymKindLocal } func addsym(ctxt *Link, s *Symbol, name string, type_ SymbolType, addr int64, gotype *Symbol) { if s == nil { return } switch type_ { default: return case DataSym, BSSSym, TextSym: break } if sortsym != nil { sortsym[nsortsym] = s nkind[symkind(s)]++ } nsortsym++ } type machoscmp []*Symbol func (x machoscmp) Len() int { return len(x) } func (x machoscmp) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x machoscmp) Less(i, j int) bool { s1 := x[i] s2 := x[j] k1 := symkind(s1) k2 := symkind(s2) if k1 != k2 { return k1 < k2 } return s1.Extname < s2.Extname } func machogenasmsym(ctxt *Link) { genasmsym(ctxt, addsym) for _, s := range ctxt.Syms.Allsym { if s.Type == obj.SDYNIMPORT || s.Type == obj.SHOSTOBJ { if s.Attr.Reachable() { addsym(ctxt, s, "", DataSym, 0, nil) } } } } func machosymorder(ctxt *Link) { // On Mac OS X Mountain Lion, we must sort exported symbols // So we sort them here and pre-allocate dynid for them // See https://golang.org/issue/4029 for i := 0; i < len(dynexp); i++ { dynexp[i].Attr |= AttrReachable } machogenasmsym(ctxt) sortsym = make([]*Symbol, nsortsym) nsortsym = 0 machogenasmsym(ctxt) sort.Sort(machoscmp(sortsym[:nsortsym])) for i := 0; i < nsortsym; i++ { sortsym[i].Dynid = int32(i) } } // machoShouldExport reports whether a symbol needs to be exported. // // When dynamically linking, all non-local variables and plugin-exported // symbols need to be exported. func machoShouldExport(ctxt *Link, s *Symbol) bool { if !ctxt.DynlinkingGo() || s.Attr.Local() { return false } if Buildmode == BuildmodePlugin && strings.HasPrefix(s.Extname, *flagPluginPath) { return true } if strings.HasPrefix(s.Name, "type.") && !strings.HasPrefix(s.Name, "type..") { // reduce runtime typemap pressure, but do not // export alg functions (type..*), as these // appear in pclntable. return true } if strings.HasPrefix(s.Name, "go.link.pkghash") { return true } return s.Type >= obj.SELFSECT // only writable sections } func machosymtab(ctxt *Link) { symtab := ctxt.Syms.Lookup(".machosymtab", 0) symstr := ctxt.Syms.Lookup(".machosymstr", 0) for i := 0; i < nsortsym; i++ { s := sortsym[i] Adduint32(ctxt, symtab, uint32(symstr.Size)) export := machoShouldExport(ctxt, s) // In normal buildmodes, only add _ to C symbols, as // Go symbols have dot in the name. // // Do not export C symbols in plugins, as runtime C // symbols like crosscall2 are in pclntab and end up // pointing at the host binary, breaking unwinding. // See Issue #18190. cexport := !strings.Contains(s.Extname, ".") && (Buildmode != BuildmodePlugin || onlycsymbol(s)) if cexport || export { Adduint8(ctxt, symstr, '_') } // replace "·" as ".", because DTrace cannot handle it. Addstring(symstr, strings.Replace(s.Extname, "·", ".", -1)) if s.Type == obj.SDYNIMPORT || s.Type == obj.SHOSTOBJ { Adduint8(ctxt, symtab, 0x01) // type N_EXT, external symbol Adduint8(ctxt, symtab, 0) // no section Adduint16(ctxt, symtab, 0) // desc adduintxx(ctxt, symtab, 0, SysArch.PtrSize) // no value } else { if s.Attr.CgoExport() || export { Adduint8(ctxt, symtab, 0x0f) } else { Adduint8(ctxt, symtab, 0x0e) } o := s for o.Outer != nil { o = o.Outer } if o.Sect == nil { Errorf(s, "missing section for symbol") Adduint8(ctxt, symtab, 0) } else { Adduint8(ctxt, symtab, uint8(o.Sect.Extnum)) } Adduint16(ctxt, symtab, 0) // desc adduintxx(ctxt, symtab, uint64(Symaddr(s)), SysArch.PtrSize) } } } func machodysymtab(ctxt *Link) { ml := newMachoLoad(11, 18) /* LC_DYSYMTAB */ n := 0 ml.data[0] = uint32(n) /* ilocalsym */ ml.data[1] = uint32(nkind[SymKindLocal]) /* nlocalsym */ n += nkind[SymKindLocal] ml.data[2] = uint32(n) /* iextdefsym */ ml.data[3] = uint32(nkind[SymKindExtdef]) /* nextdefsym */ n += nkind[SymKindExtdef] ml.data[4] = uint32(n) /* iundefsym */ ml.data[5] = uint32(nkind[SymKindUndef]) /* nundefsym */ ml.data[6] = 0 /* tocoffset */ ml.data[7] = 0 /* ntoc */ ml.data[8] = 0 /* modtaboff */ ml.data[9] = 0 /* nmodtab */ ml.data[10] = 0 /* extrefsymoff */ ml.data[11] = 0 /* nextrefsyms */ // must match domacholink below s1 := ctxt.Syms.Lookup(".machosymtab", 0) s2 := ctxt.Syms.Lookup(".linkedit.plt", 0) s3 := ctxt.Syms.Lookup(".linkedit.got", 0) ml.data[12] = uint32(linkoff + s1.Size) /* indirectsymoff */ ml.data[13] = uint32((s2.Size + s3.Size) / 4) /* nindirectsyms */ ml.data[14] = 0 /* extreloff */ ml.data[15] = 0 /* nextrel */ ml.data[16] = 0 /* locreloff */ ml.data[17] = 0 /* nlocrel */ } func Domacholink(ctxt *Link) int64 { machosymtab(ctxt) // write data that will be linkedit section s1 := ctxt.Syms.Lookup(".machosymtab", 0) s2 := ctxt.Syms.Lookup(".linkedit.plt", 0) s3 := ctxt.Syms.Lookup(".linkedit.got", 0) s4 := ctxt.Syms.Lookup(".machosymstr", 0) // Force the linkedit section to end on a 16-byte // boundary. This allows pure (non-cgo) Go binaries // to be code signed correctly. // // Apple's codesign_allocate (a helper utility for // the codesign utility) can do this fine itself if // it is run on a dynamic Mach-O binary. However, // when it is run on a pure (non-cgo) Go binary, where // the linkedit section is mostly empty, it fails to // account for the extra padding that it itself adds // when adding the LC_CODE_SIGNATURE load command // (which must be aligned on a 16-byte boundary). // // By forcing the linkedit section to end on a 16-byte // boundary, codesign_allocate will not need to apply // any alignment padding itself, working around the // issue. for s4.Size%16 != 0 { Adduint8(ctxt, s4, 0) } size := int(s1.Size + s2.Size + s3.Size + s4.Size) if size > 0 { linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound)) Cseek(linkoff) Cwrite(s1.P[:s1.Size]) Cwrite(s2.P[:s2.Size]) Cwrite(s3.P[:s3.Size]) Cwrite(s4.P[:s4.Size]) } return Rnd(int64(size), int64(*FlagRound)) } func machorelocsect(ctxt *Link, sect *Section, syms []*Symbol) { // If main section has no bits, nothing to relocate. if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { return } sect.Reloff = uint64(coutbuf.Offset()) for i, s := range syms { if !s.Attr.Reachable() { continue } if uint64(s.Value) >= sect.Vaddr { syms = syms[i:] break } } eaddr := int32(sect.Vaddr + sect.Length) for _, sym := range syms { if !sym.Attr.Reachable() { continue } if sym.Value >= int64(eaddr) { break } for ri := 0; ri < len(sym.R); ri++ { r := &sym.R[ri] if r.Done != 0 { continue } if r.Xsym == nil { Errorf(sym, "missing xsym in relocation") continue } if !r.Xsym.Attr.Reachable() { Errorf(sym, "unreachable reloc %v target %v", r.Type, r.Xsym.Name) } if Thearch.Machoreloc1(sym, r, int64(uint64(sym.Value+int64(r.Off))-sect.Vaddr)) < 0 { Errorf(sym, "unsupported obj reloc %v/%d to %s", r.Type, r.Siz, r.Sym.Name) } } } sect.Rellen = uint64(coutbuf.Offset()) - sect.Reloff } func Machoemitreloc(ctxt *Link) { for coutbuf.Offset()&7 != 0 { Cput(0) } machorelocsect(ctxt, Segtext.Sect, ctxt.Textp) for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next { machorelocsect(ctxt, sect, datap) } for sect := Segdata.Sect; sect != nil; sect = sect.Next { machorelocsect(ctxt, sect, datap) } for sect := Segdwarf.Sect; sect != nil; sect = sect.Next { machorelocsect(ctxt, sect, dwarfp) } }