| // 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 ( | |
| "bytes" | |
| "cmd/internal/objabi" | |
| "cmd/internal/sys" | |
| "cmd/link/internal/loader" | |
| "cmd/link/internal/sym" | |
| "debug/macho" | |
| "encoding/binary" | |
| "fmt" | |
| "io" | |
| "os" | |
| "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 | |
| } | |
| // MachoPlatformLoad represents a LC_VERSION_MIN_* or | |
| // LC_BUILD_VERSION load command. | |
| type MachoPlatformLoad struct { | |
| platform MachoPlatform // One of PLATFORM_* constants. | |
| cmd MachoLoad | |
| } | |
| type MachoLoad struct { | |
| type_ uint32 | |
| data []uint32 | |
| } | |
| type MachoPlatform int | |
| /* | |
| * 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_GOT_LOAD_PAGE21 = 5 | |
| MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6 | |
| MACHO_ARM64_RELOC_ADDEND = 10 | |
| MACHO_GENERIC_RELOC_VANILLA = 0 | |
| MACHO_FAKE_GOTPCREL = 100 | |
| ) | |
| const ( | |
| MH_MAGIC = 0xfeedface | |
| MH_MAGIC_64 = 0xfeedfacf | |
| MH_OBJECT = 0x1 | |
| MH_EXECUTE = 0x2 | |
| MH_NOUNDEFS = 0x1 | |
| MH_DYLDLINK = 0x4 | |
| MH_PIE = 0x200000 | |
| ) | |
| const ( | |
| LC_SEGMENT = 0x1 | |
| LC_SYMTAB = 0x2 | |
| LC_SYMSEG = 0x3 | |
| LC_THREAD = 0x4 | |
| LC_UNIXTHREAD = 0x5 | |
| LC_LOADFVMLIB = 0x6 | |
| LC_IDFVMLIB = 0x7 | |
| LC_IDENT = 0x8 | |
| LC_FVMFILE = 0x9 | |
| LC_PREPAGE = 0xa | |
| LC_DYSYMTAB = 0xb | |
| LC_LOAD_DYLIB = 0xc | |
| LC_ID_DYLIB = 0xd | |
| LC_LOAD_DYLINKER = 0xe | |
| LC_ID_DYLINKER = 0xf | |
| LC_PREBOUND_DYLIB = 0x10 | |
| LC_ROUTINES = 0x11 | |
| LC_SUB_FRAMEWORK = 0x12 | |
| LC_SUB_UMBRELLA = 0x13 | |
| LC_SUB_CLIENT = 0x14 | |
| LC_SUB_LIBRARY = 0x15 | |
| LC_TWOLEVEL_HINTS = 0x16 | |
| LC_PREBIND_CKSUM = 0x17 | |
| LC_LOAD_WEAK_DYLIB = 0x80000018 | |
| LC_SEGMENT_64 = 0x19 | |
| LC_ROUTINES_64 = 0x1a | |
| LC_UUID = 0x1b | |
| LC_RPATH = 0x8000001c | |
| LC_CODE_SIGNATURE = 0x1d | |
| LC_SEGMENT_SPLIT_INFO = 0x1e | |
| LC_REEXPORT_DYLIB = 0x8000001f | |
| LC_LAZY_LOAD_DYLIB = 0x20 | |
| LC_ENCRYPTION_INFO = 0x21 | |
| LC_DYLD_INFO = 0x22 | |
| LC_DYLD_INFO_ONLY = 0x80000022 | |
| LC_LOAD_UPWARD_DYLIB = 0x80000023 | |
| LC_VERSION_MIN_MACOSX = 0x24 | |
| LC_VERSION_MIN_IPHONEOS = 0x25 | |
| LC_FUNCTION_STARTS = 0x26 | |
| LC_DYLD_ENVIRONMENT = 0x27 | |
| LC_MAIN = 0x80000028 | |
| LC_DATA_IN_CODE = 0x29 | |
| LC_SOURCE_VERSION = 0x2A | |
| LC_DYLIB_CODE_SIGN_DRS = 0x2B | |
| LC_ENCRYPTION_INFO_64 = 0x2C | |
| LC_LINKER_OPTION = 0x2D | |
| LC_LINKER_OPTIMIZATION_HINT = 0x2E | |
| LC_VERSION_MIN_TVOS = 0x2F | |
| LC_VERSION_MIN_WATCHOS = 0x30 | |
| LC_VERSION_NOTE = 0x31 | |
| LC_BUILD_VERSION = 0x32 | |
| ) | |
| const ( | |
| S_REGULAR = 0x0 | |
| S_ZEROFILL = 0x1 | |
| S_NON_LAZY_SYMBOL_POINTERS = 0x6 | |
| S_SYMBOL_STUBS = 0x8 | |
| S_MOD_INIT_FUNC_POINTERS = 0x9 | |
| S_ATTR_PURE_INSTRUCTIONS = 0x80000000 | |
| S_ATTR_DEBUG = 0x02000000 | |
| S_ATTR_SOME_INSTRUCTIONS = 0x00000400 | |
| ) | |
| const ( | |
| PLATFORM_MACOS MachoPlatform = 1 | |
| PLATFORM_IOS MachoPlatform = 2 | |
| PLATFORM_TVOS MachoPlatform = 3 | |
| PLATFORM_WATCHOS MachoPlatform = 4 | |
| PLATFORM_BRIDGEOS MachoPlatform = 5 | |
| ) | |
| // rebase table opcode | |
| const ( | |
| REBASE_TYPE_POINTER = 1 | |
| REBASE_TYPE_TEXT_ABSOLUTE32 = 2 | |
| REBASE_TYPE_TEXT_PCREL32 = 3 | |
| REBASE_OPCODE_MASK = 0xF0 | |
| REBASE_IMMEDIATE_MASK = 0x0F | |
| REBASE_OPCODE_DONE = 0x00 | |
| REBASE_OPCODE_SET_TYPE_IMM = 0x10 | |
| REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x20 | |
| REBASE_OPCODE_ADD_ADDR_ULEB = 0x30 | |
| REBASE_OPCODE_ADD_ADDR_IMM_SCALED = 0x40 | |
| REBASE_OPCODE_DO_REBASE_IMM_TIMES = 0x50 | |
| REBASE_OPCODE_DO_REBASE_ULEB_TIMES = 0x60 | |
| REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB = 0x70 | |
| REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB = 0x80 | |
| ) | |
| // bind table opcode | |
| const ( | |
| BIND_TYPE_POINTER = 1 | |
| BIND_TYPE_TEXT_ABSOLUTE32 = 2 | |
| BIND_TYPE_TEXT_PCREL32 = 3 | |
| BIND_SPECIAL_DYLIB_SELF = 0 | |
| BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1 | |
| BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2 | |
| BIND_SPECIAL_DYLIB_WEAK_LOOKUP = -3 | |
| BIND_OPCODE_MASK = 0xF0 | |
| BIND_IMMEDIATE_MASK = 0x0F | |
| BIND_OPCODE_DONE = 0x00 | |
| BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10 | |
| BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20 | |
| BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30 | |
| BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40 | |
| BIND_OPCODE_SET_TYPE_IMM = 0x50 | |
| BIND_OPCODE_SET_ADDEND_SLEB = 0x60 | |
| BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70 | |
| BIND_OPCODE_ADD_ADDR_ULEB = 0x80 | |
| BIND_OPCODE_DO_BIND = 0x90 | |
| BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0 | |
| BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0 | |
| BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0 | |
| BIND_OPCODE_THREADED = 0xD0 | |
| BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB = 0x00 | |
| BIND_SUBOPCODE_THREADED_APPLY = 0x01 | |
| ) | |
| // Mach-O file writing | |
| // https://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html | |
| var machohdr MachoHdr | |
| var load []MachoLoad | |
| var machoPlatform MachoPlatform | |
| 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 []loader.Sym | |
| 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 = INITIAL_MACHO_HEADR - 2*1024 | |
| func getMachoHdr() *MachoHdr { | |
| return &machohdr | |
| } | |
| func newMachoLoad(arch *sys.Arch, type_ uint32, ndata uint32) *MachoLoad { | |
| if arch.PtrSize == 8 && (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(ctxt *Link, arch *sys.Arch, out *OutBuf, linkmode LinkMode) int { | |
| o1 := out.Offset() | |
| loadsize := 4 * 4 * ndebug | |
| for i := range load { | |
| loadsize += 4 * (len(load[i].data) + 2) | |
| } | |
| if arch.PtrSize == 8 { | |
| loadsize += 18 * 4 * nseg | |
| loadsize += 20 * 4 * nsect | |
| } else { | |
| loadsize += 14 * 4 * nseg | |
| loadsize += 17 * 4 * nsect | |
| } | |
| if arch.PtrSize == 8 { | |
| out.Write32(MH_MAGIC_64) | |
| } else { | |
| out.Write32(MH_MAGIC) | |
| } | |
| out.Write32(machohdr.cpu) | |
| out.Write32(machohdr.subcpu) | |
| if linkmode == LinkExternal { | |
| out.Write32(MH_OBJECT) /* file type - mach object */ | |
| } else { | |
| out.Write32(MH_EXECUTE) /* file type - mach executable */ | |
| } | |
| out.Write32(uint32(len(load)) + uint32(nseg) + uint32(ndebug)) | |
| out.Write32(uint32(loadsize)) | |
| flags := uint32(0) | |
| if nkind[SymKindUndef] == 0 { | |
| flags |= MH_NOUNDEFS | |
| } | |
| if ctxt.IsPIE() && linkmode == LinkInternal { | |
| flags |= MH_PIE | MH_DYLDLINK | |
| } | |
| out.Write32(flags) /* flags */ | |
| if arch.PtrSize == 8 { | |
| out.Write32(0) /* reserved */ | |
| } | |
| for i := 0; i < nseg; i++ { | |
| s := &seg[i] | |
| if arch.PtrSize == 8 { | |
| out.Write32(LC_SEGMENT_64) | |
| out.Write32(72 + 80*s.nsect) | |
| out.WriteStringN(s.name, 16) | |
| out.Write64(s.vaddr) | |
| out.Write64(s.vsize) | |
| out.Write64(s.fileoffset) | |
| out.Write64(s.filesize) | |
| out.Write32(s.prot1) | |
| out.Write32(s.prot2) | |
| out.Write32(s.nsect) | |
| out.Write32(s.flag) | |
| } else { | |
| out.Write32(LC_SEGMENT) | |
| out.Write32(56 + 68*s.nsect) | |
| out.WriteStringN(s.name, 16) | |
| out.Write32(uint32(s.vaddr)) | |
| out.Write32(uint32(s.vsize)) | |
| out.Write32(uint32(s.fileoffset)) | |
| out.Write32(uint32(s.filesize)) | |
| out.Write32(s.prot1) | |
| out.Write32(s.prot2) | |
| out.Write32(s.nsect) | |
| out.Write32(s.flag) | |
| } | |
| for j := uint32(0); j < s.nsect; j++ { | |
| t := &s.sect[j] | |
| if arch.PtrSize == 8 { | |
| out.WriteStringN(t.name, 16) | |
| out.WriteStringN(t.segname, 16) | |
| out.Write64(t.addr) | |
| out.Write64(t.size) | |
| out.Write32(t.off) | |
| out.Write32(t.align) | |
| out.Write32(t.reloc) | |
| out.Write32(t.nreloc) | |
| out.Write32(t.flag) | |
| out.Write32(t.res1) /* reserved */ | |
| out.Write32(t.res2) /* reserved */ | |
| out.Write32(0) /* reserved */ | |
| } else { | |
| out.WriteStringN(t.name, 16) | |
| out.WriteStringN(t.segname, 16) | |
| out.Write32(uint32(t.addr)) | |
| out.Write32(uint32(t.size)) | |
| out.Write32(t.off) | |
| out.Write32(t.align) | |
| out.Write32(t.reloc) | |
| out.Write32(t.nreloc) | |
| out.Write32(t.flag) | |
| out.Write32(t.res1) /* reserved */ | |
| out.Write32(t.res2) /* reserved */ | |
| } | |
| } | |
| } | |
| for i := range load { | |
| l := &load[i] | |
| out.Write32(l.type_) | |
| out.Write32(4 * (uint32(len(l.data)) + 2)) | |
| for j := 0; j < len(l.data); j++ { | |
| out.Write32(l.data[j]) | |
| } | |
| } | |
| return int(out.Offset() - o1) | |
| } | |
| func (ctxt *Link) domacho() { | |
| if *FlagD { | |
| return | |
| } | |
| // Copy platform load command. | |
| for _, h := range hostobj { | |
| load, err := hostobjMachoPlatform(&h) | |
| if err != nil { | |
| Exitf("%v", err) | |
| } | |
| if load != nil { | |
| machoPlatform = load.platform | |
| ml := newMachoLoad(ctxt.Arch, load.cmd.type_, uint32(len(load.cmd.data))) | |
| copy(ml.data, load.cmd.data) | |
| break | |
| } | |
| } | |
| if machoPlatform == 0 { | |
| switch ctxt.Arch.Family { | |
| default: | |
| machoPlatform = PLATFORM_MACOS | |
| if ctxt.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. | |
| // | |
| // The version must be at least 10.9; see golang.org/issues/30488. | |
| ml := newMachoLoad(ctxt.Arch, LC_VERSION_MIN_MACOSX, 2) | |
| ml.data[0] = 10<<16 | 9<<8 | 0<<0 // OS X version 10.9.0 | |
| ml.data[1] = 10<<16 | 9<<8 | 0<<0 // SDK 10.9.0 | |
| } | |
| case sys.ARM, sys.ARM64: | |
| machoPlatform = PLATFORM_IOS | |
| } | |
| } | |
| // empirically, string table must begin with " \x00". | |
| s := ctxt.loader.LookupOrCreateSym(".machosymstr", 0) | |
| sb := ctxt.loader.MakeSymbolUpdater(s) | |
| sb.SetType(sym.SMACHOSYMSTR) | |
| sb.SetReachable(true) | |
| sb.AddUint8(' ') | |
| sb.AddUint8('\x00') | |
| s = ctxt.loader.LookupOrCreateSym(".machosymtab", 0) | |
| sb = ctxt.loader.MakeSymbolUpdater(s) | |
| sb.SetType(sym.SMACHOSYMTAB) | |
| sb.SetReachable(true) | |
| if ctxt.IsInternal() { | |
| s = ctxt.loader.LookupOrCreateSym(".plt", 0) // will be __symbol_stub | |
| sb = ctxt.loader.MakeSymbolUpdater(s) | |
| sb.SetType(sym.SMACHOPLT) | |
| sb.SetReachable(true) | |
| s = ctxt.loader.LookupOrCreateSym(".got", 0) // will be __nl_symbol_ptr | |
| sb = ctxt.loader.MakeSymbolUpdater(s) | |
| sb.SetType(sym.SMACHOGOT) | |
| sb.SetReachable(true) | |
| sb.SetAlign(4) | |
| s = ctxt.loader.LookupOrCreateSym(".linkedit.plt", 0) // indirect table for .plt | |
| sb = ctxt.loader.MakeSymbolUpdater(s) | |
| sb.SetType(sym.SMACHOINDIRECTPLT) | |
| sb.SetReachable(true) | |
| s = ctxt.loader.LookupOrCreateSym(".linkedit.got", 0) // indirect table for .got | |
| sb = ctxt.loader.MakeSymbolUpdater(s) | |
| sb.SetType(sym.SMACHOINDIRECTGOT) | |
| sb.SetReachable(true) | |
| } | |
| // Add a dummy symbol that will become the __asm marker section. | |
| if ctxt.IsExternal() { | |
| s = ctxt.loader.LookupOrCreateSym(".llvmasm", 0) | |
| sb = ctxt.loader.MakeSymbolUpdater(s) | |
| sb.SetType(sym.SMACHO) | |
| sb.SetReachable(true) | |
| sb.AddUint8(0) | |
| } | |
| // Do not export C symbols dynamically in plugins, as runtime C symbols like crosscall2 | |
| // are in pclntab and end up pointing at the host binary, breaking unwinding. | |
| // See Issue #18190. | |
| if ctxt.BuildMode == BuildModePlugin { | |
| for _, name := range []string{"_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2"} { | |
| s := ctxt.loader.Lookup(name, 0) | |
| if s != 0 { | |
| ctxt.loader.SetAttrCgoExportDynamic(s, false) | |
| } | |
| } | |
| } | |
| } | |
| func machoadddynlib(lib string, linkmode LinkMode) { | |
| if seenlib[lib] || linkmode == LinkExternal { | |
| return | |
| } | |
| seenlib[lib] = true | |
| // 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 *sym.Section, segname string) { | |
| buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1) | |
| 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 { | |
| msect.off = 0 | |
| msect.flag |= S_ZEROFILL | |
| } | |
| if sect.Rwx&1 != 0 { | |
| msect.flag |= S_ATTR_SOME_INSTRUCTIONS | |
| } | |
| if sect.Name == ".text" { | |
| msect.flag |= S_ATTR_PURE_INSTRUCTIONS | |
| } | |
| if sect.Name == ".plt" { | |
| msect.name = "__symbol_stub1" | |
| msect.flag = S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS | S_SYMBOL_STUBS | |
| msect.res1 = 0 //nkind[SymKindLocal]; | |
| msect.res2 = 6 | |
| } | |
| if sect.Name == ".got" { | |
| msect.name = "__nl_symbol_ptr" | |
| msect.flag = S_NON_LAZY_SYMBOL_POINTERS | |
| msect.res1 = uint32(ctxt.loader.SymSize(ctxt.ArchSyms.LinkEditPLT) / 4) /* offset into indirect symbol table */ | |
| } | |
| if sect.Name == ".init_array" { | |
| msect.name = "__mod_init_func" | |
| msect.flag = S_MOD_INIT_FUNC_POINTERS | |
| } | |
| // Some platforms such as watchOS and tvOS require binaries with | |
| // bitcode enabled. The Go toolchain can't output bitcode, so use | |
| // a marker section in the __LLVM segment, "__asm", to tell the Apple | |
| // toolchain that the Go text came from assembler and thus has no | |
| // bitcode. This is not true, but Kotlin/Native, Rust and Flutter | |
| // are also using this trick. | |
| if sect.Name == ".llvmasm" { | |
| msect.name = "__asm" | |
| msect.segname = "__LLVM" | |
| } | |
| if segname == "__DWARF" { | |
| msect.flag |= S_ATTR_DEBUG | |
| } | |
| } | |
| func asmbMacho(ctxt *Link) { | |
| machlink := doMachoLink(ctxt) | |
| if !*FlagS && ctxt.IsExternal() { | |
| symo := int64(Segdwarf.Fileoff + uint64(Rnd(int64(Segdwarf.Filelen), int64(*FlagRound))) + uint64(machlink)) | |
| ctxt.Out.SeekSet(symo) | |
| machoEmitReloc(ctxt) | |
| } | |
| ctxt.Out.SeekSet(0) | |
| /* apple MACH */ | |
| va := *FlagTextAddr - int64(HEADR) | |
| mh := getMachoHdr() | |
| switch ctxt.Arch.Family { | |
| default: | |
| Exitf("unknown macho architecture: %v", ctxt.Arch.Family) | |
| 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 | |
| } | |
| var ms *MachoSeg | |
| if ctxt.LinkMode == LinkExternal { | |
| /* segment for entire file */ | |
| ms = newMachoSeg("", 40) | |
| ms.fileoffset = Segtext.Fileoff | |
| ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff | |
| ms.vsize = Segdwarf.Vaddr + Segdwarf.Length - Segtext.Vaddr | |
| } | |
| /* segment for zero page */ | |
| if ctxt.LinkMode != LinkExternal { | |
| ms = newMachoSeg("__PAGEZERO", 0) | |
| ms.vsize = uint64(va) | |
| } | |
| /* text */ | |
| v := Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) | |
| if ctxt.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") | |
| } | |
| /* rodata */ | |
| if ctxt.LinkMode != LinkExternal && Segrelrodata.Length > 0 { | |
| ms = newMachoSeg("__DATA_CONST", 20) | |
| ms.vaddr = Segrelrodata.Vaddr | |
| ms.vsize = Segrelrodata.Length | |
| ms.fileoffset = Segrelrodata.Fileoff | |
| ms.filesize = Segrelrodata.Filelen | |
| ms.prot1 = 3 | |
| ms.prot2 = 3 | |
| ms.flag = 0x10 // SG_READ_ONLY | |
| } | |
| for _, sect := range Segrelrodata.Sections { | |
| machoshbits(ctxt, ms, sect, "__DATA_CONST") | |
| } | |
| /* data */ | |
| if ctxt.LinkMode != LinkExternal { | |
| ms = newMachoSeg("__DATA", 20) | |
| ms.vaddr = Segdata.Vaddr | |
| ms.vsize = Segdata.Length | |
| ms.fileoffset = Segdata.Fileoff | |
| ms.filesize = Segdata.Filelen | |
| ms.prot1 = 3 | |
| ms.prot2 = 3 | |
| } | |
| for _, sect := range Segdata.Sections { | |
| machoshbits(ctxt, ms, sect, "__DATA") | |
| } | |
| /* dwarf */ | |
| if !*FlagW { | |
| if ctxt.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 ctxt.LinkMode != LinkExternal { | |
| switch ctxt.Arch.Family { | |
| default: | |
| Exitf("unknown macho architecture: %v", ctxt.Arch.Family) | |
| case sys.AMD64: | |
| ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 42+2) | |
| 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(ctxt.Arch, LC_MAIN, 4) | |
| ml.data[0] = uint32(uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR))) | |
| ml.data[1] = uint32((uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR))) >> 32) | |
| } | |
| } | |
| if !*FlagD { | |
| ldr := ctxt.loader | |
| // must match domacholink below | |
| s1 := ldr.SymSize(ldr.Lookup(".machosymtab", 0)) | |
| s2 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT) | |
| s3 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT) | |
| s4 := ldr.SymSize(ldr.Lookup(".machosymstr", 0)) | |
| s5 := ldr.SymSize(ldr.Lookup(".machorebase", 0)) | |
| s6 := ldr.SymSize(ldr.Lookup(".machobind", 0)) | |
| if ctxt.LinkMode != LinkExternal { | |
| ms := newMachoSeg("__LINKEDIT", 0) | |
| ms.vaddr = uint64(Rnd(int64(Segdata.Vaddr+Segdata.Length), int64(*FlagRound))) | |
| ms.vsize = uint64(s1 + s2 + s3 + s4 + s5 + s6) | |
| ms.fileoffset = uint64(linkoff) | |
| ms.filesize = ms.vsize | |
| ms.prot1 = 1 | |
| ms.prot2 = 1 | |
| } | |
| ml := newMachoLoad(ctxt.Arch, LC_SYMTAB, 4) | |
| ml.data[0] = uint32(linkoff) /* symoff */ | |
| ml.data[1] = uint32(nsortsym) /* nsyms */ | |
| ml.data[2] = uint32(linkoff + s1 + s2 + s3) /* stroff */ | |
| ml.data[3] = uint32(s4) /* strsize */ | |
| machodysymtab(ctxt) | |
| if ctxt.LinkMode != LinkExternal { | |
| ml := newMachoLoad(ctxt.Arch, LC_LOAD_DYLINKER, 6) | |
| ml.data[0] = 12 /* offset to string */ | |
| stringtouint32(ml.data[1:], "/usr/lib/dyld") | |
| for _, lib := range dylib { | |
| ml = newMachoLoad(ctxt.Arch, LC_LOAD_DYLIB, 4+(uint32(len(lib))+1+7)/8*2) | |
| 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:], lib) | |
| } | |
| } | |
| if ctxt.LinkMode != LinkExternal && ctxt.IsPIE() { | |
| ml := newMachoLoad(ctxt.Arch, LC_DYLD_INFO_ONLY, 10) | |
| ml.data[0] = uint32(linkoff + s1 + s2 + s3 + s4) // rebase off | |
| ml.data[1] = uint32(s5) // rebase size | |
| ml.data[2] = uint32(linkoff + s1 + s2 + s3 + s4 + s5) // bind off | |
| ml.data[3] = uint32(s6) // bind size | |
| ml.data[4] = 0 // weak bind off | |
| ml.data[5] = 0 // weak bind size | |
| ml.data[6] = 0 // lazy bind off | |
| ml.data[7] = 0 // lazy bind size | |
| ml.data[8] = 0 // export | |
| ml.data[9] = 0 // export size | |
| } | |
| } | |
| a := machowrite(ctxt, ctxt.Arch, ctxt.Out, ctxt.LinkMode) | |
| if int32(a) > HEADR { | |
| Exitf("HEADR too small: %d > %d", a, HEADR) | |
| } | |
| } | |
| func symkind(ldr *loader.Loader, s loader.Sym) int { | |
| if ldr.SymType(s) == sym.SDYNIMPORT { | |
| return SymKindUndef | |
| } | |
| if ldr.AttrCgoExport(s) { | |
| return SymKindExtdef | |
| } | |
| return SymKindLocal | |
| } | |
| func collectmachosyms(ctxt *Link) { | |
| ldr := ctxt.loader | |
| addsym := func(s loader.Sym) { | |
| sortsym = append(sortsym, s) | |
| nkind[symkind(ldr, s)]++ | |
| } | |
| // Add special runtime.text and runtime.etext symbols. | |
| // We've already included this symbol in Textp on darwin if ctxt.DynlinkingGo(). | |
| // See data.go:/textaddress | |
| if !ctxt.DynlinkingGo() { | |
| s := ldr.Lookup("runtime.text", 0) | |
| if ldr.SymType(s) == sym.STEXT { | |
| addsym(s) | |
| } | |
| s = ldr.Lookup("runtime.etext", 0) | |
| if ldr.SymType(s) == sym.STEXT { | |
| addsym(s) | |
| } | |
| } | |
| // Add text symbols. | |
| for _, s := range ctxt.Textp { | |
| addsym(s) | |
| } | |
| shouldBeInSymbolTable := func(s loader.Sym) bool { | |
| if ldr.AttrNotInSymbolTable(s) { | |
| return false | |
| } | |
| name := ldr.RawSymName(s) // TODO: try not to read the name | |
| if name == "" || name[0] == '.' { | |
| return false | |
| } | |
| return true | |
| } | |
| // Add data symbols and external references. | |
| for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { | |
| if !ldr.AttrReachable(s) { | |
| continue | |
| } | |
| t := ldr.SymType(s) | |
| if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata | |
| if t == sym.STLSBSS { | |
| // TLSBSS is not used on darwin. See data.go:allocateDataSections | |
| continue | |
| } | |
| if !shouldBeInSymbolTable(s) { | |
| continue | |
| } | |
| addsym(s) | |
| } | |
| switch t { | |
| case sym.SDYNIMPORT, sym.SHOSTOBJ, sym.SUNDEFEXT: | |
| addsym(s) | |
| } | |
| // Some 64-bit functions have a "$INODE64" or "$INODE64$UNIX2003" suffix. | |
| if t == sym.SDYNIMPORT && ldr.SymDynimplib(s) == "/usr/lib/libSystem.B.dylib" { | |
| // But only on macOS. | |
| if machoPlatform == PLATFORM_MACOS { | |
| switch n := ldr.SymExtname(s); n { | |
| case "fdopendir": | |
| switch objabi.GOARCH { | |
| case "amd64": | |
| ldr.SetSymExtname(s, n+"$INODE64") | |
| } | |
| case "readdir_r", "getfsstat": | |
| switch objabi.GOARCH { | |
| case "amd64": | |
| ldr.SetSymExtname(s, n+"$INODE64") | |
| } | |
| } | |
| } | |
| } | |
| } | |
| nsortsym = len(sortsym) | |
| } | |
| func machosymorder(ctxt *Link) { | |
| ldr := ctxt.loader | |
| // 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 _, s := range ctxt.dynexp { | |
| if !ldr.AttrReachable(s) { | |
| panic("dynexp symbol is not reachable") | |
| } | |
| } | |
| collectmachosyms(ctxt) | |
| sort.Slice(sortsym[:nsortsym], func(i, j int) bool { | |
| s1 := sortsym[i] | |
| s2 := sortsym[j] | |
| k1 := symkind(ldr, s1) | |
| k2 := symkind(ldr, s2) | |
| if k1 != k2 { | |
| return k1 < k2 | |
| } | |
| return ldr.SymExtname(s1) < ldr.SymExtname(s2) // Note: unnamed symbols are not added in collectmachosyms | |
| }) | |
| for i, s := range sortsym { | |
| ldr.SetSymDynid(s, 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, ldr *loader.Loader, s loader.Sym) bool { | |
| if !ctxt.DynlinkingGo() || ldr.AttrLocal(s) { | |
| return false | |
| } | |
| if ctxt.BuildMode == BuildModePlugin && strings.HasPrefix(ldr.SymExtname(s), objabi.PathToPrefix(*flagPluginPath)) { | |
| return true | |
| } | |
| name := ldr.RawSymName(s) | |
| if strings.HasPrefix(name, "go.itab.") { | |
| return true | |
| } | |
| if strings.HasPrefix(name, "type.") && !strings.HasPrefix(name, "type..") { | |
| // reduce runtime typemap pressure, but do not | |
| // export alg functions (type..*), as these | |
| // appear in pclntable. | |
| return true | |
| } | |
| if strings.HasPrefix(name, "go.link.pkghash") { | |
| return true | |
| } | |
| return ldr.SymType(s) >= sym.SFirstWritable // only writable sections | |
| } | |
| func machosymtab(ctxt *Link) { | |
| ldr := ctxt.loader | |
| symtab := ldr.CreateSymForUpdate(".machosymtab", 0) | |
| symstr := ldr.CreateSymForUpdate(".machosymstr", 0) | |
| for _, s := range sortsym[:nsortsym] { | |
| symtab.AddUint32(ctxt.Arch, uint32(symstr.Size())) | |
| export := machoShouldExport(ctxt, ldr, s) | |
| // Prefix symbol names with "_" to match the system toolchain. | |
| // (We used to only prefix C symbols, which is all required for the build. | |
| // But some tools don't recognize Go symbols as symbols, so we prefix them | |
| // as well.) | |
| symstr.AddUint8('_') | |
| // replace "·" as ".", because DTrace cannot handle it. | |
| symstr.Addstring(strings.Replace(ldr.SymExtname(s), "·", ".", -1)) | |
| if t := ldr.SymType(s); t == sym.SDYNIMPORT || t == sym.SHOSTOBJ || t == sym.SUNDEFEXT { | |
| symtab.AddUint8(0x01) // type N_EXT, external symbol | |
| symtab.AddUint8(0) // no section | |
| symtab.AddUint16(ctxt.Arch, 0) // desc | |
| symtab.AddUintXX(ctxt.Arch, 0, ctxt.Arch.PtrSize) // no value | |
| } else { | |
| if export || ldr.AttrCgoExportDynamic(s) { | |
| symtab.AddUint8(0x0f) // N_SECT | N_EXT | |
| } else if ldr.AttrCgoExportStatic(s) { | |
| // Only export statically, not dynamically. (N_PEXT is like hidden visibility) | |
| symtab.AddUint8(0x1f) // N_SECT | N_EXT | N_PEXT | |
| } else { | |
| symtab.AddUint8(0x0e) // N_SECT | |
| } | |
| o := s | |
| if outer := ldr.OuterSym(o); outer != 0 { | |
| o = outer | |
| } | |
| if ldr.SymSect(o) == nil { | |
| ldr.Errorf(s, "missing section for symbol") | |
| symtab.AddUint8(0) | |
| } else { | |
| symtab.AddUint8(uint8(ldr.SymSect(o).Extnum)) | |
| } | |
| symtab.AddUint16(ctxt.Arch, 0) // desc | |
| symtab.AddUintXX(ctxt.Arch, uint64(ldr.SymAddr(s)), ctxt.Arch.PtrSize) | |
| } | |
| } | |
| } | |
| func machodysymtab(ctxt *Link) { | |
| ml := newMachoLoad(ctxt.Arch, LC_DYSYMTAB, 18) | |
| 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 */ | |
| ldr := ctxt.loader | |
| // must match domacholink below | |
| s1 := ldr.SymSize(ldr.Lookup(".machosymtab", 0)) | |
| s2 := ldr.SymSize(ctxt.ArchSyms.LinkEditPLT) | |
| s3 := ldr.SymSize(ctxt.ArchSyms.LinkEditGOT) | |
| ml.data[12] = uint32(linkoff + s1) /* indirectsymoff */ | |
| ml.data[13] = uint32((s2 + s3) / 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) | |
| machoDyldInfo(ctxt) | |
| ldr := ctxt.loader | |
| // write data that will be linkedit section | |
| s1 := ldr.Lookup(".machosymtab", 0) | |
| s2 := ctxt.ArchSyms.LinkEditPLT | |
| s3 := ctxt.ArchSyms.LinkEditGOT | |
| s4 := ldr.Lookup(".machosymstr", 0) | |
| s5 := ldr.Lookup(".machorebase", 0) | |
| s6 := ldr.Lookup(".machobind", 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. | |
| s4b := ldr.MakeSymbolUpdater(s4) | |
| for s4b.Size()%16 != 0 { | |
| s4b.AddUint8(0) | |
| } | |
| size := int(ldr.SymSize(s1) + ldr.SymSize(s2) + ldr.SymSize(s3) + ldr.SymSize(s4) + ldr.SymSize(s5) + ldr.SymSize(s6)) | |
| if size > 0 { | |
| linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segrelrodata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound)) | |
| ctxt.Out.SeekSet(linkoff) | |
| ctxt.Out.Write(ldr.Data(s1)) | |
| ctxt.Out.Write(ldr.Data(s2)) | |
| ctxt.Out.Write(ldr.Data(s3)) | |
| ctxt.Out.Write(ldr.Data(s4)) | |
| ctxt.Out.Write(ldr.Data(s5)) | |
| ctxt.Out.Write(ldr.Data(s6)) | |
| } | |
| return Rnd(int64(size), int64(*FlagRound)) | |
| } | |
| func machorelocsect(ctxt *Link, out *OutBuf, sect *sym.Section, syms []loader.Sym) { | |
| // If main section has no bits, nothing to relocate. | |
| if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { | |
| return | |
| } | |
| ldr := ctxt.loader | |
| for i, s := range syms { | |
| if !ldr.AttrReachable(s) { | |
| continue | |
| } | |
| if uint64(ldr.SymValue(s)) >= sect.Vaddr { | |
| syms = syms[i:] | |
| break | |
| } | |
| } | |
| eaddr := int32(sect.Vaddr + sect.Length) | |
| for _, s := range syms { | |
| if !ldr.AttrReachable(s) { | |
| continue | |
| } | |
| if ldr.SymValue(s) >= int64(eaddr) { | |
| break | |
| } | |
| // Compute external relocations on the go, and pass to Machoreloc1 | |
| // to stream out. | |
| relocs := ldr.Relocs(s) | |
| for ri := 0; ri < relocs.Count(); ri++ { | |
| r := relocs.At(ri) | |
| rr, ok := extreloc(ctxt, ldr, s, r) | |
| if !ok { | |
| continue | |
| } | |
| if rr.Xsym == 0 { | |
| ldr.Errorf(s, "missing xsym in relocation") | |
| continue | |
| } | |
| if !ldr.AttrReachable(rr.Xsym) { | |
| ldr.Errorf(s, "unreachable reloc %d (%s) target %v", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), ldr.SymName(rr.Xsym)) | |
| } | |
| if !thearch.Machoreloc1(ctxt.Arch, out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-sect.Vaddr)) { | |
| ldr.Errorf(s, "unsupported obj reloc %d (%s)/%d to %s", r.Type(), sym.RelocName(ctxt.Arch, r.Type()), r.Siz(), ldr.SymName(r.Sym())) | |
| } | |
| } | |
| } | |
| // sanity check | |
| if uint64(out.Offset()) != sect.Reloff+sect.Rellen { | |
| panic("machorelocsect: size mismatch") | |
| } | |
| } | |
| func machoEmitReloc(ctxt *Link) { | |
| for ctxt.Out.Offset()&7 != 0 { | |
| ctxt.Out.Write8(0) | |
| } | |
| sizeExtRelocs(ctxt, thearch.MachorelocSize) | |
| relocSect, wg := relocSectFn(ctxt, machorelocsect) | |
| relocSect(ctxt, Segtext.Sections[0], ctxt.Textp) | |
| for _, sect := range Segtext.Sections[1:] { | |
| relocSect(ctxt, sect, ctxt.datap) | |
| } | |
| for _, sect := range Segrelrodata.Sections { | |
| relocSect(ctxt, sect, ctxt.datap) | |
| } | |
| for _, sect := range Segdata.Sections { | |
| relocSect(ctxt, sect, ctxt.datap) | |
| } | |
| for i := 0; i < len(Segdwarf.Sections); i++ { | |
| sect := Segdwarf.Sections[i] | |
| si := dwarfp[i] | |
| if si.secSym() != loader.Sym(sect.Sym) || | |
| ctxt.loader.SymSect(si.secSym()) != sect { | |
| panic("inconsistency between dwarfp and Segdwarf") | |
| } | |
| relocSect(ctxt, sect, si.syms) | |
| } | |
| wg.Wait() | |
| } | |
| // hostobjMachoPlatform returns the first platform load command found | |
| // in the host object, if any. | |
| func hostobjMachoPlatform(h *Hostobj) (*MachoPlatformLoad, error) { | |
| f, err := os.Open(h.file) | |
| if err != nil { | |
| return nil, fmt.Errorf("%s: failed to open host object: %v\n", h.file, err) | |
| } | |
| defer f.Close() | |
| sr := io.NewSectionReader(f, h.off, h.length) | |
| m, err := macho.NewFile(sr) | |
| if err != nil { | |
| // Not a valid Mach-O file. | |
| return nil, nil | |
| } | |
| return peekMachoPlatform(m) | |
| } | |
| // peekMachoPlatform returns the first LC_VERSION_MIN_* or LC_BUILD_VERSION | |
| // load command found in the Mach-O file, if any. | |
| func peekMachoPlatform(m *macho.File) (*MachoPlatformLoad, error) { | |
| for _, cmd := range m.Loads { | |
| raw := cmd.Raw() | |
| ml := MachoLoad{ | |
| type_: m.ByteOrder.Uint32(raw), | |
| } | |
| // Skip the type and command length. | |
| data := raw[8:] | |
| var p MachoPlatform | |
| switch ml.type_ { | |
| case LC_VERSION_MIN_IPHONEOS: | |
| p = PLATFORM_IOS | |
| case LC_VERSION_MIN_MACOSX: | |
| p = PLATFORM_MACOS | |
| case LC_VERSION_MIN_WATCHOS: | |
| p = PLATFORM_WATCHOS | |
| case LC_VERSION_MIN_TVOS: | |
| p = PLATFORM_TVOS | |
| case LC_BUILD_VERSION: | |
| p = MachoPlatform(m.ByteOrder.Uint32(data)) | |
| default: | |
| continue | |
| } | |
| ml.data = make([]uint32, len(data)/4) | |
| r := bytes.NewReader(data) | |
| if err := binary.Read(r, m.ByteOrder, &ml.data); err != nil { | |
| return nil, err | |
| } | |
| return &MachoPlatformLoad{ | |
| platform: p, | |
| cmd: ml, | |
| }, nil | |
| } | |
| return nil, nil | |
| } | |
| // A rebase entry tells the dynamic linker the data at sym+off needs to be | |
| // relocated when the in-memory image moves. (This is somewhat like, say, | |
| // ELF R_X86_64_RELATIVE). | |
| // For now, the only kind of entry we support is that the data is an absolute | |
| // address. That seems all we need. | |
| // In the binary it uses a compact stateful bytecode encoding. So we record | |
| // entries as we go and build the table at the end. | |
| type machoRebaseRecord struct { | |
| sym loader.Sym | |
| off int64 | |
| } | |
| var machorebase []machoRebaseRecord | |
| func MachoAddRebase(s loader.Sym, off int64) { | |
| machorebase = append(machorebase, machoRebaseRecord{s, off}) | |
| } | |
| // A bind entry tells the dynamic linker the data at GOT+off should be bound | |
| // to the address of the target symbol, which is a dynamic import. | |
| // For now, the only kind of entry we support is that the data is an absolute | |
| // address, and the source symbol is always the GOT. That seems all we need. | |
| // In the binary it uses a compact stateful bytecode encoding. So we record | |
| // entries as we go and build the table at the end. | |
| type machoBindRecord struct { | |
| off int64 | |
| targ loader.Sym | |
| } | |
| var machobind []machoBindRecord | |
| func MachoAddBind(off int64, targ loader.Sym) { | |
| machobind = append(machobind, machoBindRecord{off, targ}) | |
| } | |
| // Generate data for the dynamic linker, used in LC_DYLD_INFO_ONLY load command. | |
| // See mach-o/loader.h, struct dyld_info_command, for the encoding. | |
| // e.g. https://opensource.apple.com/source/xnu/xnu-6153.81.5/EXTERNAL_HEADERS/mach-o/loader.h | |
| func machoDyldInfo(ctxt *Link) { | |
| ldr := ctxt.loader | |
| rebase := ldr.CreateSymForUpdate(".machorebase", 0) | |
| bind := ldr.CreateSymForUpdate(".machobind", 0) | |
| if !(ctxt.IsPIE() && ctxt.IsInternal()) { | |
| return | |
| } | |
| segId := func(seg *sym.Segment) uint8 { | |
| switch seg { | |
| case &Segtext: | |
| return 1 | |
| case &Segrelrodata: | |
| return 2 | |
| case &Segdata: | |
| if Segrelrodata.Length > 0 { | |
| return 3 | |
| } | |
| return 2 | |
| } | |
| panic("unknown segment") | |
| } | |
| dylibId := func(s loader.Sym) int { | |
| slib := ldr.SymDynimplib(s) | |
| for i, lib := range dylib { | |
| if lib == slib { | |
| return i + 1 | |
| } | |
| } | |
| return BIND_SPECIAL_DYLIB_FLAT_LOOKUP // don't know where it is from | |
| } | |
| // Rebase table. | |
| // TODO: use more compact encoding. The encoding is stateful, and | |
| // we can use delta encoding. | |
| rebase.AddUint8(REBASE_OPCODE_SET_TYPE_IMM | REBASE_TYPE_POINTER) | |
| for _, r := range machorebase { | |
| seg := ldr.SymSect(r.sym).Seg | |
| off := uint64(ldr.SymValue(r.sym)+r.off) - seg.Vaddr | |
| rebase.AddUint8(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segId(seg)) | |
| rebase.AddUleb(off) | |
| rebase.AddUint8(REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1) | |
| } | |
| rebase.AddUint8(REBASE_OPCODE_DONE) | |
| sz := Rnd(rebase.Size(), 8) | |
| rebase.Grow(sz) | |
| rebase.SetSize(sz) | |
| // Bind table. | |
| // TODO: compact encoding, as above. | |
| // TODO: lazy binding? | |
| got := ctxt.GOT | |
| seg := ldr.SymSect(got).Seg | |
| gotAddr := ldr.SymValue(got) | |
| bind.AddUint8(BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER) | |
| for _, r := range machobind { | |
| off := uint64(gotAddr+r.off) - seg.Vaddr | |
| bind.AddUint8(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segId(seg)) | |
| bind.AddUleb(off) | |
| d := dylibId(r.targ) | |
| if d > 0 && d < 128 { | |
| bind.AddUint8(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | uint8(d)&0xf) | |
| } else if d >= 128 { | |
| bind.AddUint8(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB) | |
| bind.AddUleb(uint64(d)) | |
| } else { // d <= 0 | |
| bind.AddUint8(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | uint8(d)&0xf) | |
| } | |
| bind.AddUint8(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM) | |
| // target symbol name as a C string, with _ prefix | |
| bind.AddUint8('_') | |
| bind.Addstring(ldr.SymExtname(r.targ)) | |
| bind.AddUint8(BIND_OPCODE_DO_BIND) | |
| } | |
| bind.AddUint8(BIND_OPCODE_DONE) | |
| sz = Rnd(bind.Size(), 16) // make it 16-byte aligned, see the comment in doMachoLink | |
| bind.Grow(sz) | |
| bind.SetSize(sz) | |
| // TODO: export table. | |
| // The symbols names are encoded as a trie. I'm really too lazy to do that | |
| // for now. | |
| // Without it, the symbols are not dynamically exported, so they cannot be | |
| // e.g. dlsym'd. But internal linking is not the default in that case, so | |
| // it is fine. | |
| } |