// Copyright 2020 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/objabi" "cmd/link/internal/loader" "cmd/link/internal/sym" "fmt" "runtime" "sync" ) // Assembling the binary is broken into two steps: // - writing out the code/data/dwarf Segments, applying relocations on the fly // - writing out the architecture specific pieces. // This function handles the first part. func asmb(ctxt *Link) { // TODO(jfaller): delete me. if thearch.Asmb != nil { thearch.Asmb(ctxt, ctxt.loader) return } if ctxt.IsELF { Asmbelfsetup() } var wg sync.WaitGroup f := func(ctxt *Link, out *OutBuf, start, length int64) { pad := thearch.CodePad if pad == nil { pad = zeros[:] } CodeblkPad(ctxt, out, start, length, pad) } for _, sect := range Segtext.Sections { offset := sect.Vaddr - Segtext.Vaddr + Segtext.Fileoff // Handle text sections with Codeblk if sect.Name == ".text" { writeParallel(&wg, f, ctxt, offset, sect.Vaddr, sect.Length) } else { writeParallel(&wg, datblk, ctxt, offset, sect.Vaddr, sect.Length) } } if Segrodata.Filelen > 0 { writeParallel(&wg, datblk, ctxt, Segrodata.Fileoff, Segrodata.Vaddr, Segrodata.Filelen) } if Segrelrodata.Filelen > 0 { writeParallel(&wg, datblk, ctxt, Segrelrodata.Fileoff, Segrelrodata.Vaddr, Segrelrodata.Filelen) } writeParallel(&wg, datblk, ctxt, Segdata.Fileoff, Segdata.Vaddr, Segdata.Filelen) writeParallel(&wg, dwarfblk, ctxt, Segdwarf.Fileoff, Segdwarf.Vaddr, Segdwarf.Filelen) wg.Wait() } // Assembling the binary is broken into two steps: // - writing out the code/data/dwarf Segments // - writing out the architecture specific pieces. // This function handles the second part. func asmb2(ctxt *Link) { if thearch.Asmb2 != nil { thearch.Asmb2(ctxt, ctxt.loader) return } symSize = 0 spSize = 0 lcSize = 0 switch ctxt.HeadType { default: panic("unknown platform") // Macho case objabi.Hdarwin: asmbMacho(ctxt) // Plan9 case objabi.Hplan9: asmbPlan9(ctxt) // PE case objabi.Hwindows: asmbPe(ctxt) // Xcoff case objabi.Haix: asmbXcoff(ctxt) // Elf case objabi.Hdragonfly, objabi.Hfreebsd, objabi.Hlinux, objabi.Hnetbsd, objabi.Hopenbsd, objabi.Hsolaris: asmbElf(ctxt) } if *FlagC { fmt.Printf("textsize=%d\n", Segtext.Filelen) fmt.Printf("datsize=%d\n", Segdata.Filelen) fmt.Printf("bsssize=%d\n", Segdata.Length-Segdata.Filelen) fmt.Printf("symsize=%d\n", symSize) fmt.Printf("lcsize=%d\n", lcSize) fmt.Printf("total=%d\n", Segtext.Filelen+Segdata.Length+uint64(symSize)+uint64(lcSize)) } } // writePlan9Header writes out the plan9 header at the present position in the OutBuf. func writePlan9Header(buf *OutBuf, magic uint32, entry int64, is64Bit bool) { if is64Bit { magic |= 0x00008000 } buf.Write32b(magic) buf.Write32b(uint32(Segtext.Filelen)) buf.Write32b(uint32(Segdata.Filelen)) buf.Write32b(uint32(Segdata.Length - Segdata.Filelen)) buf.Write32b(uint32(symSize)) if is64Bit { buf.Write32b(uint32(entry &^ 0x80000000)) } else { buf.Write32b(uint32(entry)) } buf.Write32b(uint32(spSize)) buf.Write32b(uint32(lcSize)) // amd64 includes the entry at the beginning of the symbol table. if is64Bit { buf.Write64b(uint64(entry)) } } // asmbPlan9 assembles a plan 9 binary. func asmbPlan9(ctxt *Link) { if !*FlagS { *FlagS = true symo := int64(Segdata.Fileoff + Segdata.Filelen) ctxt.Out.SeekSet(symo) asmbPlan9Sym(ctxt) } ctxt.Out.SeekSet(0) writePlan9Header(ctxt.Out, thearch.Plan9Magic, Entryvalue(ctxt), thearch.Plan9_64Bit) } // sizeExtRelocs precomputes the size needed for the reloc records, // sets the size and offset for relocation records in each section, // and mmap the output buffer with the proper size. func sizeExtRelocs(ctxt *Link, relsize uint32) { if relsize == 0 { panic("sizeExtRelocs: relocation size not set") } var sz int64 for _, seg := range Segments { for _, sect := range seg.Sections { sect.Reloff = uint64(ctxt.Out.Offset() + sz) sect.Rellen = uint64(relsize * sect.Relcount) sz += int64(sect.Rellen) } } filesz := ctxt.Out.Offset() + sz err := ctxt.Out.Mmap(uint64(filesz)) if err != nil { Exitf("mapping output file failed: %v", err) } } // relocSectFn wraps the function writing relocations of a section // for parallel execution. Returns the wrapped function and a wait // group for which the caller should wait. func relocSectFn(ctxt *Link, relocSect func(*Link, *OutBuf, *sym.Section, []loader.Sym)) (func(*Link, *sym.Section, []loader.Sym), *sync.WaitGroup) { var fn func(ctxt *Link, sect *sym.Section, syms []loader.Sym) var wg sync.WaitGroup var sem chan int if ctxt.Out.isMmapped() { // Write sections in parallel. sem = make(chan int, 2*runtime.GOMAXPROCS(0)) fn = func(ctxt *Link, sect *sym.Section, syms []loader.Sym) { wg.Add(1) sem <- 1 out, err := ctxt.Out.View(sect.Reloff) if err != nil { panic(err) } go func() { relocSect(ctxt, out, sect, syms) wg.Done() <-sem }() } } else { // We cannot Mmap. Write sequentially. fn = func(ctxt *Link, sect *sym.Section, syms []loader.Sym) { relocSect(ctxt, ctxt.Out, sect, syms) } } return fn, &wg }