- /* Nothing to do if there is no need or no output. */
- if ((sec->flags & (SEC_CODE | SEC_RELOC)) != (SEC_CODE | SEC_RELOC)
- || sec->need_convert_load == 0
- || bfd_is_abs_section (sec->output_section))
- return TRUE;
-
- symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-
- /* Load the relocations for this section. */
- internal_relocs = (_bfd_elf_link_read_relocs
- (abfd, sec, NULL, (Elf_Internal_Rela *) NULL,
- link_info->keep_memory));
- if (internal_relocs == NULL)
- return FALSE;
-
- htab = elf_x86_64_hash_table (link_info);
- changed_contents = FALSE;
- changed_relocs = FALSE;
- local_got_refcounts = elf_local_got_refcounts (abfd);
- maxpagesize = get_elf_backend_data (abfd)->maxpagesize;
-
- /* Get the section contents. */
- if (elf_section_data (sec)->this_hdr.contents != NULL)
- contents = elf_section_data (sec)->this_hdr.contents;
- else
- {
- if (!bfd_malloc_and_get_section (abfd, sec, &contents))
- goto error_return;
- }
-
- is_pic = bfd_link_pic (link_info);
-
- /* TRUE if we can convert only to R_X86_64_PC32. Enable it for
- --no-relax. */
- require_reloc_pc32
- = link_info->disable_target_specific_optimizations > 1;
-
- irelend = internal_relocs + sec->reloc_count;
- for (irel = internal_relocs; irel < irelend; irel++)
- {
- unsigned int r_type = ELF32_R_TYPE (irel->r_info);
- unsigned int r_symndx = htab->r_sym (irel->r_info);
- unsigned int indx;
- struct elf_link_hash_entry *h;
- asection *tsec;
- char symtype;
- bfd_vma toff, roff;
- bfd_signed_vma raddend;
- unsigned int opcode;
- unsigned int modrm;
- bfd_boolean relocx;
- bfd_boolean to_reloc_pc32;
-
- relocx = (r_type == R_X86_64_GOTPCRELX
- || r_type == R_X86_64_REX_GOTPCRELX);
- if (!relocx && r_type != R_X86_64_GOTPCREL)
- continue;
-
- roff = irel->r_offset;
- if (roff < (r_type == R_X86_64_REX_GOTPCRELX ? 3 : 2))
- continue;
-
- raddend = irel->r_addend;
- /* Addend for 32-bit PC-relative relocation must be -4. */
- if (raddend != -4)
- continue;
-
- opcode = bfd_get_8 (abfd, contents + roff - 2);
-
- /* Convert mov to lea since it has been done for a while. */
- if (opcode != 0x8b)
- {
- /* Only convert R_X86_64_GOTPCRELX and R_X86_64_REX_GOTPCRELX
- for call, jmp or one of adc, add, and, cmp, or, sbb, sub,
- test, xor instructions. */
- if (!relocx)
- continue;
- }
-
- /* We convert only to R_X86_64_PC32:
- 1. Branch.
- 2. R_X86_64_GOTPCREL since we can't modify REX byte.
- 3. require_reloc_pc32 is true.
- 4. PIC.
- */
- to_reloc_pc32 = (opcode == 0xff
- || !relocx
- || require_reloc_pc32
- || is_pic);
-
- /* Get the symbol referred to by the reloc. */
- if (r_symndx < symtab_hdr->sh_info)
- {
- Elf_Internal_Sym *isym;
-
- isym = bfd_sym_from_r_symndx (&htab->sym_cache,
- abfd, r_symndx);
-
- symtype = ELF_ST_TYPE (isym->st_info);
-
- /* STT_GNU_IFUNC must keep GOTPCREL relocations and skip
- relocation against undefined symbols. */
- if (symtype == STT_GNU_IFUNC || isym->st_shndx == SHN_UNDEF)
- continue;
-
- if (isym->st_shndx == SHN_ABS)
- tsec = bfd_abs_section_ptr;
- else if (isym->st_shndx == SHN_COMMON)
- tsec = bfd_com_section_ptr;
- else if (isym->st_shndx == SHN_X86_64_LCOMMON)
- tsec = &_bfd_elf_large_com_section;
- else
- tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
-
- h = NULL;
- toff = isym->st_value;
- }
- else
- {
- indx = r_symndx - symtab_hdr->sh_info;
- h = elf_sym_hashes (abfd)[indx];
- BFD_ASSERT (h != NULL);
-
- while (h->root.type == bfd_link_hash_indirect
- || h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
- /* STT_GNU_IFUNC must keep GOTPCREL relocations. We also
- avoid optimizing GOTPCREL relocations againt _DYNAMIC
- since ld.so may use its link-time address. */
- if (h->type == STT_GNU_IFUNC)
- continue;
-
- /* Undefined weak symbol is only bound locally in executable
- and its reference is resolved as 0 without relocation
- overflow. We can only perform this optimization for
- GOTPCRELX relocations since we need to modify REX byte.
- It is OK convert mov with R_X86_64_GOTPCREL to
- R_X86_64_PC32. */
- if ((relocx || opcode == 0x8b)
- && UNDEFINED_WEAK_RESOLVED_TO_ZERO (link_info,
- elf_x86_64_hash_entry (h)))
- {
- if (opcode == 0xff)
- {
- /* Skip for branch instructions since R_X86_64_PC32
- may overflow. */
- if (require_reloc_pc32)
- continue;
- }
- else if (relocx)
- {
- /* For non-branch instructions, we can convert to
- R_X86_64_32/R_X86_64_32S since we know if there
- is a REX byte. */
- to_reloc_pc32 = FALSE;
- }
-
- /* Since we don't know the current PC when PIC is true,
- we can't convert to R_X86_64_PC32. */
- if (to_reloc_pc32 && is_pic)
- continue;
-
- goto convert;
- }
- else if ((h->def_regular
- || h->root.type == bfd_link_hash_defined
- || h->root.type == bfd_link_hash_defweak)
- && h != htab->elf.hdynamic
- && SYMBOL_REFERENCES_LOCAL (link_info, h))
- {
- /* bfd_link_hash_new or bfd_link_hash_undefined is
- set by an assignment in a linker script in
- bfd_elf_record_link_assignment. */
- if (h->def_regular
- && (h->root.type == bfd_link_hash_new
- || h->root.type == bfd_link_hash_undefined))
- {
- /* Skip since R_X86_64_32/R_X86_64_32S may overflow. */
- if (require_reloc_pc32)
- continue;
- goto convert;
- }
- tsec = h->root.u.def.section;
- toff = h->root.u.def.value;
- symtype = h->type;
- }
- else
- continue;
- }
-
- /* We can only estimate relocation overflow for R_X86_64_PC32. */
- if (!to_reloc_pc32)
- goto convert;
-
- if (tsec->sec_info_type == SEC_INFO_TYPE_MERGE)
- {
- /* At this stage in linking, no SEC_MERGE symbol has been
- adjusted, so all references to such symbols need to be
- passed through _bfd_merged_section_offset. (Later, in
- relocate_section, all SEC_MERGE symbols *except* for
- section symbols have been adjusted.)
-
- gas may reduce relocations against symbols in SEC_MERGE
- sections to a relocation against the section symbol when
- the original addend was zero. When the reloc is against
- a section symbol we should include the addend in the
- offset passed to _bfd_merged_section_offset, since the
- location of interest is the original symbol. On the
- other hand, an access to "sym+addend" where "sym" is not
- a section symbol should not include the addend; Such an
- access is presumed to be an offset from "sym"; The
- location of interest is just "sym". */
- if (symtype == STT_SECTION)
- toff += raddend;
-
- toff = _bfd_merged_section_offset (abfd, &tsec,
- elf_section_data (tsec)->sec_info,
- toff);
-
- if (symtype != STT_SECTION)
- toff += raddend;
- }
- else
- toff += raddend;
-
- /* Don't convert if R_X86_64_PC32 relocation overflows. */
- if (tsec->output_section == sec->output_section)
- {
- if ((toff - roff + 0x80000000) > 0xffffffff)
- continue;
- }
- else
- {
- bfd_signed_vma distance;
-
- /* At this point, we don't know the load addresses of TSEC
- section nor SEC section. We estimate the distrance between
- SEC and TSEC. We store the estimated distances in the
- compressed_size field of the output section, which is only
- used to decompress the compressed input section. */
- if (sec->output_section->compressed_size == 0)
- {
- asection *asect;
- bfd_size_type size = 0;
- for (asect = link_info->output_bfd->sections;
- asect != NULL;
- asect = asect->next)
- {
- asection *i;
- for (i = asect->map_head.s;
- i != NULL;
- i = i->map_head.s)
- {
- size = align_power (size, i->alignment_power);
- size += i->size;
- }
- asect->compressed_size = size;
- }
- }
-
- /* Don't convert GOTPCREL relocations if TSEC isn't placed
- after SEC. */
- distance = (tsec->output_section->compressed_size
- - sec->output_section->compressed_size);
- if (distance < 0)
- continue;
-
- /* Take PT_GNU_RELRO segment into account by adding
- maxpagesize. */
- if ((toff + distance + maxpagesize - roff + 0x80000000)
- > 0xffffffff)
- continue;
- }
-
-convert:
- if (opcode == 0xff)
- {
- /* We have "call/jmp *foo@GOTPCREL(%rip)". */
- unsigned int nop;
- unsigned int disp;
- bfd_vma nop_offset;
-
- /* Convert R_X86_64_GOTPCRELX and R_X86_64_REX_GOTPCRELX to
- R_X86_64_PC32. */
- modrm = bfd_get_8 (abfd, contents + roff - 1);
- if (modrm == 0x25)
- {
- /* Convert to "jmp foo nop". */
- modrm = 0xe9;
- nop = NOP_OPCODE;
- nop_offset = irel->r_offset + 3;
- disp = bfd_get_32 (abfd, contents + irel->r_offset);
- irel->r_offset -= 1;
- bfd_put_32 (abfd, disp, contents + irel->r_offset);
- }
- else
- {
- /* Convert to "nop call foo". ADDR_PREFIX_OPCODE
- is a nop prefix. */
- modrm = 0xe8;
- nop = link_info->call_nop_byte;
- if (link_info->call_nop_as_suffix)
- {
- nop_offset = irel->r_offset + 3;
- disp = bfd_get_32 (abfd, contents + irel->r_offset);
- irel->r_offset -= 1;
- bfd_put_32 (abfd, disp, contents + irel->r_offset);
- }
- else
- nop_offset = irel->r_offset - 2;
- }
- bfd_put_8 (abfd, nop, contents + nop_offset);
- bfd_put_8 (abfd, modrm, contents + irel->r_offset - 1);
- r_type = R_X86_64_PC32;
- }
- else
- {
- unsigned int rex;
- unsigned int rex_mask = REX_R;
-
- if (r_type == R_X86_64_REX_GOTPCRELX)
- rex = bfd_get_8 (abfd, contents + roff - 3);
- else
- rex = 0;
-
- if (opcode == 0x8b)
- {
- if (to_reloc_pc32)
- {
- /* Convert "mov foo@GOTPCREL(%rip), %reg" to
- "lea foo(%rip), %reg". */
- opcode = 0x8d;
- r_type = R_X86_64_PC32;
- }
- else
- {
- /* Convert "mov foo@GOTPCREL(%rip), %reg" to
- "mov $foo, %reg". */
- opcode = 0xc7;
- modrm = bfd_get_8 (abfd, contents + roff - 1);
- modrm = 0xc0 | (modrm & 0x38) >> 3;
- if ((rex & REX_W) != 0
- && ABI_64_P (link_info->output_bfd))
- {
- /* Keep the REX_W bit in REX byte for LP64. */
- r_type = R_X86_64_32S;
- goto rewrite_modrm_rex;
- }
- else
- {
- /* If the REX_W bit in REX byte isn't needed,
- use R_X86_64_32 and clear the W bit to avoid
- sign-extend imm32 to imm64. */
- r_type = R_X86_64_32;
- /* Clear the W bit in REX byte. */
- rex_mask |= REX_W;
- goto rewrite_modrm_rex;
- }
- }
- }
- else
- {
- /* R_X86_64_PC32 isn't supported. */
- if (to_reloc_pc32)
- continue;
-
- modrm = bfd_get_8 (abfd, contents + roff - 1);
- if (opcode == 0x85)
- {
- /* Convert "test %reg, foo@GOTPCREL(%rip)" to
- "test $foo, %reg". */
- modrm = 0xc0 | (modrm & 0x38) >> 3;
- opcode = 0xf7;
- }
- else
- {
- /* Convert "binop foo@GOTPCREL(%rip), %reg" to
- "binop $foo, %reg". */
- modrm = 0xc0 | (modrm & 0x38) >> 3 | (opcode & 0x3c);
- opcode = 0x81;
- }