| // 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/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 = SMACHOSYMSTR |
| s.Attr |= AttrReachable |
| Adduint8(ctxt, s, ' ') |
| Adduint8(ctxt, s, '\x00') |
| |
| s = ctxt.Syms.Lookup(".machosymtab", 0) |
| s.Type = SMACHOSYMTAB |
| s.Attr |= AttrReachable |
| |
| if Linkmode != LinkExternal { |
| s := ctxt.Syms.Lookup(".plt", 0) // will be __symbol_stub |
| s.Type = SMACHOPLT |
| s.Attr |= AttrReachable |
| |
| s = ctxt.Syms.Lookup(".got", 0) // will be __nl_symbol_ptr |
| s.Type = SMACHOGOT |
| s.Attr |= AttrReachable |
| s.Align = 4 |
| |
| s = ctxt.Syms.Lookup(".linkedit.plt", 0) // indirect table for .plt |
| s.Type = SMACHOINDIRECTPLT |
| s.Attr |= AttrReachable |
| |
| s = ctxt.Syms.Lookup(".linkedit.got", 0) // indirect table for .got |
| s.Type = 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 = Segdwarf.Vaddr + Segdwarf.Length - Segtext.Vaddr |
| } |
| } |
| |
| /* 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 := range Segtext.Sections { |
| 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 := range Segdata.Sections { |
| 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 := range Segdwarf.Sections { |
| 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 == 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 == SDYNIMPORT || s.Type == 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 >= 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 == SDYNIMPORT || s.Type == 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.Sections[0], ctxt.Textp) |
| for _, sect := range Segtext.Sections[1:] { |
| machorelocsect(ctxt, sect, datap) |
| } |
| for _, sect := range Segdata.Sections { |
| machorelocsect(ctxt, sect, datap) |
| } |
| for _, sect := range Segdwarf.Sections { |
| machorelocsect(ctxt, sect, dwarfp) |
| } |
| } |