// 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 := Linklookup(ctxt, ".machosymstr", 0)

	s.Type = obj.SMACHOSYMSTR
	s.Attr |= AttrReachable
	Adduint8(ctxt, s, ' ')
	Adduint8(ctxt, s, '\x00')

	s = Linklookup(ctxt, ".machosymtab", 0)
	s.Type = obj.SMACHOSYMTAB
	s.Attr |= AttrReachable

	if Linkmode != LinkExternal {
		s := Linklookup(ctxt, ".plt", 0) // will be __symbol_stub
		s.Type = obj.SMACHOPLT
		s.Attr |= AttrReachable

		s = Linklookup(ctxt, ".got", 0) // will be __nl_symbol_ptr
		s.Type = obj.SMACHOGOT
		s.Attr |= AttrReachable
		s.Align = 4

		s = Linklookup(ctxt, ".linkedit.plt", 0) // indirect table for .plt
		s.Type = obj.SMACHOINDIRECTPLT
		s.Attr |= AttrReachable

		s = Linklookup(ctxt, ".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)) ||
		(SysArch.Family == sys.ARM && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive))) {
		// 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 {
			ctxt.Diag("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(Linklookup(ctxt, ".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 := Linklookup(ctxt, ".machosymtab", 0)
		s2 := Linklookup(ctxt, ".linkedit.plt", 0)
		s3 := Linklookup(ctxt, ".linkedit.got", 0)
		s4 := Linklookup(ctxt, ".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_ int, addr int64, size int64, ver int, gotype *Symbol) {
	if s == nil {
		return
	}

	switch type_ {
	default:
		return

	case 'D', 'B', 'T':
		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, put func(*Link, *Symbol, string, int, int64, int64, int, *Symbol)) {
	genasmsym(ctxt, put)
	for _, s := range ctxt.Allsym {
		if s.Type == obj.SDYNIMPORT || s.Type == obj.SHOSTOBJ {
			if s.Attr.Reachable() {
				put(ctxt, s, "", 'D', 0, 0, 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, addsym)
	sortsym = make([]*Symbol, nsortsym)
	nsortsym = 0
	machogenasmsym(ctxt, addsym)
	sort.Sort(machoscmp(sortsym[:nsortsym]))
	for i := 0; i < nsortsym; i++ {
		sortsym[i].Dynid = int32(i)
	}
}

func machosymtab(ctxt *Link) {
	symtab := Linklookup(ctxt, ".machosymtab", 0)
	symstr := Linklookup(ctxt, ".machosymstr", 0)

	for i := 0; i < nsortsym; i++ {
		s := sortsym[i]
		Adduint32(ctxt, symtab, uint32(symstr.Size))

		// Only add _ to C symbols. Go symbols have dot in the name.
		if !strings.Contains(s.Extname, ".") {
			Adduint8(ctxt, symstr, '_')
		}

		// replace "·" as ".", because DTrace cannot handle it.
		Addstring(ctxt, 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() {
				Adduint8(ctxt, symtab, 0x0f)
			} else {
				Adduint8(ctxt, symtab, 0x0e)
			}
			o := s
			for o.Outer != nil {
				o = o.Outer
			}
			if o.Sect == nil {
				ctxt.Diag("missing section for %s", s.Name)
				Adduint8(ctxt, symtab, 0)
			} else {
				Adduint8(ctxt, symtab, uint8(o.Sect.Extnum))
			}
			Adduint16(ctxt, symtab, 0) // desc
			adduintxx(ctxt, symtab, uint64(Symaddr(ctxt, 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 := Linklookup(ctxt, ".machosymtab", 0)

	s2 := Linklookup(ctxt, ".linkedit.plt", 0)
	s3 := Linklookup(ctxt, ".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 := Linklookup(ctxt, ".machosymtab", 0)

	s2 := Linklookup(ctxt, ".linkedit.plt", 0)
	s3 := Linklookup(ctxt, ".linkedit.got", 0)
	s4 := Linklookup(ctxt, ".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
		}
		ctxt.Cursym = sym

		for ri := 0; ri < len(sym.R); ri++ {
			r := &sym.R[ri]
			if r.Done != 0 {
				continue
			}
			if Thearch.Machoreloc1(ctxt, r, int64(uint64(sym.Value+int64(r.Off))-sect.Vaddr)) < 0 {
				ctxt.Diag("unsupported obj reloc %d/%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)
	}
}
