summaryrefslogtreecommitdiff
path: root/bfd/elf64-x86-64.c
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2013-08-29 10:25:26 +0000
committerJakub Jelinek <jakub@redhat.com>2013-08-29 10:25:26 +0000
commit2e5c876acf1b17fede02a8eb697fc1450e6138f5 (patch)
treeb5f739609e09853a64956dee86d58cd82184f733 /bfd/elf64-x86-64.c
parent1d0e78b47680ce4b23445e559a8b6858d55fcbc8 (diff)
downloadbinutils-redhat-2e5c876acf1b17fede02a8eb697fc1450e6138f5.tar.gz
* 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.
Diffstat (limited to 'bfd/elf64-x86-64.c')
-rw-r--r--bfd/elf64-x86-64.c124
1 files changed, 104 insertions, 20 deletions
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 0da839ac94..a89485a157 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;
}