aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bfd/elf-s390-common.c81
-rw-r--r--bfd/elf64-s390.c143
-rw-r--r--ld/emulparams/elf64_s390.sh3
-rw-r--r--ld/testsuite/ld-s390/gotreloc_64-norelro-1.dd (renamed from ld/testsuite/ld-s390/gotreloc_64-1.dd)0
-rw-r--r--ld/testsuite/ld-s390/gotreloc_64-relro-1.dd12
-rw-r--r--ld/testsuite/ld-s390/s390.exp11
6 files changed, 188 insertions, 62 deletions
diff --git a/bfd/elf-s390-common.c b/bfd/elf-s390-common.c
index b37cb25..032ba27 100644
--- a/bfd/elf-s390-common.c
+++ b/bfd/elf-s390-common.c
@@ -30,6 +30,87 @@ s390_is_ifunc_symbol_p (struct elf_link_hash_entry *h)
return h->type == STT_GNU_IFUNC || eh->ifunc_resolver_address != 0;
}
+/* Return true if .got.plt is supposed to be emitted after .got. */
+
+static inline bfd_boolean
+s390_gotplt_after_got_p (struct bfd_link_info *info)
+{
+ struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
+
+ if (!htab->elf.sgot || !htab->elf.sgotplt)
+ return TRUE;
+
+ if (htab->elf.sgot->output_section == htab->elf.sgotplt->output_section)
+ {
+ if (htab->elf.sgot->output_offset < htab->elf.sgotplt->output_offset)
+ return TRUE;
+ }
+ else
+ {
+ if (htab->elf.sgot->output_section->vma
+ <= htab->elf.sgotplt->output_section->vma)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Return the value of the _GLOBAL_OFFSET_TABLE_ symbol. */
+
+static inline bfd_vma
+s390_got_pointer (struct bfd_link_info *info)
+{
+ struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
+ bfd_vma got_pointer;
+
+ BFD_ASSERT (htab && htab->elf.hgot);
+
+ got_pointer = (htab->elf.hgot->root.u.def.section->output_section->vma
+ + htab->elf.hgot->root.u.def.section->output_offset);
+ /* Our ABI requires the GOT pointer to point at the very beginning
+ of the global offset table. */
+ BFD_ASSERT (got_pointer
+ <= (htab->elf.sgot->output_section->vma
+ + htab->elf.sgot->output_offset));
+ BFD_ASSERT (got_pointer
+ <= (htab->elf.sgotplt->output_section->vma
+ + htab->elf.sgotplt->output_offset));
+
+ return got_pointer;
+}
+
+
+/* Return the offset of the .got versus _GLOBAL_OFFSET_TABLE_. */
+
+static inline bfd_vma
+s390_got_offset (struct bfd_link_info *info)
+{
+ struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
+
+ /* The absolute address of the .got in the target image. */
+ bfd_vma got_address = (htab->elf.sgot->output_section->vma
+ + htab->elf.sgot->output_offset);
+
+ /* GOT offset must not be negative. */
+ BFD_ASSERT (s390_got_pointer (info) <= got_address);
+ return got_address - s390_got_pointer (info);
+}
+
+/* Return the offset of the .got.plt versus _GLOBAL_OFFSET_TABLE_. */
+
+static inline bfd_vma
+s390_gotplt_offset (struct bfd_link_info *info)
+{
+ struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
+
+ /* The absolute address of the .got.plt in the target image. */
+ bfd_vma gotplt_address = (htab->elf.sgotplt->output_section->vma
+ + htab->elf.sgotplt->output_offset);
+
+ /* GOT offset must not be negative. */
+ BFD_ASSERT (s390_got_pointer (info) <= gotplt_address);
+ return gotplt_address - s390_got_pointer (info);
+}
+
/* Create sections needed by STT_GNU_IFUNC symbol. */
static bfd_boolean
diff --git a/bfd/elf64-s390.c b/bfd/elf64-s390.c
index ea10a89..0a9bfe4 100644
--- a/bfd/elf64-s390.c
+++ b/bfd/elf64-s390.c
@@ -481,7 +481,7 @@ elf_s390_is_local_label_name (bfd *abfd, const char *name)
#define RELA_ENTRY_SIZE sizeof (Elf64_External_Rela)
-/* The first three entries in a procedure linkage table are reserved,
+/* The first three entries in a global offset table are reserved,
and the initial contents are unimportant (we zero them out).
Subsequent entries look like this. See the SVR4 ABI 386
supplement to see how this works. */
@@ -511,8 +511,8 @@ elf_s390_is_local_label_name (bfd *abfd, const char *name)
LG 1,0(1) # 6 bytes Load address from GOT in r1
BCR 15,1 # 2 bytes Jump to address
RET1: BASR 1,0 # 2 bytes Return from GOT 1st time
- LGF 1,12(1) # 6 bytes Load offset in symbl table in r1
- BRCL 15,-x # 6 bytes Jump to start of PLT
+ LGF 1,12(1) # 6 bytes Load rela.plt offset into r1
+ BRCL 15,-x # 6 bytes Jump to first PLT entry
.long ? # 4 bytes offset into .rela.plt
Total = 32 bytes per PLT entry
@@ -1605,8 +1605,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h,
/* Make room for this entry. */
s->size += PLT_ENTRY_SIZE;
- /* We also need to make an entry in the .got.plt section, which
- will be placed in the .got section by the linker script. */
+ /* We also need to make an entry in the .got.plt section. */
htab->elf.sgotplt->size += GOT_ENTRY_SIZE;
/* We also need to make an entry in the .rela.plt section. */
@@ -1831,6 +1830,20 @@ elf_s390_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
}
}
+ if (htab->elf.sgot && s390_gotplt_after_got_p (info))
+ {
+ /* _bfd_elf_create_got_section adds the got header size always
+ to .got.plt but we need it in .got if this section comes
+ first. */
+ htab->elf.sgot->size += 3 * GOT_ENTRY_SIZE;
+ htab->elf.sgotplt->size -= 3 * GOT_ENTRY_SIZE;
+
+ /* Make the _GLOBAL_OFFSET_TABLE_ symbol point to the .got
+ instead of .got.plt. */
+ htab->elf.hgot->root.u.def.section = htab->elf.sgot;
+ htab->elf.hgot->root.u.def.value = 0;
+ }
+
/* Set up .got offsets for local syms, and space for local dynamic
relocs. */
for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
@@ -2131,7 +2144,6 @@ elf_s390_relocate_section (bfd *output_bfd,
bfd_boolean unresolved_reloc;
bfd_reloc_status_type r;
int tls_type;
- asection *base_got = htab->elf.sgot;
bfd_boolean resolved_to_zero;
r_type = ELF64_R_TYPE (rel->r_info);
@@ -2172,7 +2184,7 @@ elf_s390_relocate_section (bfd *output_bfd,
case R_390_PLTOFF16:
case R_390_PLTOFF32:
case R_390_PLTOFF64:
- relocation -= htab->elf.sgot->output_section->vma;
+ relocation -= s390_got_pointer (info);
break;
case R_390_GOTPLT12:
case R_390_GOTPLT16:
@@ -2192,10 +2204,10 @@ elf_s390_relocate_section (bfd *output_bfd,
htab->elf.sgot->contents +
local_got_offsets[r_symndx]);
relocation = (local_got_offsets[r_symndx] +
- htab->elf.sgot->output_offset);
+ s390_got_offset (info));
if (r_type == R_390_GOTENT || r_type == R_390_GOTPLTENT)
- relocation += htab->elf.sgot->output_section->vma;
+ relocation += s390_got_pointer (info);
break;
}
default:
@@ -2254,25 +2266,23 @@ elf_s390_relocate_section (bfd *output_bfd,
if (s390_is_ifunc_symbol_p (h))
{
+ /* Entry indices of .iplt and .igot.plt match
+ 1:1. No magic PLT first entry here. */
plt_index = h->plt.offset / PLT_ENTRY_SIZE;
- relocation = (plt_index * GOT_ENTRY_SIZE +
- htab->elf.igotplt->output_offset);
- if (r_type == R_390_GOTPLTENT)
- relocation += htab->elf.igotplt->output_section->vma;
+ relocation = (plt_index * GOT_ENTRY_SIZE
+ + s390_gotplt_offset (info)
+ + htab->elf.igotplt->output_offset);
}
else
{
- /* Calc. index no.
- Current offset - size first entry / entry size. */
- plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) /
- PLT_ENTRY_SIZE;
-
- /* Offset in GOT is PLT index plus GOT headers(3)
- times 8, addr & GOT addr. */
- relocation = (plt_index + 3) * GOT_ENTRY_SIZE;
- if (r_type == R_390_GOTPLTENT)
- relocation += htab->elf.sgot->output_section->vma;
+ plt_index = ((h->plt.offset - PLT_FIRST_ENTRY_SIZE)
+ / PLT_ENTRY_SIZE);
+
+ relocation = (plt_index * GOT_ENTRY_SIZE
+ + s390_gotplt_offset (info));
}
+ if (r_type == R_390_GOTPLTENT)
+ relocation += s390_got_pointer (info);
unresolved_reloc = FALSE;
break;
}
@@ -2286,7 +2296,7 @@ elf_s390_relocate_section (bfd *output_bfd,
case R_390_GOTENT:
/* Relocation is to the entry for this symbol in the global
offset table. */
- if (base_got == NULL)
+ if (htab->elf.sgot == NULL)
abort ();
if (h != NULL)
@@ -2303,8 +2313,19 @@ elf_s390_relocate_section (bfd *output_bfd,
{
/* No explicit GOT usage so redirect to the
got.iplt slot. */
- base_got = htab->elf.igotplt;
- off = h->plt.offset / PLT_ENTRY_SIZE * GOT_ENTRY_SIZE;
+ relocation = (s390_gotplt_offset (info)
+ + htab->elf.igotplt->output_offset
+ + (h->plt.offset / PLT_ENTRY_SIZE
+ * GOT_ENTRY_SIZE));
+
+ /* For @GOTENT the relocation is against the offset between
+ the instruction and the symbols entry in the GOT and not
+ between the start of the GOT and the symbols entry. We
+ add the vma of the GOT to get the correct value. */
+ if (r_type == R_390_GOTENT || r_type == R_390_GOTPLTENT)
+ relocation += s390_got_pointer (info);
+
+ break;
}
else
{
@@ -2337,7 +2358,7 @@ elf_s390_relocate_section (bfd *output_bfd,
else
{
bfd_put_64 (output_bfd, relocation,
- base_got->contents + off);
+ htab->elf.sgot->contents + off);
h->got.offset |= 1;
}
@@ -2419,7 +2440,7 @@ elf_s390_relocate_section (bfd *output_bfd,
if (off >= (bfd_vma) -2)
abort ();
- relocation = base_got->output_offset + off;
+ relocation = s390_got_offset (info) + off;
/* For @GOTENT the relocation is against the offset between
the instruction and the symbols entry in the GOT and not
@@ -2427,7 +2448,7 @@ elf_s390_relocate_section (bfd *output_bfd,
add the vma of the GOT to get the correct value. */
if ( r_type == R_390_GOTENT
|| r_type == R_390_GOTPLTENT)
- relocation += base_got->output_section->vma;
+ relocation += s390_got_pointer (info);
break;
@@ -2445,22 +2466,17 @@ elf_s390_relocate_section (bfd *output_bfd,
relocation = (htab->elf.iplt->output_section->vma
+ htab->elf.iplt->output_offset
+ h->plt.offset
- - htab->elf.sgot->output_section->vma);
+ - s390_got_pointer (info));
goto do_relocation;
}
- /* Note that sgot->output_offset is not involved in this
- calculation. We always want the start of .got. If we
- defined _GLOBAL_OFFSET_TABLE in a different way, as is
- permitted by the ABI, we might have to change this
- calculation. */
- relocation -= htab->elf.sgot->output_section->vma;
+ relocation -= s390_got_pointer (info);
break;
case R_390_GOTPC:
case R_390_GOTPCDBL:
/* Use global offset table as symbol value. */
- relocation = htab->elf.sgot->output_section->vma;
+ relocation = s390_got_pointer (info);
unresolved_reloc = FALSE;
break;
@@ -2509,7 +2525,7 @@ elf_s390_relocate_section (bfd *output_bfd,
|| h->plt.offset == (bfd_vma) -1
|| (htab->elf.splt == NULL && !s390_is_ifunc_symbol_p (h)))
{
- relocation -= htab->elf.sgot->output_section->vma;
+ relocation -= s390_got_pointer (info);
break;
}
@@ -2517,12 +2533,12 @@ elf_s390_relocate_section (bfd *output_bfd,
relocation = (htab->elf.iplt->output_section->vma
+ htab->elf.iplt->output_offset
+ h->plt.offset
- - htab->elf.sgot->output_section->vma);
+ - s390_got_pointer (info));
else
relocation = (htab->elf.splt->output_section->vma
+ htab->elf.splt->output_offset
+ h->plt.offset
- - htab->elf.sgot->output_section->vma);
+ - s390_got_pointer (info));
unresolved_reloc = FALSE;
break;
@@ -3296,7 +3312,7 @@ elf_s390_finish_dynamic_symbol (bfd *output_bfd,
if (h->plt.offset != (bfd_vma) -1)
{
bfd_vma plt_index;
- bfd_vma got_offset;
+ bfd_vma gotplt_offset;
Elf_Internal_Rela rela;
bfd_byte *loc;
@@ -3325,18 +3341,25 @@ elf_s390_finish_dynamic_symbol (bfd *output_bfd,
Current offset - size first entry / entry size. */
plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) / PLT_ENTRY_SIZE;
- /* Offset in GOT is PLT index plus GOT headers(3) times 8,
- addr & GOT addr. */
- got_offset = (plt_index + 3) * GOT_ENTRY_SIZE;
+ /* The slots in the .got.plt correspond to the PLT slots in
+ the same order. */
+ gotplt_offset = plt_index * GOT_ENTRY_SIZE;
+
+ /* If .got.plt comes first it needs to contain the 3 header
+ entries. */
+ if (!s390_gotplt_after_got_p (info))
+ gotplt_offset += 3 * GOT_ENTRY_SIZE;
/* Fill in the blueprint of a PLT. */
memcpy (htab->elf.splt->contents + h->plt.offset, elf_s390x_plt_entry,
PLT_ENTRY_SIZE);
- /* Fixup the relative address to the GOT entry */
+ /* The first instruction in the PLT entry is a LARL loading
+ the address of the GOT slot. We write the 4 byte
+ immediate operand of the LARL instruction here. */
bfd_put_32 (output_bfd,
(htab->elf.sgotplt->output_section->vma +
- htab->elf.sgotplt->output_offset + got_offset
+ htab->elf.sgotplt->output_offset + gotplt_offset
- (htab->elf.splt->output_section->vma +
htab->elf.splt->output_offset +
h->plt.offset))/2,
@@ -3356,12 +3379,12 @@ elf_s390_finish_dynamic_symbol (bfd *output_bfd,
+ htab->elf.splt->output_offset
+ h->plt.offset
+ 14),
- htab->elf.sgotplt->contents + got_offset);
+ htab->elf.sgotplt->contents + gotplt_offset);
/* Fill in the entry in the .rela.plt section. */
rela.r_offset = (htab->elf.sgotplt->output_section->vma
+ htab->elf.sgotplt->output_offset
- + got_offset);
+ + gotplt_offset);
rela.r_info = ELF64_R_INFO (h->dynindx, R_390_JMP_SLOT);
rela.r_addend = 0;
loc = htab->elf.srelplt->contents + plt_index *
@@ -3568,8 +3591,8 @@ elf_s390_finish_dynamic_sections (bfd *output_bfd,
continue;
case DT_PLTGOT:
- s = htab->elf.sgotplt;
- dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
+ /* DT_PLTGOT matches _GLOBAL_OFFSET_TABLE_ */
+ dyn.d_un.d_ptr = s390_got_pointer (info);
break;
case DT_JMPREL:
@@ -3606,10 +3629,11 @@ elf_s390_finish_dynamic_sections (bfd *output_bfd,
/* fill in blueprint for plt 0 entry */
memcpy (htab->elf.splt->contents, elf_s390x_first_plt_entry,
PLT_FIRST_ENTRY_SIZE);
- /* Fixup relative address to start of GOT */
+ /* The second instruction in the first PLT entry is a LARL
+ loading the GOT pointer. Fill in the LARL immediate
+ address. */
bfd_put_32 (output_bfd,
- (htab->elf.sgotplt->output_section->vma
- + htab->elf.sgotplt->output_offset
+ (s390_got_pointer (info)
- htab->elf.splt->output_section->vma
- htab->elf.splt->output_offset - 6)/2,
htab->elf.splt->contents + 8);
@@ -3619,21 +3643,22 @@ elf_s390_finish_dynamic_sections (bfd *output_bfd,
= PLT_ENTRY_SIZE;
}
- if (htab->elf.sgotplt)
+ if (htab->elf.hgot && htab->elf.hgot->root.u.def.section)
{
/* Fill in the first three entries in the global offset table. */
- if (htab->elf.sgotplt->size > 0)
+ if (htab->elf.hgot->root.u.def.section->size > 0)
{
bfd_put_64 (output_bfd,
(sdyn == NULL ? (bfd_vma) 0
: sdyn->output_section->vma + sdyn->output_offset),
- htab->elf.sgotplt->contents);
+ htab->elf.hgot->root.u.def.section->contents);
/* One entry for shared object struct ptr. */
- bfd_put_64 (output_bfd, (bfd_vma) 0, htab->elf.sgotplt->contents + 8);
+ bfd_put_64 (output_bfd, (bfd_vma) 0,
+ htab->elf.hgot->root.u.def.section->contents + 8);
/* One entry for _dl_runtime_resolve. */
- bfd_put_64 (output_bfd, (bfd_vma) 0, htab->elf.sgotplt->contents + 16);
+ bfd_put_64 (output_bfd, (bfd_vma) 0,
+ htab->elf.hgot->root.u.def.section->contents + 16);
}
-
elf_section_data (htab->elf.sgot->output_section)
->this_hdr.sh_entsize = 8;
}
diff --git a/ld/emulparams/elf64_s390.sh b/ld/emulparams/elf64_s390.sh
index 5309774..8d02042 100644
--- a/ld/emulparams/elf64_s390.sh
+++ b/ld/emulparams/elf64_s390.sh
@@ -11,9 +11,12 @@ NOP=0x07070707
TEMPLATE_NAME=elf32
GENERATE_SHLIB_SCRIPT=yes
GENERATE_PIE_SCRIPT=yes
+GENERATE_RELRO_SCRIPT=yes
NO_SMALL_DATA=yes
EXTRA_EM_FILE=s390
IREL_IN_PLT=
+SEPARATE_GOTPLT=0
+test -z "$RELRO" && unset SEPARATE_GOTPLT
# Treat a host that matches the target with the possible exception of "x"
# in the name as if it were native.
diff --git a/ld/testsuite/ld-s390/gotreloc_64-1.dd b/ld/testsuite/ld-s390/gotreloc_64-norelro-1.dd
index 8c8c619..8c8c619 100644
--- a/ld/testsuite/ld-s390/gotreloc_64-1.dd
+++ b/ld/testsuite/ld-s390/gotreloc_64-norelro-1.dd
diff --git a/ld/testsuite/ld-s390/gotreloc_64-relro-1.dd b/ld/testsuite/ld-s390/gotreloc_64-relro-1.dd
new file mode 100644
index 0000000..36f5001
--- /dev/null
+++ b/ld/testsuite/ld-s390/gotreloc_64-relro-1.dd
@@ -0,0 +1,12 @@
+tmpdir/gotreloc_64-1: file format elf64-s390
+
+Disassembly of section .text:
+
+.* <foo>:
+.*: c0 10 00 00 00 0e [ ]*larl %r1,.* <bar>
+.*: c0 10 00 00 00 0b [ ]*larl %r1,.* <bar>
+.*: c4 1d 00 00 0f 1a [ ]*lrl %r1,.* <_GLOBAL_OFFSET_TABLE_\+0x18>
+.*: 58 10 c0 18 [ ]*l %r1,24\(%r12\)
+.*: e3 10 c0 18 00 58 [ ]*ly %r1,24\(%r12\)
+.* <bar>:
+.*: 00 00 01 23 .long 0x00000123
diff --git a/ld/testsuite/ld-s390/s390.exp b/ld/testsuite/ld-s390/s390.exp
index e391841..1934d92 100644
--- a/ld/testsuite/ld-s390/s390.exp
+++ b/ld/testsuite/ld-s390/s390.exp
@@ -70,10 +70,15 @@ set s390xtests {
{{readelf -WSsrl tlsbin_64.rd} {objdump -dzrj.text tlsbin_64.dd}
{objdump -sj.got tlsbin_64.sd} {objdump -sj.tdata tlsbin_64.td}}
"tlsbin_64"}
- {"GOT: symbol address load from got to larl"
- "-shared -melf64_s390 --hash-style=sysv --version-script=gotreloc-1.ver" ""
+ {"GOT: norelro symbol address load from got to larl"
+ "-shared -melf64_s390 -z norelro --hash-style=sysv --version-script=gotreloc-1.ver" ""
+ "-m64" {gotreloc-1.s}
+ {{objdump -dzrj.text gotreloc_64-norelro-1.dd}}
+ "gotreloc_64-1"}
+ {"GOT: relro symbol address load from got to larl"
+ "-shared -melf64_s390 -z relro --hash-style=sysv --version-script=gotreloc-1.ver" ""
"-m64" {gotreloc-1.s}
- {{objdump -dzrj.text gotreloc_64-1.dd}}
+ {{objdump -dzrj.text gotreloc_64-relro-1.dd}}
"gotreloc_64-1"}
{"PLT: offset test"
"-shared -m elf64_s390 -dT pltoffset-1.ld" ""