| // 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 |
| } |