diff options
author | Alan Modra <amodra@gmail.com> | 2017-02-20 17:20:45 +1030 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2017-02-20 21:20:06 +1030 |
commit | 499345cc67f44f27d7490d4c8996d3f9b5f76b1f (patch) | |
tree | 00de32fea4a99ac30fecd4d034dcaef4c22ea3c7 | |
parent | e99763c4b153bb4e0460dc8e3a11867702d62cd6 (diff) | |
download | gdb-499345cc67f44f27d7490d4c8996d3f9b5f76b1f.zip gdb-499345cc67f44f27d7490d4c8996d3f9b5f76b1f.tar.gz gdb-499345cc67f44f27d7490d4c8996d3f9b5f76b1f.tar.bz2 |
Alpha executables segfault when linked with -z,now
PR 21181
* elflink.c (bfd_elf_final_link): Make DT_REL/DT_RELA zero
if DT_RELSZ/DT_RELASZ is zero.
-rw-r--r-- | bfd/ChangeLog | 6 | ||||
-rw-r--r-- | bfd/elflink.c | 46 |
2 files changed, 37 insertions, 15 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index f20f79d..4976c23 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,9 @@ +2017-02-20 Alan Modra <amodra@gmail.com> + + PR 21181 + * elflink.c (bfd_elf_final_link): Make DT_REL/DT_RELA zero + if DT_RELSZ/DT_RELASZ is zero. + 2017-02-16 Alan Modra <amodra@gmail.com> PR 21000 diff --git a/bfd/elflink.c b/bfd/elflink.c index d7ed8ce..a9df6bd 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -12063,6 +12063,8 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info) Elf_Internal_Dyn dyn; const char *name; unsigned int type; + bfd_size_type sh_size; + bfd_vma sh_addr; bed->s->swap_dyn_in (dynobj, dyncon, &dyn); @@ -12196,8 +12198,8 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info) type = SHT_REL; else type = SHT_RELA; - dyn.d_un.d_val = 0; - dyn.d_un.d_ptr = 0; + sh_size = 0; + sh_addr = 0; for (i = 1; i < elf_numsections (abfd); i++) { Elf_Internal_Shdr *hdr; @@ -12206,28 +12208,42 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info) if (hdr->sh_type == type && (hdr->sh_flags & SHF_ALLOC) != 0) { - if (dyn.d_tag == DT_RELSZ || dyn.d_tag == DT_RELASZ) - dyn.d_un.d_val += hdr->sh_size; - else - { - if (dyn.d_un.d_ptr == 0 - || hdr->sh_addr < dyn.d_un.d_ptr) - dyn.d_un.d_ptr = hdr->sh_addr; - } + sh_size += hdr->sh_size; + if (sh_addr == 0 + || sh_addr > hdr->sh_addr) + sh_addr = hdr->sh_addr; } } + if (bed->dtrel_excludes_plt && htab->srelplt != NULL) { /* Don't count procedure linkage table relocs in the overall reloc count. */ - if (dyn.d_tag == DT_RELSZ || dyn.d_tag == DT_RELASZ) - dyn.d_un.d_val -= htab->srelplt->size; + sh_size -= htab->srelplt->size; + if (sh_size == 0) + /* If the size is zero, make the address zero too. + This is to avoid a glibc bug. If the backend + emits DT_RELA/DT_RELASZ even when DT_RELASZ is + zero, then we'll put DT_RELA at the end of + DT_JMPREL. glibc will interpret the end of + DT_RELA matching the end of DT_JMPREL as the + case where DT_RELA includes DT_JMPREL, and for + LD_BIND_NOW will decide that processing DT_RELA + will process the PLT relocs too. Net result: + No PLT relocs applied. */ + sh_addr = 0; + /* If .rela.plt is the first .rela section, exclude it from DT_RELA. */ - else if (dyn.d_un.d_ptr == (htab->srelplt->output_section->vma - + htab->srelplt->output_offset)) - dyn.d_un.d_ptr += htab->srelplt->size; + else if (sh_addr == (htab->srelplt->output_section->vma + + htab->srelplt->output_offset)) + sh_addr += htab->srelplt->size; } + + if (dyn.d_tag == DT_RELSZ || dyn.d_tag == DT_RELASZ) + dyn.d_un.d_val = sh_size; + else + dyn.d_un.d_ptr = sh_addr; break; } bed->s->swap_dyn_out (dynobj, &dyn, dyncon); |