Skip to content

Commit

Permalink
cmd/internal/obj/x86, cmd/link/internal/x86: support IE model TLS on …
Browse files Browse the repository at this point in the history
…linux/386

This includes the first parts of the general approach to PIC: load PC into CX
whenever it is needed. This is going to lead to large binaries and poor
performance but it's a start and easy to get right.

Change-Id: Ic8bf1d0a74284cca0d94a68cf75024e8ab063b4e
Reviewed-on: https://go-review.googlesource.com/16383
Reviewed-by: Ian Lance Taylor <iant@golang.org>
  • Loading branch information
mwhudson committed Nov 18, 2015
1 parent 61da0e9 commit 3bf61fb
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 19 deletions.
85 changes: 67 additions & 18 deletions src/cmd/internal/obj/x86/asm6.go
Original file line number Diff line number Diff line change
Expand Up @@ -2018,6 +2018,21 @@ func prefixof(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
}

if p.Mode == 32 {
if a.Index == REG_TLS && ctxt.Flag_shared != 0 {
// When building for inclusion into a shared library, an instruction of the form
// MOVL 0(CX)(TLS*1), AX
// becomes
// mov %gs:(%ecx), %eax
// which assumes that the correct TLS offset has been loaded into %ecx (today
// there is only one TLS variable -- g -- so this is OK). When not building for
// a shared library the instruction it becomes
// mov 0x0(%ecx), $eax
// and a R_TLS_LE relocation, and so does not require a prefix.
if a.Offset != 0 {
ctxt.Diag("cannot handle non-0 offsets to TLS")
}
return 0x65 // GS
}
return 0
}

Expand Down Expand Up @@ -2518,10 +2533,12 @@ func vaddr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r *obj.Reloc) int64 {
log.Fatalf("reloc")
}

r.Type = obj.R_TLS_LE
r.Siz = 4
r.Off = -1 // caller must fill in
r.Add = a.Offset
if ctxt.Flag_shared == 0 {
r.Type = obj.R_TLS_LE
r.Siz = 4
r.Off = -1 // caller must fill in
r.Add = a.Offset
}
return 0
}

Expand Down Expand Up @@ -3922,20 +3939,52 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {

case obj.Hlinux,
obj.Hnacl:
// ELF TLS base is 0(GS).
pp.From = p.From

pp.From.Type = obj.TYPE_MEM
pp.From.Reg = REG_GS
pp.From.Offset = 0
pp.From.Index = REG_NONE
pp.From.Scale = 0
ctxt.Andptr[0] = 0x65
ctxt.Andptr = ctxt.Andptr[1:] // GS
ctxt.Andptr[0] = 0x8B
ctxt.Andptr = ctxt.Andptr[1:]
asmand(ctxt, p, &pp.From, &p.To)

if ctxt.Flag_shared != 0 {
// Note that this is not generating the same insns as the other cases.
// MOV TLS, R_to
// becomes
// call __x86.get_pc_thunk.cx
// movl (gotpc + g@gotntpoff)(%ecx),$R_To
// which is encoded as
// call __x86.get_pc_thunk.cx
// movq 0(%ecx), R_to
// and R_CALL & R_TLS_IE relocs. This all assumes the only tls variable we access
// is g, which we can't check here, but will when we assemble the second
// instruction.
ctxt.Andptr[0] = 0xe8
ctxt.Andptr = ctxt.Andptr[1:]
r = obj.Addrel(ctxt.Cursym)
r.Off = int32(p.Pc + int64(-cap(ctxt.Andptr)+cap(ctxt.And[:])))
r.Type = obj.R_CALL
r.Siz = 4
r.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk.cx", 0)
put4(ctxt, 0)

ctxt.Andptr[0] = 0x8B
ctxt.Andptr = ctxt.Andptr[1:]
ctxt.Andptr[0] = byte(2<<6 | reg[REG_CX] | (reg[p.To.Reg] << 3))
ctxt.Andptr = ctxt.Andptr[1:]
r = obj.Addrel(ctxt.Cursym)
r.Off = int32(p.Pc + int64(-cap(ctxt.Andptr)+cap(ctxt.And[:])))
r.Type = obj.R_TLS_IE
r.Siz = 4
r.Add = 2
put4(ctxt, 0)
} else {
// ELF TLS base is 0(GS).
pp.From = p.From

pp.From.Type = obj.TYPE_MEM
pp.From.Reg = REG_GS
pp.From.Offset = 0
pp.From.Index = REG_NONE
pp.From.Scale = 0
ctxt.Andptr[0] = 0x65
ctxt.Andptr = ctxt.Andptr[1:] // GS
ctxt.Andptr[0] = 0x8B
ctxt.Andptr = ctxt.Andptr[1:]
asmand(ctxt, p, &pp.From, &p.To)
}
case obj.Hplan9:
if ctxt.Plan9privates == nil {
ctxt.Plan9privates = obj.Linklookup(ctxt, "_privates", 0)
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/link/internal/ld/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -1669,7 +1669,7 @@ func stkcheck(up *Chain, depth int) int {
// should never be called directly.
// only diagnose the direct caller.
// TODO(mwhudson): actually think about this.
if depth == 1 && s.Type != obj.SXREF && !DynlinkingGo() {
if depth == 1 && s.Type != obj.SXREF && !DynlinkingGo() && Buildmode != BuildmodePIE {
Diag("call to external function %s", s.Name)
}
return -1
Expand Down
33 changes: 33 additions & 0 deletions src/cmd/link/internal/x86/asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,30 @@ import (
)

func gentext() {
if !ld.DynlinkingGo() && ld.Buildmode != ld.BuildmodePIE {
return
}

thunkfunc := ld.Linklookup(ld.Ctxt, "__x86.get_pc_thunk.cx", 0)
thunkfunc.Type = obj.STEXT
thunkfunc.Local = true
thunkfunc.Reachable = true
o := func(op ...uint8) {
for _, op1 := range op {
ld.Adduint8(ld.Ctxt, thunkfunc, op1)
}
}
// 8b 0c 24 mov (%esp),%ecx
o(0x8b, 0x0c, 0x24)
// c3 ret
o(0xc3)

if ld.Ctxt.Etextp != nil {
ld.Ctxt.Etextp.Next = thunkfunc
} else {
ld.Ctxt.Textp = thunkfunc
}
ld.Ctxt.Etextp = thunkfunc
}

func adddynrela(rela *ld.LSym, s *ld.LSym, r *ld.Reloc) {
Expand Down Expand Up @@ -257,6 +281,15 @@ func elfreloc1(r *ld.Reloc, sectoff int64) int {
} else {
return -1
}

case obj.R_TLS_IE:
if r.Siz == 4 {
ld.Thearch.Lput(ld.R_386_GOTPC)
ld.Thearch.Lput(uint32(sectoff))
ld.Thearch.Lput(ld.R_386_TLS_GOTIE | uint32(elfsym)<<8)
} else {
return -1
}
}

return 0
Expand Down
7 changes: 7 additions & 0 deletions src/cmd/link/internal/x86/obj.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ func archinit() {
ld.Linkmode = ld.LinkInternal
}

if ld.Buildmode == ld.BuildmodeCShared || ld.Buildmode == ld.BuildmodePIE || ld.DynlinkingGo() {
ld.Linkmode = ld.LinkExternal
got := ld.Linklookup(ld.Ctxt, "_GLOBAL_OFFSET_TABLE_", 0)
got.Type = obj.SDYNIMPORT
got.Reachable = true
}

switch ld.HEADTYPE {
default:
if ld.Linkmode == ld.LinkAuto {
Expand Down

0 comments on commit 3bf61fb

Please sign in to comment.