The PLT entry in executables and shared libraries contains an indirect
branch, like
jmp *foo@GOTPCREL(%rip)
push $index_foo
jmp .PLT0
or
endbr64
jmp *foo@GOTPCREL(%rip)
NOP padding
which is used to branch to the function, foo, defined in another object.
Each R_X86_64_JUMP_SLOT relocation has a corresponding PLT entry.
The dynamic tags have been added to the x86-64 psABI to mark such PLT
entries:
https://gitlab.com/x86-psABIs/x86-64-ABI/-/commit/
6d824a52a42d173eb838b879616c1be5870b593e
Add an x86-64 linker option, -z mark-plt, to mark PLT entries with
#define DT_X86_64_PLT (DT_LOPROC + 0)
#define DT_X86_64_PLTSZ (DT_LOPROC + 1)
#define DT_X86_64_PLTENT (DT_LOPROC + 3)
1. DT_X86_64_PLT: The address of the procedure linkage table.
2. DT_X86_64_PLTSZ: The total size, in bytes, of the procedure linkage
table.
3. DT_X86_64_PLTENT: The size, in bytes, of a procedure linkage table
entry.
and set the r_addend field of the R_X86_64_JUMP_SLOT relocation to the
memory offset of the indirect branch instruction. The dynamic linker
can use these tags to update the PLT section to direct branch.
bfd/
* elf-linker-x86.h (elf_linker_x86_params): Add mark_plt.
* elf64-x86-64.c (elf_x86_64_finish_dynamic_symbol): Set the
r_addend of R_X86_64_JUMP_SLOT to the indirect branch offset
in PLT entry for -z mark-plt.
* elfxx-x86.c (_bfd_x86_elf_size_dynamic_sections): Add
DT_X86_64_PLT, DT_X86_64_PLTSZ and DT_X86_64_PLTENT for
-z mark-plt.
(_bfd_x86_elf_finish_dynamic_sections): Set DT_X86_64_PLT,
DT_X86_64_PLTSZ and DT_X86_64_PLTENT.
(_bfd_x86_elf_get_synthetic_symtab): Ignore addend for
JUMP_SLOT relocation.
(_bfd_x86_elf_link_setup_gnu_properties): Set
plt_indirect_branch_offset.
* elfxx-x86.h (elf_x86_plt_layout): Add plt_indirect_branch_offset.
binutils/
* readelf.c (get_x86_64_dynamic_type): New function.
(get_dynamic_type): Call get_x86_64_dynamic_type.
include/
* elf/x86-64.h (DT_X86_64_PLT): New.
(DT_X86_64_PLTSZ): Likewise.
(DT_X86_64_PLTENT): Likewise.
ld/
* ld.texi: Document -z mark-plt and -z nomark-plt.
* emulparams/elf32_x86_64.sh: Source x86-64-plt.sh.
* emulparams/elf_x86_64.sh: Likewise.
* emulparams/x86-64-plt.sh: New file.
* testsuite/ld-x86-64/mark-plt-1.s: Likewise.
* testsuite/ld-x86-64/mark-plt-1a-x32.d: Likewise.
* testsuite/ld-x86-64/mark-plt-1a.d: Likewise.
* testsuite/ld-x86-64/mark-plt-1b-x32.d: Likewise.
* testsuite/ld-x86-64/mark-plt-1b.d: Likewise.
* testsuite/ld-x86-64/mark-plt-1c-x32.d: Likewise.
* testsuite/ld-x86-64/mark-plt-1c.d: Likewise.
* testsuite/ld-x86-64/mark-plt-1d-x32.d: Likewise.
* testsuite/ld-x86-64/mark-plt-1d.d: Likewise.
* testsuite/ld-x86-64/x86-64.exp: Run -z mark-plt tests.
/* Report relative relocations. */
unsigned int report_relative_reloc : 1;
+ /* Mark PLT with dynamic tags. */
+ unsigned int mark_plt : 1;
+
/* X86-64 ISA level needed. */
unsigned int isa_level;
else
{
rela.r_info = htab->r_info (h->dynindx, R_X86_64_JUMP_SLOT);
- rela.r_addend = 0;
+ if (htab->params->mark_plt)
+ rela.r_addend = (resolved_plt->output_section->vma
+ + plt_offset
+ + htab->plt.plt_indirect_branch_offset);
+ else
+ rela.r_addend = 0;
plt_index = htab->next_jump_slot_index++;
}
}
}
+ asection *resolved_plt = NULL;
+
+ if (htab->params->mark_plt && htab->elf.dynamic_sections_created)
+ {
+ if (htab->plt_second != NULL)
+ resolved_plt = htab->plt_second;
+ else
+ resolved_plt = htab->elf.splt;
+
+ if (resolved_plt != NULL && resolved_plt->size == 0)
+ resolved_plt = NULL;
+ }
+
/* We now have determined the sizes of the various dynamic sections.
Allocate memory for them. */
relocs = false;
_bfd_x86_elf_write_sframe_plt (output_bfd, info, SFRAME_PLT_SEC);
}
+ if (resolved_plt != NULL
+ && (!_bfd_elf_add_dynamic_entry (info, DT_X86_64_PLT, 0)
+ || !_bfd_elf_add_dynamic_entry (info, DT_X86_64_PLTSZ, 0)
+ || !_bfd_elf_add_dynamic_entry (info, DT_X86_64_PLTENT, 0)))
+ return false;
+
return _bfd_elf_maybe_vxworks_add_dynamic_tags (output_bfd, info,
relocs);
}
if (sdyn == NULL || htab->elf.sgot == NULL)
abort ();
+ asection *resolved_plt;
+ if (htab->plt_second != NULL)
+ resolved_plt = htab->plt_second;
+ else
+ resolved_plt = htab->elf.splt;
+
sizeof_dyn = bed->s->sizeof_dyn;
dyncon = sdyn->contents;
dynconend = sdyn->contents + sdyn->size;
dyn.d_un.d_ptr = s->output_section->vma + s->output_offset
+ htab->elf.tlsdesc_got;
break;
+
+ case DT_X86_64_PLT:
+ s = resolved_plt->output_section;
+ dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
+ break;
+
+ case DT_X86_64_PLTSZ:
+ dyn.d_un.d_val = resolved_plt->size;
+ break;
+
+ case DT_X86_64_PLTENT:
+ dyn.d_un.d_ptr = htab->plt.plt_entry_size;
+ break;
}
(*bed->s->swap_dyn_out) (output_bfd, &dyn, dyncon);
bfd_vma (*get_plt_got_vma) (struct elf_x86_plt *, bfd_vma, bfd_vma,
bfd_vma);
bool (*valid_plt_reloc_p) (unsigned int);
+ unsigned int jump_slot_reloc;
dynrelbuf = NULL;
if (count == 0)
{
get_plt_got_vma = elf_x86_64_get_plt_got_vma;
valid_plt_reloc_p = elf_x86_64_valid_plt_reloc_p;
+ jump_slot_reloc = R_X86_64_JUMP_SLOT;
}
else
{
get_plt_got_vma = elf_i386_get_plt_got_vma;
valid_plt_reloc_p = elf_i386_valid_plt_reloc_p;
+ jump_slot_reloc = R_386_JUMP_SLOT;
if (got_addr)
{
/* Check .got.plt and then .got to get the _GLOBAL_OFFSET_TABLE_
len = strlen ((*p->sym_ptr_ptr)->name);
memcpy (names, (*p->sym_ptr_ptr)->name, len);
names += len;
- if (p->addend != 0)
+ /* There may be JUMP_SLOT and IRELATIVE relocations.
+ JUMP_SLOT r_addend should be ignored. */
+ if (p->addend != 0 && p->howto->type != jump_slot_reloc)
{
char buf[30], *a;
still be used with LD_AUDIT or LD_PROFILE if PLT entry is used for
canonical function address. */
htab->plt.has_plt0 = 1;
+ htab->plt.plt_indirect_branch_offset = 0;
normal_target = htab->elf.target_os == is_normal;
if (normal_target)
{
htab->lazy_plt = init_table->lazy_ibt_plt;
htab->non_lazy_plt = init_table->non_lazy_ibt_plt;
+ htab->plt.plt_indirect_branch_offset = 4;
}
else
{
/* 1 has PLT0. */
unsigned int has_plt0;
+ /* Offset of indirect branch in plt_entry. */
+ unsigned int plt_indirect_branch_offset;
+
/* Offsets into plt_entry that are to be replaced with... */
unsigned int plt_got_offset; /* ... address of this symbol in .got. */
}
}
+static const char *
+get_x86_64_dynamic_type (unsigned long type)
+{
+ switch (type)
+ {
+ case DT_X86_64_PLT:
+ return "DT_X86_64_PLT";
+ case DT_X86_64_PLTSZ:
+ return "DT_X86_64_PLTSZ";
+ case DT_X86_64_PLTENT:
+ return "DT_X86_64_PLTENT";
+ default:
+ return NULL;
+ }
+}
+
static const char *
get_dynamic_type (Filedata * filedata, unsigned long type)
{
case EM_RISCV:
result = get_riscv_dynamic_type (type);
break;
+ case EM_X86_64:
+ result = get_x86_64_dynamic_type (type);
+ break;
default:
if (filedata->file_header.e_ident[EI_OSABI] == ELFOSABI_SOLARIS)
result = get_solaris_dynamic_type (type);
#define SHN_X86_64_LCOMMON (SHN_LORESERVE + 2)
#define SHF_X86_64_LARGE 0x10000000
+
+#define DT_X86_64_PLT (DT_LOPROC + 0)
+#define DT_X86_64_PLTSZ (DT_LOPROC + 1)
+#define DT_X86_64_PLTENT (DT_LOPROC + 3)
+
#endif
source_sh ${srcdir}/emulparams/cet.sh
source_sh ${srcdir}/emulparams/x86-report-relative.sh
source_sh ${srcdir}/emulparams/x86-64-level.sh
+source_sh ${srcdir}/emulparams/x86-64-plt.sh
source_sh ${srcdir}/emulparams/static.sh
source_sh ${srcdir}/emulparams/dt-relr.sh
SCRIPT_NAME=elf
source_sh ${srcdir}/emulparams/x86-report-relative.sh
source_sh ${srcdir}/emulparams/x86-64-level.sh
source_sh ${srcdir}/emulparams/x86-64-lam.sh
+source_sh ${srcdir}/emulparams/x86-64-plt.sh
source_sh ${srcdir}/emulparams/static.sh
source_sh ${srcdir}/emulparams/dt-relr.sh
SCRIPT_NAME=elf
--- /dev/null
+PARSE_AND_LIST_OPTIONS_X86_64_PLT='
+ fprintf (file, _("\
+ -z mark-plt Mark PLT with dynamic tags\n\
+ -z nomark-plt Do not mark PLT with dynamic tags (default)\n"));
+'
+PARSE_AND_LIST_ARGS_CASE_Z_X86_64_PLT='
+ else if (strcmp (optarg, "mark-plt") == 0)
+ params.mark_plt = 1;
+ else if (strcmp (optarg, "nomark-plt") == 0)
+ params.mark_plt = 0;
+'
+
+PARSE_AND_LIST_OPTIONS="$PARSE_AND_LIST_OPTIONS $PARSE_AND_LIST_OPTIONS_X86_64_PLT"
+PARSE_AND_LIST_ARGS_CASE_Z="$PARSE_AND_LIST_ARGS_CASE_Z $PARSE_AND_LIST_ARGS_CASE_Z_X86_64_PLT"
@item max-page-size=@var{value}
Set the maximum memory page size supported to @var{value}.
+@item mark-plt
+@itemx nomark-plt
+Mark PLT entries with dynamic tags, DT_X86_64_PLT, DT_X86_64_PLTSZ and
+DT_X86_64_PLTENT. Since this option stores a non-zero value in the
+r_addend field of R_X86_64_JUMP_SLOT relocations, the resulting
+executables and shared libraries are incompatible with dynamic linkers,
+such as those in older versions of glibc without the change to ignore
+r_addend in R_X86_64_GLOB_DAT and R_X86_64_JUMP_SLOT relocations, which
+don't ignore the r_addend field of R_X86_64_JUMP_SLOT relocations.
+Supported for x86_64.
+
@item muldefs
Allow multiple definitions.
--- /dev/null
+ .text
+ .globl foo
+ .type foo, @function
+foo:
+ call bar@PLT
+ ret
+ .section .note.GNU-stack,"",@progbits
--- /dev/null
+#source: mark-plt-1.s
+#as: --x32
+#ld: -melf32_x86_64 -shared -z mark-plt
+#readelf: -drW
+
+#...
+ 0x70000000 \(DT_X86_64_PLT\) 0x1000
+ 0x70000001 \(DT_X86_64_PLTSZ\) 0x20
+ 0x70000003 \(DT_X86_64_PLTENT\) 0x10
+#...
+[0-9a-f ]+R_X86_64_JUMP_SLOT +0+ +bar \+ 1010
+#pass
--- /dev/null
+#source: mark-plt-1.s
+#as: --64
+#ld: -melf_x86_64 -shared -z mark-plt
+#readelf: -drW
+
+#...
+ 0x0000000070000000 \(DT_X86_64_PLT\) 0x1000
+ 0x0000000070000001 \(DT_X86_64_PLTSZ\) 0x20
+ 0x0000000070000003 \(DT_X86_64_PLTENT\) 0x10
+#...
+[0-9a-f ]+R_X86_64_JUMP_SLOT +0+ +bar \+ 1010
+#pass
--- /dev/null
+#source: mark-plt-1.s
+#as: --x32
+#ld: -melf32_x86_64 -shared -z mark-plt
+#objdump: -dw
+
+#...
+0+1010 <bar@plt>:
+ +1010: ff 25 9a 10 00 00 jmp \*0x109a\(%rip\) # 20b0 <bar>
+ +1016: 68 00 00 00 00 push \$0x0
+ +101b: e9 e0 ff ff ff jmp 1000 <bar@plt-0x10>
+
+Disassembly of section .text:
+
+0+1020 <foo>:
+ +1020: e8 eb ff ff ff call 1010 <bar@plt>
+#pass
--- /dev/null
+#source: mark-plt-1.s
+#as: --64
+#ld: -melf_x86_64 -shared -z mark-plt
+#objdump: -dw
+
+#...
+0+1010 <bar@plt>:
+ +1010: ff 25 32 11 00 00 jmp \*0x1132\(%rip\) # 2148 <bar>
+ +1016: 68 00 00 00 00 push \$0x0
+ +101b: e9 e0 ff ff ff jmp 1000 <bar@plt-0x10>
+
+Disassembly of section .text:
+
+0+1020 <foo>:
+ +1020: e8 eb ff ff ff call 1010 <bar@plt>
+#pass
--- /dev/null
+#source: mark-plt-1.s
+#as: --x32
+#ld: -melf32_x86_64 -shared -z mark-plt -z ibtplt
+#readelf: -drW
+
+#...
+ 0x70000000 \(DT_X86_64_PLT\) 0x1020
+ 0x70000001 \(DT_X86_64_PLTSZ\) 0x10
+ 0x70000003 \(DT_X86_64_PLTENT\) 0x10
+#...
+[0-9a-f ]+R_X86_64_JUMP_SLOT +0+ +bar \+ 1024
+#pass
--- /dev/null
+#source: mark-plt-1.s
+#as: --64
+#ld: -melf_x86_64 -shared -z mark-plt -z ibtplt
+#readelf: -drW
+
+#...
+ 0x0000000070000000 \(DT_X86_64_PLT\) 0x1020
+ 0x0000000070000001 \(DT_X86_64_PLTSZ\) 0x10
+ 0x0000000070000003 \(DT_X86_64_PLTENT\) 0x10
+#...
+[0-9a-f ]+R_X86_64_JUMP_SLOT +0+ +bar \+ 1024
+#pass
--- /dev/null
+#source: mark-plt-1.s
+#as: --x32
+#ld: -melf32_x86_64 -shared -z mark-plt -z ibtplt
+#objdump: -dw
+
+#...
+0+1020 <bar@plt>:
+ +1020: f3 0f 1e fa endbr64
+ +1024: ff 25 86 10 00 00 jmp \*0x1086\(%rip\) # 20b0 <bar>
+ +102a: 66 0f 1f 44 00 00 nopw 0x0\(%rax,%rax,1\)
+
+Disassembly of section .text:
+
+0+1030 <foo>:
+ +1030: e8 eb ff ff ff call 1020 <bar@plt>
+#pass
--- /dev/null
+#source: mark-plt-1.s
+#as: --64
+#ld: -melf_x86_64 -shared -z mark-plt -z ibtplt
+#objdump: -dw
+
+#...
+0+1020 <bar@plt>:
+ +1020: f3 0f 1e fa endbr64
+ +1024: ff 25 1e 11 00 00 jmp \*0x111e\(%rip\) # 2148 <bar>
+ +102a: 66 0f 1f 44 00 00 nopw 0x0\(%rax,%rax,1\)
+
+Disassembly of section .text:
+
+0+1030 <foo>:
+ +1030: e8 eb ff ff ff call 1020 <bar@plt>
+#pass
run_dump_test "ibt-plt-3b-x32"
run_dump_test "ibt-plt-3c-x32"
run_dump_test "ibt-plt-3d-x32"
+run_dump_test "mark-plt-1a"
+run_dump_test "mark-plt-1b"
+run_dump_test "mark-plt-1c"
+run_dump_test "mark-plt-1d"
+run_dump_test "mark-plt-1a-x32"
+run_dump_test "mark-plt-1b-x32"
+run_dump_test "mark-plt-1c-x32"
+run_dump_test "mark-plt-1d-x32"
set ASFLAGS "$saved_ASFLAGS"