func progedit(ctxt *liblink.Link, p *liblink.Prog) { var literal string var s *liblink.LSym var q *liblink.Prog // Thread-local storage references use the TLS pseudo-register. // As a register, TLS refers to the thread-local storage base, and it // can only be loaded into another register: // // MOVQ TLS, AX // // An offset from the thread-local storage base is written off(reg)(TLS*1). // Semantically it is off(reg), but the (TLS*1) annotation marks this as // indexing from the loaded TLS base. This emits a relocation so that // if the linker needs to adjust the offset, it can. For example: // // MOVQ TLS, AX // MOVQ 8(AX)(TLS*1), CX // load m into CX // // On systems that support direct access to the TLS memory, this // pair of instructions can be reduced to a direct TLS memory reference: // // MOVQ 8(TLS), CX // load m into CX // // The 2-instruction and 1-instruction forms correspond roughly to // ELF TLS initial exec mode and ELF TLS local exec mode, respectively. // // We applies this rewrite on systems that support the 1-instruction form. // The decision is made using only the operating system (and probably // the -shared flag, eventually), not the link mode. If some link modes // on a particular operating system require the 2-instruction form, // then all builds for that operating system will use the 2-instruction // form, so that the link mode decision can be delayed to link time. // // In this way, all supported systems use identical instructions to // access TLS, and they are rewritten appropriately first here in // liblink and then finally using relocations in the linker. if canuselocaltls(ctxt) { // Reduce TLS initial exec model to TLS local exec model. // Sequences like // MOVQ TLS, BX // ... off(BX)(TLS*1) ... // become // NOP // ... off(TLS) ... // // TODO(rsc): Remove the Hsolaris special case. It exists only to // guarantee we are producing byte-identical binaries as before this code. // But it should be unnecessary. if (p.As == AMOVQ || p.As == AMOVL) && p.From.Typ == D_TLS && D_AX <= p.To.Typ && p.To.Typ <= D_R15 && ctxt.Headtype != liblink.Hsolaris { nopout(p) } if p.From.Index == D_TLS && D_INDIR+D_AX <= p.From.Typ && p.From.Typ <= D_INDIR+D_R15 { p.From.Typ = D_INDIR + D_TLS p.From.Scale = 0 p.From.Index = D_NONE } if p.To.Index == D_TLS && D_INDIR+D_AX <= p.To.Typ && p.To.Typ <= D_INDIR+D_R15 { p.To.Typ = D_INDIR + D_TLS p.To.Scale = 0 p.To.Index = D_NONE } } else { // As a courtesy to the C compilers, rewrite TLS local exec load as TLS initial exec load. // The instruction // MOVQ off(TLS), BX // becomes the sequence // MOVQ TLS, BX // MOVQ off(BX)(TLS*1), BX // This allows the C compilers to emit references to m and g using the direct off(TLS) form. if (p.As == AMOVQ || p.As == AMOVL) && p.From.Typ == D_INDIR+D_TLS && D_AX <= p.To.Typ && p.To.Typ <= D_R15 { q = liblink.Appendp(ctxt, p) q.As = p.As q.From = p.From q.From.Typ = D_INDIR + p.To.Typ q.From.Index = D_TLS q.From.Scale = 2 // TODO: use 1 q.To = p.To p.From.Typ = D_TLS p.From.Index = D_NONE p.From.Offset = 0 } } // TODO: Remove. if ctxt.Headtype == liblink.Hwindows || ctxt.Headtype == liblink.Hplan9 { if p.From.Scale == 1 && p.From.Index == D_TLS { p.From.Scale = 2 } if p.To.Scale == 1 && p.To.Index == D_TLS { p.To.Scale = 2 } } if ctxt.Headtype == liblink.Hnacl { nacladdr(ctxt, p, &p.From) nacladdr(ctxt, p, &p.To) } // Maintain information about code generation mode. if ctxt.Mode == 0 { ctxt.Mode = 64 } p.Mode = ctxt.Mode switch p.As { case AMODE: if p.From.Typ == D_CONST || p.From.Typ == D_INDIR+D_NONE { switch int(p.From.Offset) { case 16, 32, 64: ctxt.Mode = int(p.From.Offset) break } } nopout(p) break } // Rewrite CALL/JMP/RET to symbol as D_BRANCH. switch p.As { case ACALL, AJMP, ARET: if (p.To.Typ == D_EXTERN || p.To.Typ == D_STATIC) && p.To.Sym != nil { p.To.Typ = D_BRANCH } break } // Rewrite float constants to values stored in memory. switch p.As { case AFMOVF, AFADDF, AFSUBF, AFSUBRF, AFMULF, AFDIVF, AFDIVRF, AFCOMF, AFCOMFP, AMOVSS, AADDSS, ASUBSS, AMULSS, ADIVSS, ACOMISS, AUCOMISS: if p.From.Typ == D_FCONST { var i32 uint32 var f32 float32 f32 = float32(p.From.U.Dval) i32 = math.Float32bits(f32) literal = fmt.Sprintf("$f32.%08x", uint32(i32)) s = liblink.Linklookup(ctxt, literal, 0) if s.Typ == 0 { s.Typ = liblink.SRODATA liblink.Adduint32(ctxt, s, i32) s.Reachable = 0 } p.From.Typ = D_EXTERN p.From.Sym = s p.From.Offset = 0 } case AFMOVD, AFADDD, AFSUBD, AFSUBRD, AFMULD, AFDIVD, AFDIVRD, AFCOMD, AFCOMDP, AMOVSD, AADDSD, ASUBSD, AMULSD, ADIVSD, ACOMISD, AUCOMISD: if p.From.Typ == D_FCONST { var i64 uint64 i64 = math.Float64bits(p.From.U.Dval) literal = fmt.Sprintf("$f64.%016x", uint64(i64)) s = liblink.Linklookup(ctxt, literal, 0) if s.Typ == 0 { s.Typ = liblink.SRODATA liblink.Adduint64(ctxt, s, i64) s.Reachable = 0 } p.From.Typ = D_EXTERN p.From.Sym = s p.From.Offset = 0 } break } }
func readsym(b *bufio.Reader, s *liblink.LSym) { if !undef[s] { panic("double-def") } delete(undef, s) s.Name = rdstring(b) s.Extname = rdstring(b) s.Typ = int(rdint(b)) s.Version = uint32(rdint(b)) s.Dupok = int(rdint(b)) s.External = uint8(rdint(b)) s.Nosplit = uint8(rdint(b)) s.Reachable = uint8(rdint(b)) s.Cgoexport = uint8(rdint(b)) s.Special = uint8(rdint(b)) s.Stkcheck = uint8(rdint(b)) s.Hide = uint8(rdint(b)) s.Leaf = uint8(rdint(b)) s.Fnptr = uint8(rdint(b)) s.Seenglobl = uint8(rdint(b)) s.Onlist = uint8(rdint(b)) s.Symid = int16(rdint(b)) s.Dynid = int(rdint(b)) s.Sig = int(rdint(b)) s.Plt = int(rdint(b)) s.Got = int(rdint(b)) s.Align = int(rdint(b)) s.Elfsym = int(rdint(b)) s.Args = int(rdint(b)) s.Locals = rdint(b) s.Value = rdint(b) s.Size = rdint(b) s.Hash = rdsym(b) s.Allsym = rdsym(b) s.Next = rdsym(b) s.Sub = rdsym(b) s.Outer = rdsym(b) s.Gotype = rdsym(b) s.Reachparent = rdsym(b) s.Queue = rdsym(b) s.File = rdstring(b) s.Dynimplib = rdstring(b) s.Dynimpvers = rdstring(b) s.Text = rdprog(b) s.Etext = rdprog(b) n := int(rdint(b)) if n > 0 { s.P = make([]byte, n) io.ReadFull(b, s.P) } s.R = make([]liblink.Reloc, int(rdint(b))) for i := range s.R { r := &s.R[i] r.Off = rdint(b) r.Siz = uint8(rdint(b)) r.Done = uint8(rdint(b)) r.Typ = int(rdint(b)) r.Add = rdint(b) r.Xadd = rdint(b) r.Sym = rdsym(b) r.Xsym = rdsym(b) } }
func progedit(ctxt *liblink.Link, p *liblink.Prog) { var literal string var s *liblink.LSym var tlsfallback *liblink.LSym p.From.Class = 0 p.To.Class = 0 // Rewrite B/BL to symbol as D_BRANCH. switch p.As { case AB, ABL, ADUFFZERO, ADUFFCOPY: if p.To.Typ == D_OREG && (p.To.Name == D_EXTERN || p.To.Name == D_STATIC) && p.To.Sym != nil { p.To.Typ = D_BRANCH } break } // Replace TLS register fetches on older ARM procesors. switch p.As { // If the instruction matches MRC 15, 0, <reg>, C13, C0, 3, replace it. case AMRC: if ctxt.Goarm < 7 && p.To.Offset&0xffff0fff == 0xee1d0f70 { tlsfallback = liblink.Linklookup(ctxt, "runtime.read_tls_fallback", 0) // BL runtime.read_tls_fallback(SB) p.As = ABL p.To.Typ = D_BRANCH p.To.Sym = tlsfallback p.To.Offset = 0 } else { // Otherwise, MRC/MCR instructions need no further treatment. p.As = AWORD } break } // Rewrite float constants to values stored in memory. switch p.As { case AMOVF: if p.From.Typ == D_FCONST && chipfloat5(ctxt, p.From.U.Dval) < 0 && (chipzero5(ctxt, p.From.U.Dval) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { var i32 uint32 var f32 float32 f32 = float32(p.From.U.Dval) i32 = math.Float32bits(f32) literal = fmt.Sprintf("$f32.%08x", i32) s = liblink.Linklookup(ctxt, literal, 0) if s.Typ == 0 { s.Typ = liblink.SRODATA liblink.Adduint32(ctxt, s, i32) s.Reachable = 0 } p.From.Typ = D_OREG p.From.Sym = s p.From.Name = D_EXTERN p.From.Offset = 0 } case AMOVD: if p.From.Typ == D_FCONST && chipfloat5(ctxt, p.From.U.Dval) < 0 && (chipzero5(ctxt, p.From.U.Dval) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { var i64 uint64 i64 = math.Float64bits(p.From.U.Dval) literal = fmt.Sprintf("$f64.%016x", uint64(i64)) s = liblink.Linklookup(ctxt, literal, 0) if s.Typ == 0 { s.Typ = liblink.SRODATA liblink.Adduint64(ctxt, s, i64) s.Reachable = 0 } p.From.Typ = D_OREG p.From.Sym = s p.From.Name = D_EXTERN p.From.Offset = 0 } break } if ctxt.Flag_shared != 0 { // Shared libraries use R_ARM_TLS_IE32 instead of // R_ARM_TLS_LE32, replacing the link time constant TLS offset in // runtime.tlsg with an address to a GOT entry containing the // offset. Rewrite $runtime.tlsg(SB) to runtime.tlsg(SB) to // compensate. if ctxt.Tlsg == nil { ctxt.Tlsg = liblink.Linklookup(ctxt, "runtime.tlsg", 0) } if p.From.Typ == D_CONST && p.From.Name == D_EXTERN && p.From.Sym == ctxt.Tlsg { p.From.Typ = D_OREG } if p.To.Typ == D_CONST && p.To.Name == D_EXTERN && p.To.Sym == ctxt.Tlsg { p.To.Typ = D_OREG } } }
func progedit(ctxt *liblink.Link, p *liblink.Prog) { var literal string var s *liblink.LSym var q *liblink.Prog // See obj6.c for discussion of TLS. if canuselocaltls(ctxt) { // Reduce TLS initial exec model to TLS local exec model. // Sequences like // MOVL TLS, BX // ... off(BX)(TLS*1) ... // become // NOP // ... off(TLS) ... if p.As == AMOVL && p.From.Typ == D_TLS && D_AX <= p.To.Typ && p.To.Typ <= D_DI { p.As = ANOP p.From.Typ = D_NONE p.To.Typ = D_NONE } if p.From.Index == D_TLS && D_INDIR+D_AX <= p.From.Typ && p.From.Typ <= D_INDIR+D_DI { p.From.Typ = D_INDIR + D_TLS p.From.Scale = 0 p.From.Index = D_NONE } if p.To.Index == D_TLS && D_INDIR+D_AX <= p.To.Typ && p.To.Typ <= D_INDIR+D_DI { p.To.Typ = D_INDIR + D_TLS p.To.Scale = 0 p.To.Index = D_NONE } } else { // As a courtesy to the C compilers, rewrite TLS local exec load as TLS initial exec load. // The instruction // MOVL off(TLS), BX // becomes the sequence // MOVL TLS, BX // MOVL off(BX)(TLS*1), BX // This allows the C compilers to emit references to m and g using the direct off(TLS) form. if p.As == AMOVL && p.From.Typ == D_INDIR+D_TLS && D_AX <= p.To.Typ && p.To.Typ <= D_DI { q = liblink.Appendp(ctxt, p) q.As = p.As q.From = p.From q.From.Typ = D_INDIR + p.To.Typ q.From.Index = D_TLS q.From.Scale = 2 // TODO: use 1 q.To = p.To p.From.Typ = D_TLS p.From.Index = D_NONE p.From.Offset = 0 } } // TODO: Remove. if ctxt.Headtype == liblink.Hplan9 { if p.From.Scale == 1 && p.From.Index == D_TLS { p.From.Scale = 2 } if p.To.Scale == 1 && p.To.Index == D_TLS { p.To.Scale = 2 } } // Rewrite CALL/JMP/RET to symbol as D_BRANCH. switch p.As { case ACALL, AJMP, ARET: if (p.To.Typ == D_EXTERN || p.To.Typ == D_STATIC) && p.To.Sym != nil { p.To.Typ = D_BRANCH } break } // Rewrite float constants to values stored in memory. switch p.As { case AFMOVF, AFADDF, AFSUBF, AFSUBRF, AFMULF, AFDIVF, AFDIVRF, AFCOMF, AFCOMFP, AMOVSS, AADDSS, ASUBSS, AMULSS, ADIVSS, ACOMISS, AUCOMISS: if p.From.Typ == D_FCONST { var i32 uint32 var f32 float32 f32 = float32(p.From.U.Dval) i32 = math.Float32bits(f32) literal = fmt.Sprintf("$f32.%08x", uint32(i32)) s = liblink.Linklookup(ctxt, literal, 0) if s.Typ == 0 { s.Typ = liblink.SRODATA liblink.Adduint32(ctxt, s, i32) s.Reachable = 0 } p.From.Typ = D_EXTERN p.From.Sym = s p.From.Offset = 0 } case AFMOVD, AFADDD, AFSUBD, AFSUBRD, AFMULD, AFDIVD, AFDIVRD, AFCOMD, AFCOMDP, AMOVSD, AADDSD, ASUBSD, AMULSD, ADIVSD, ACOMISD, AUCOMISD: if p.From.Typ == D_FCONST { var i64 uint64 i64 = math.Float64bits(p.From.U.Dval) literal = fmt.Sprintf("$f64.%016x", uint64(i64)) s = liblink.Linklookup(ctxt, literal, 0) if s.Typ == 0 { s.Typ = liblink.SRODATA liblink.Adduint64(ctxt, s, i64) s.Reachable = 0 } p.From.Typ = D_EXTERN p.From.Sym = s p.From.Offset = 0 } break } }