From 5c98a14e1e4ffe0e8754ca07e50c564e1a22d6c1 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Thu, 29 Aug 2013 10:25:27 +0000 Subject: * elf64-x86-64.c (elf_x86_64_check_tls_transition): Allow 64-bit -mcmodel=large -fpic TLS GD and LD sequences. (elf_x86_64_relocate_section): Handle -mcmodel=large -fpic TLS GD and LD sequences in GD->LE, GD->IE and LD->LE transitions. ld/testsuite/ * ld-x86-64/x86-64.exp: Add tlsld3, tlsgd7 and tlsgd8 tests. * ld-x86-64/tlspic1.s: Add -mcmodel=large -fpic TLS GD and LD sequences. * ld-x86-64/tlspic.dd: Adjusted. * ld-x86-64/tlspic.rd: Adjusted. * ld-x86-64/tlspic-nacl.rd: Adjusted. * ld-x86-64/tlsld3.dd: New test. * ld-x86-64/tlsld3.s: New file. * ld-x86-64/tlsgd7.dd: New test. * ld-x86-64/tlsgd7.s: New file. * ld-x86-64/tlsgd8.dd: New test. * ld-x86-64/tlsgd8.s: New file. --- bfd/elf64-x86-64.c | 124 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 104 insertions(+), 20 deletions(-) (limited to 'bfd/elf64-x86-64.c') diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c index 0da839a..a89485a 100644 --- a/bfd/elf64-x86-64.c +++ b/bfd/elf64-x86-64.c @@ -1089,6 +1089,7 @@ elf_x86_64_check_tls_transition (bfd *abfd, { unsigned int val; unsigned long r_symndx; + bfd_boolean largepic = FALSE; struct elf_link_hash_entry *h; bfd_vma offset; struct elf_x86_64_link_hash_table *htab; @@ -1126,16 +1127,32 @@ elf_x86_64_check_tls_transition (bfd *abfd, can transit to different access model. For 32bit, only leaq foo@tlsgd(%rip), %rdi .word 0x6666; rex64; call __tls_get_addr - can transit to different access model. */ + can transit to different access model. For largepic + we also support: + leaq foo@tlsgd(%rip), %rdi + movabsq $__tls_get_addr@pltoff, %rax + addq $rbx, %rax + call *%rax. */ static const unsigned char call[] = { 0x66, 0x66, 0x48, 0xe8 }; static const unsigned char leaq[] = { 0x66, 0x48, 0x8d, 0x3d }; - if ((offset + 12) > sec->size - || memcmp (contents + offset + 4, call, 4) != 0) + if ((offset + 12) > sec->size) return FALSE; - if (ABI_64_P (abfd)) + if (memcmp (contents + offset + 4, call, 4) != 0) + { + if (!ABI_64_P (abfd) + || (offset + 19) > sec->size + || offset < 3 + || memcmp (contents + offset - 3, leaq + 1, 3) != 0 + || memcmp (contents + offset + 4, "\x48\xb8", 2) != 0 + || memcmp (contents + offset + 14, "\x48\x01\xd8\xff\xd0", 5) + != 0) + return FALSE; + largepic = TRUE; + } + else if (ABI_64_P (abfd)) { if (offset < 4 || memcmp (contents + offset - 4, leaq, 4) != 0) @@ -1153,16 +1170,31 @@ elf_x86_64_check_tls_transition (bfd *abfd, /* Check transition from LD access model. Only leaq foo@tlsld(%rip), %rdi; call __tls_get_addr - can transit to different access model. */ + can transit to different access model. For largepic + we also support: + leaq foo@tlsld(%rip), %rdi + movabsq $__tls_get_addr@pltoff, %rax + addq $rbx, %rax + call *%rax. */ static const unsigned char lea[] = { 0x48, 0x8d, 0x3d }; if (offset < 3 || (offset + 9) > sec->size) return FALSE; - if (memcmp (contents + offset - 3, lea, 3) != 0 - || 0xe8 != *(contents + offset + 4)) + if (memcmp (contents + offset - 3, lea, 3) != 0) return FALSE; + + if (0xe8 != *(contents + offset + 4)) + { + if (!ABI_64_P (abfd) + || (offset + 19) > sec->size + || memcmp (contents + offset + 4, "\x48\xb8", 2) != 0 + || memcmp (contents + offset + 14, "\x48\x01\xd8\xff\xd0", 5) + != 0) + return FALSE; + largepic = TRUE; + } } r_symndx = htab->r_sym (rel[1].r_info); @@ -1174,8 +1206,10 @@ elf_x86_64_check_tls_transition (bfd *abfd, may be versioned. */ return (h != NULL && h->root.root.string != NULL - && (ELF32_R_TYPE (rel[1].r_info) == R_X86_64_PC32 - || ELF32_R_TYPE (rel[1].r_info) == R_X86_64_PLT32) + && (largepic + ? ELF32_R_TYPE (rel[1].r_info) == R_X86_64_PLTOFF64 + : (ELF32_R_TYPE (rel[1].r_info) == R_X86_64_PC32 + || ELF32_R_TYPE (rel[1].r_info) == R_X86_64_PLT32)) && (strncmp (h->root.root.string, "__tls_get_addr", 14) == 0)); @@ -3949,8 +3983,26 @@ direct: .word 0x6666; rex64; call __tls_get_addr into: movl %fs:0, %eax - leaq foo@tpoff(%rax), %rax */ - if (ABI_64_P (output_bfd)) + leaq foo@tpoff(%rax), %rax + For largepic, change: + leaq foo@tlsgd(%rip), %rdi + movabsq $__tls_get_addr@pltoff, %rax + addq %rbx, %rax + call *%rax + into: + movq %fs:0, %rax + leaq foo@tpoff(%rax), %rax + nopw 0x0(%rax,%rax,1) */ + int largepic = 0; + if (ABI_64_P (output_bfd) + && contents[roff + 5] == (bfd_byte) '\xb8') + { + memcpy (contents + roff - 3, + "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x8d\x80" + "\0\0\0\0\x66\x0f\x1f\x44\0", 22); + largepic = 1; + } + else if (ABI_64_P (output_bfd)) memcpy (contents + roff - 4, "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x8d\x80\0\0\0", 16); @@ -3960,8 +4012,8 @@ direct: 15); bfd_put_32 (output_bfd, elf_x86_64_tpoff (info, relocation), - contents + roff + 8); - /* Skip R_X86_64_PC32/R_X86_64_PLT32. */ + contents + roff + 8 + largepic); + /* Skip R_X86_64_PC32/R_X86_64_PLT32/R_X86_64_PLTOFF64. */ rel++; continue; } @@ -4196,8 +4248,26 @@ direct: .word 0x6666; rex64; call __tls_get_addr@plt into: movl %fs:0, %eax - addq foo@gottpoff(%rip), %rax */ - if (ABI_64_P (output_bfd)) + addq foo@gottpoff(%rip), %rax + For largepic, change: + leaq foo@tlsgd(%rip), %rdi + movabsq $__tls_get_addr@pltoff, %rax + addq %rbx, %rax + call *%rax + into: + movq %fs:0, %rax + addq foo@gottpoff(%rax), %rax + nopw 0x0(%rax,%rax,1) */ + int largepic = 0; + if (ABI_64_P (output_bfd) + && contents[roff + 5] == (bfd_byte) '\xb8') + { + memcpy (contents + roff - 3, + "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x03\x05" + "\0\0\0\0\x66\x0f\x1f\x44\0", 22); + largepic = 1; + } + else if (ABI_64_P (output_bfd)) memcpy (contents + roff - 4, "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x03\x05\0\0\0", 16); @@ -4209,12 +4279,13 @@ direct: relocation = (htab->elf.sgot->output_section->vma + htab->elf.sgot->output_offset + off - roff + - largepic - input_section->output_section->vma - input_section->output_offset - 12); bfd_put_32 (output_bfd, relocation, - contents + roff + 8); - /* Skip R_X86_64_PLT32. */ + contents + roff + 8 + largepic); + /* Skip R_X86_64_PLT32/R_X86_64_PLTOFF64. */ rel++; continue; } @@ -4276,16 +4347,29 @@ direct: For 64bit, we change it into: .word 0x6666; .byte 0x66; movq %fs:0, %rax. For 32bit, we change it into: - nopl 0x0(%rax); movl %fs:0, %eax. */ + nopl 0x0(%rax); movl %fs:0, %eax. + For largepic, change: + leaq foo@tlsgd(%rip), %rdi + movabsq $__tls_get_addr@pltoff, %rax + addq %rbx, %rax + call *%rax + into: + data32 data32 data32 nopw %cs:0x0(%rax,%rax,1) + movq %fs:0, %eax */ BFD_ASSERT (r_type == R_X86_64_TPOFF32); - if (ABI_64_P (output_bfd)) + if (ABI_64_P (output_bfd) + && contents[rel->r_offset + 5] == (bfd_byte) '\xb8') + memcpy (contents + rel->r_offset - 3, + "\x66\x66\x66\x66\x2e\x0f\x1f\x84\0\0\0\0\0" + "\x64\x48\x8b\x04\x25\0\0\0", 22); + else if (ABI_64_P (output_bfd)) memcpy (contents + rel->r_offset - 3, "\x66\x66\x66\x64\x48\x8b\x04\x25\0\0\0", 12); else memcpy (contents + rel->r_offset - 3, "\x0f\x1f\x40\x00\x64\x8b\x04\x25\0\0\0", 12); - /* Skip R_X86_64_PC32/R_X86_64_PLT32. */ + /* Skip R_X86_64_PC32/R_X86_64_PLT32/R_X86_64_PLTOFF64. */ rel++; continue; } -- cgit v1.1