Print this page
3337 x64 link-editor is painfully literal-minded about TLS
*** 512,521 ****
--- 512,538 ----
*/
0x64, 0x48, 0x8b, 0x04, 0x25,
0x00, 0x00, 0x00, 0x00
};
+ #define REX_B 0x1
+ #define REX_X 0x2
+ #define REX_R 0x4
+ #define REX_W 0x8
+ #define REX_PREFIX 0x40
+
+ #define REX_RW (REX_PREFIX | REX_R | REX_W)
+ #define REX_BW (REX_PREFIX | REX_B | REX_W)
+ #define REX_BRW (REX_PREFIX | REX_B | REX_R | REX_W)
+
+ #define REG_ESP 0x4
+
+ #define INSN_ADDMR 0x03 /* addq mem,reg */
+ #define INSN_ADDIR 0x81 /* addq imm,reg */
+ #define INSN_MOVMR 0x8b /* movq mem,reg */
+ #define INSN_MOVIR 0xc7 /* movq imm,reg */
+ #define INSN_LEA 0x8d /* leaq mem,reg */
static Fixupret
tls_fixups(Ofl_desc *ofl, Rel_desc *arsp)
{
Sym_desc *sdp = arsp->rel_sym;
*** 524,533 ****
--- 541,554 ----
offset = (uchar_t *)((uintptr_t)arsp->rel_roffset +
(uintptr_t)_elf_getxoff(arsp->rel_isdesc->is_indata) +
(uintptr_t)RELAUX_GET_OSDESC(arsp)->os_outdata->d_buf);
+ /*
+ * Note that in certain of the original insn sequences below, the
+ * instructions are not necessarily adjacent
+ */
if (sdp->sd_ref == REF_DYN_NEED) {
/*
* IE reference model
*/
switch (rtype) {
*** 603,641 ****
*/
offset -= 4;
(void) memcpy(offset, tlsinstr_gd_le, sizeof (tlsinstr_gd_le));
return (FIX_RELOC);
! case R_AMD64_GOTTPOFF:
/*
* IE -> LE
*
! * Transition:
! * 0x00 movq %fs:0, %rax
! * 0x09 addq x@gottopoff(%rip), %rax
! * 0x10
* To:
! * 0x00 movq %fs:0, %rax
! * 0x09 leaq x@tpoff(%rax), %rax
! * 0x10
*/
DBG_CALL(Dbg_reloc_transition(ofl->ofl_lml, M_MACH,
R_AMD64_TPOFF32, arsp, ld_reloc_sym_name));
arsp->rel_rtype = R_AMD64_TPOFF32;
arsp->rel_raddend = 0;
/*
! * Adjust 'offset' to beginning of instruction sequence.
! */
! offset -= 12;
/*
! * Same code sequence used in the GD -> LE transition.
*/
! (void) memcpy(offset, tlsinstr_gd_le, sizeof (tlsinstr_gd_le));
return (FIX_RELOC);
case R_AMD64_TLSLD:
/*
* LD -> LE
*
* Transition
--- 624,715 ----
*/
offset -= 4;
(void) memcpy(offset, tlsinstr_gd_le, sizeof (tlsinstr_gd_le));
return (FIX_RELOC);
! case R_AMD64_GOTTPOFF: {
/*
* IE -> LE
*
! * Transition 1:
! * movq %fs:0, %reg
! * addq x@gottpoff(%rip), %reg
* To:
! * movq %fs:0, %reg
! * leaq x@tpoff(%reg), %reg
! *
! * Transition (as a special case):
! * movq %fs:0, %r12/%rsp
! * addq x@gottpoff(%rip), %r12/%rsp
! * To:
! * movq %fs:0, %r12/%rsp
! * addq x@tpoff(%rax), %r12/%rsp
! *
! * Transition 2:
! * movq x@gottpoff(%rip), %reg
! * movq %fs:(%reg), %reg
! * To:
! * movq x@tpoff(%reg), %reg
! * movq %fs:(%reg), %reg
*/
+ Conv_inv_buf_t inv_buf;
+ uint8_t reg; /* Register */
+
+ offset -= 3;
+
+ reg = offset[2] >> 3; /* Encoded dest. reg. operand */
+
DBG_CALL(Dbg_reloc_transition(ofl->ofl_lml, M_MACH,
R_AMD64_TPOFF32, arsp, ld_reloc_sym_name));
arsp->rel_rtype = R_AMD64_TPOFF32;
arsp->rel_raddend = 0;
/*
! * This is transition 2, and the special case of form 1 where
! * a normal transition would index %rsp or %r12 and need a SIB
! * byte in the leaq for which we lack space
! */
! if ((offset[1] == INSN_MOVMR) ||
! ((offset[1] == INSN_ADDMR) && (reg == REG_ESP))) {
! /*
! * If we needed an extra bit of MOD.reg to refer to
! * this register as the dest of the original movq we
! * need an extra bit of MOD.rm to refer to it in the
! * dest of the replacement movq or addq.
! */
! if (offset[0] == REX_RW)
! offset[0] = REX_BW;
!
! offset[1] = (offset[1] == INSN_MOVMR) ?
! INSN_MOVIR : INSN_ADDIR;
! offset[2] = 0xc0 | reg;
+ return (FIX_RELOC);
+ } else if (offset[1] == INSN_ADDMR) {
/*
! * If we needed an extra bit of MOD.reg to refer to
! * this register in the dest of the addq we need an
! * extra bit of both MOD.reg and MOD.rm to refer to it
! * in the source and dest of the leaq
*/
! if (offset[0] == REX_RW)
! offset[0] = REX_BRW;
!
! offset[1] = INSN_LEA;
! offset[2] = 0x80 | (reg << 3) | reg;
!
return (FIX_RELOC);
+ }
+ ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_REL_BADTLSINS),
+ conv_reloc_amd64_type(arsp->rel_rtype, 0, &inv_buf),
+ arsp->rel_isdesc->is_file->ifl_name,
+ ld_reloc_sym_name(arsp),
+ arsp->rel_isdesc->is_name,
+ EC_OFF(arsp->rel_roffset));
+ return (FIX_ERROR);
+ }
case R_AMD64_TLSLD:
/*
* LD -> LE
*
* Transition