Compare commits

...
Author SHA1 Message Date
Florian Weimer 59c45eeb10 ld.so: Introduce delayed relocation processing
This makes it possible to use IFUNC resolvers which depend
on relocations themselves, as long as these reloctions do
not depend on IFUNCs.

So far, delayed relocation processing is only implemented for
x86-64.
2019-04-15 17:39:45 +02:00
Florian Weimer ed8938f4f6 ld.so: Defer applying RELRO protection
This is a prerequisite of a future change which applies additional
cross-object relocations after _dl_relocate_object has been called.
2019-04-15 17:38:26 +02:00
12 changed files with 661 additions and 22 deletions
+27
View File
@@ -1,3 +1,30 @@
2018-06-06 Florian Weimer <fweimer@redhat.com>
* elf/Makefile (dl-routines): Add delayed-reloc.
* elf/rtld.c (dl_main): Call _dl_delayed_reloc_init,
_dl_delayed_reloc_apply.
* elf/dl-open.c (dl_open_worker): Likewise.
(dl_open): Call _dl_delayed_reloc_clear.
* include/link.h (struct link_map): Add l_delayed_relocations.
* sysdeps/x86_64/dl-machine.h (elf_machine_rela): Replace warning
about relocations against STT_GNU_IFUNC symbols in not-fully
relocated objects with delayed relocation recording. Delay
R_X86_64_COPY and R_X86_64_IRELATIVE relocations if necessary.
* elf/dl-delayed-reloc.c: New file.
* elf/dl-delayed-reloc.h: Likewise.
* sysdeps/generic/dl-delayed-reloc-machine.h: Likewise.
* sysdeps/x86_64/dl-delayed-reloc-machine.h: Likewise.
2018-06-05 Florian Weimer <fweimer@redhat.com>
* sysdeps/generic/ldsodefs.h (_dl_relocate_apply_relro): Declare.
* elf/rtld.c (relocate_doit): Add comment.
(dl_main): Call _dl_relocate_apply_relro.
* elf/dl-open.c (dl_open_worker): Likewise.
* elf/dl-reloc.c (_dl_relocate_object): Do not call
_dl_protect_relro.
(_dl_relocate_apply_relro): New function.
2019-04-11 Florian Weimer <fweimer@redhat.com>
* resolv/nss_dns/dns-network.c (getanswer_r): Do not replace root
+1 -1
View File
@@ -33,7 +33,7 @@ dl-routines = $(addprefix dl-,load lookup object reloc deps hwcaps \
runtime init fini debug misc \
version profile tls origin scope \
execstack open close trampoline \
exception sort-maps)
exception sort-maps delayed-reloc)
ifeq (yes,$(use-ldconfig))
dl-routines += dl-cache
endif
+247
View File
@@ -0,0 +1,247 @@
/* Delayed relocation processing.
Copyright (C) 2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#if HAVE_IFUNC
# include <assert.h>
# include <dl-delayed-reloc.h>
# include <errno.h>
# include <ldsodefs.h>
# include <sys/mman.h>
# include <unistd.h>
/* Machine-specific definitions. */
# include <dl-delayed-reloc-machine.h>
/* This struct covers a whole page containing individual struct
dl_delayed_reloc elements, which are allocated individually by
allocate_reloc below. */
struct dl_delayed_reloc_array
{
struct dl_delayed_reloc_array *next;
struct dl_delayed_reloc data[];
};
/* Pointer to global state. We use this indirection so that we do not
have to add the entire struct to the BSS segment. */
static struct dl_delayed_reloc_global *global;
/* Allocate a new struct dl_delayed_reloc_array object. Update global
*accordingly. */
static void
allocate_array (void)
{
size_t page_size = GLRO(dl_pagesize);
void *ptr = __mmap (NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ptr == MAP_FAILED)
_dl_signal_error (ENOMEM, NULL, NULL,
"cannot allocate IFUNC resolver information");
struct dl_delayed_reloc_array *new_head = ptr;
if (global->array_list_tail == NULL)
{
/* First allocation. */
global->array_list_head = new_head;
global->array_list_tail = new_head;
global->array_limit
= (page_size - offsetof (struct dl_delayed_reloc_array, data))
/ sizeof (new_head->data[0]);
}
else
{
global->array_list_tail->next = new_head;
global->array_list_tail = new_head;
global->tail_array_count = 0;
}
}
/* Allocate one struct dl_delayed_reloc element from the active
allocation array. */
static struct dl_delayed_reloc *
allocate_reloc (void)
{
assert (global != NULL);
/* Allocate a new array if none exists or the current array is
full. */
if (global->tail_array_count == global->array_limit)
allocate_array ();
assert (global->tail_array_count < global->array_limit);
return &global->array_list_tail->data[global->tail_array_count++];
}
/* Deallocate the list of array allocations starting at
array_list. */
static void
free_allocations (void)
{
size_t page_size = GLRO(dl_pagesize);
struct dl_delayed_reloc_array *p = global->array_list_head;
while (p != NULL)
{
struct dl_delayed_reloc_array *next = p->next;
__munmap (p, page_size);
p = next;
}
/* The caller needs to call _dl_delayed_reloc_init again to start
over. */
global = NULL;
}
/* Called in debugging mode to print details about a delayed
relocation. */
static void
report_delayed_relocation (struct link_map **current_map,
struct dl_delayed_reloc *dr)
{
if (dr->map != *current_map)
{
*current_map = dr->map;
/* l_name is NULL for the main executable. */
const char *map_name;
if (dr->map->l_name != NULL && *dr->map->l_name != '\0')
map_name = dr->map->l_name;
else
map_name = "<executable>";
_dl_debug_printf ("applying delayed relocations for %s\n", map_name);
}
if (dr->sym != NULL)
{
const char *strtab
= (const char *) D_PTR (dr->sym_map, l_info[DT_STRTAB]);
if (dr->sym_map->l_name != NULL)
_dl_debug_printf ("delayed relocation of symbol %s in %s\n",
strtab + dr->sym->st_name, dr->sym_map->l_name);
else
_dl_debug_printf ("delayed relocation of symbol %s\n",
strtab + dr->sym->st_name);
}
else
{
unsigned long int where = (uintptr_t) dr->reloc_addr;
_dl_debug_printf ("delayed relative relocation at 0x%lx\n", where);
}
}
/* Process all delayed IFUNC resolutions for IFUNC_MAP alone. */
static void
apply_relocations (void)
{
size_t array_limit = global->array_limit;
if (array_limit == 0)
/* No delayed relocations have been allocated, so there is nothing
to do. */
return;
/* Used for debugging output, to report switches from relocated
object to another. */
struct link_map *current_map = NULL;
unsigned long int count = 0;
for (struct dl_delayed_reloc_array *list = global->array_list_head;
list != NULL; list = list->next)
{
for (size_t index = 0; index < array_limit; ++index)
{
struct dl_delayed_reloc *dr = list->data + index;
if (dr->reloc == NULL)
/* An incompletely filled array marks the end of the
list. */
goto out;
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_BINDINGS))
report_delayed_relocation (&current_map, dr);
_dl_delayed_reloc_machine (dr);
/* Mark the object as fully relocated, for subsequent dlopen
calls. This will clear the flag even if there are still
pending relocations to process, but we keep executing the
loop, so this is not a problem. */
dr->map->l_delayed_relocations = false;
++count;
}
}
out:
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_BINDINGS))
_dl_debug_printf ("%lu delayed relocations performed\n", count);
}
void
_dl_delayed_reloc_init (struct dl_delayed_reloc_global *new_global)
{
assert (global == NULL);
global = new_global;
*global = (struct dl_delayed_reloc_global) { };
}
void
_dl_delayed_reloc_record (struct link_map *map,
const ElfW(Sym) *refsym,
const ElfW(Rela) *reloc,
ElfW(Addr) *reloc_addr,
struct link_map *sym_map,
const ElfW(Sym) *sym)
{
/* reloc == NULL is a marker to find the end of the allocations. */
assert (reloc != NULL);
/* Add the delayed relocation to the global list. */
struct dl_delayed_reloc *dr = allocate_reloc ();
*dr = (struct dl_delayed_reloc)
{
.map = map,
.refsym = refsym,
.reloc = reloc,
.reloc_addr = reloc_addr,
.sym = sym,
.sym_map = sym_map,
};
/* The map containing the relocation will now need special
processing for future copy and relative IFUNC relocations. */
map->l_delayed_relocations = true;
}
void
_dl_delayed_reloc_apply (void)
{
assert (global != NULL);
apply_relocations ();
free_allocations ();
}
void
_dl_delayed_reloc_clear (void)
{
/* This can be called from error handling, where the initialization
may not yet have happened. */
if (global == NULL)
return;
free_allocations ();
}
#endif /* HAVE_IFUNC */
+179
View File
@@ -0,0 +1,179 @@
/* Private declarations for delayed relocation processing.
Copyright (C) 2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* Delayed relocation processing attempts to address relocation
dependencies which are not reflected in DT_NEEDED dependencies
because objects are incompletely linked, or the relocation order
derived from the dependencies causes objects to refer to objects
relocated later due to symbol interposition. See bugs 20019,
21041, 23240.
Architecture-specific changes are required to implement delayed
relocation processing, typically in the implementation of
elf_machine_rel or elf_machine_rela. There are three reasons why a
relocation needs to be delayed:
* The relocation is bound with the help of an IFUNC resolver, and
the IFUNC resolver resides in an object which has not been
fully relocated yet (i.e., l_relocated is false for its link
map, or l_delayed_relocations is true). The reason for the
delay is that the IFUNC resolver may depend on the
yet-unprocessed relocations.
* The relocation is a copy relocation and the source symbol
resides in an object which has not been fully relocated yet
(l_delayed_relocations is true). The reason for the delay is
that the copy relocation could otherwise copy data which has
not been fully initialized yet because some of the delayed
relocations affect it.
* The is a relative IFUNC relocation, and the link map for the
object containing the relocation has other delayed relocations
(l_delayed_relocations is true). The reason for the delay is
that the resolver for the relocation depends on other delayed
relocations for this object which have not yet been performed.
All delayed relocations are processed in the order they were
recorded, which reflects the initial relocation processing order
between objects, and the relocation order as determined by the
static link editor within objects.
This does not process correctly all implicit dependencies between
IFUNC resolvers (e.g., two IFUNC resolvers calling each other will
still not work), but it addresses common cases of implicit
dependencies, for example as the result of symbol interposition or
due to missing DT_NEEDED entries in objects. In particular, data
relocations used by the IFUNC resolvers in glibc itself will be
resolved before these IFUNC resolvers run, so it is safe to call
glibc string functions from other (non-glibc) IFUNC resolvers, or
use pointers to glibc functions (with IFUNC resolvers) in global
data initializers (inside or outside of glibc).
Note that delayed relocation processing does not address the
phenomenon that IFUNC resolvers run before ELF constructors and C++
constructors. This means that IFUNC resolvers cannot assume that
glibc itself has been initialized, or that global data structures
in other objects have the expected values, particularly if their
initializers are not constants. */
#ifndef _DL_DELAYED_RELOC_H
#define _DL_DELAYED_RELOC_H
#include <link.h>
/* All functions declared in this file must be called while the global
rtld lock is acquired. */
/* Internal storage for delayed relocations. */
struct dl_delayed_reloc_array;
/* The full implementation is only available if IFUNCs are
supported. */
#if HAVE_IFUNC
struct dl_delayed_reloc_global
{
/* List of allocated arrays of struct dl_delayed_reloc elements. */
struct dl_delayed_reloc_array *array_list_head;
/* Last element of the list. Used for allocations. */
struct dl_delayed_reloc_array *array_list_tail;
/* Number of entries in the current allocation array. */
size_t tail_array_count;
/* Maximum number of entries in each array. */
size_t array_limit;
};
/* Prepare for relocation processing. *PGLOBAL must remain in scope
until _dl_delayed_reloc_apply or _dl_delayed_reloc_clear is
called. */
void _dl_delayed_reloc_init (struct dl_delayed_reloc_global *pglobal)
attribute_hidden;
/* Apply all pending delayed relocations. Deallocate all auxiliary
allocations. */
void _dl_delayed_reloc_apply (void) attribute_hidden;
/* Clear all allocated delayed relocations. Deallocate all auxiliary
allocations. This function is intended for clearing up after a
dlopen failure. */
void _dl_delayed_reloc_clear (void) attribute_hidden;
/* Delayed relocation. These are stored in arrays inside struct
dl_delayed_reloc_array.
In case a copy or relative IFUNC relocation is encountered while
the link map has delayed (IFUNC) relocations, these relocations are
added to the list as well because the result of these relocations
could affect their results. */
struct dl_delayed_reloc
{
/* Information about the relocation. */
const ElfW(Sym) *refsym;
struct link_map *map;
const ElfW(Rela) *reloc;
ElfW(Addr) *reloc_addr;
/* Information about the target symbol (either an IFUNC resolver, or
the source of a copy relocation). The corresponding symbol map
is implied. NULL for relative IFUNC relocations. */
const ElfW(Sym) *sym;
/* The link map corresponding to sym. */
struct link_map *sym_map;
};
/* Record a delayed relocation for SYM at *RELOC_ADDR. The relocation
is associated with MAP. Can raise an error using _dl_signal_error.
SYM can be NULL if the relocation is a relative IFUNC
relocation. */
void _dl_delayed_reloc_record (struct link_map *map,
const ElfW(Sym) *refsym,
const ElfW(Rela) *reloc,
ElfW(Addr) *reloc_addr,
struct link_map *sym_map,
const ElfW(Sym) *sym) attribute_hidden;
#else /* !HAVE_IFUNC */
/* Dummy implementations for targets without IFUNC support. */
struct dl_delayed_reloc_global
{
};
static inline void
_dl_delayed_reloc_init (struct dl_delayed_reloc_global *global)
{
}
static inline void
_dl_delayed_reloc_apply (void)
{
}
static inline void
_dl_delayed_reloc_clear (void)
{
}
#endif /* !HAVE_IFUNC */
#endif /* _DL_DELAYED_RELOC_H */
+8
View File
@@ -37,6 +37,7 @@
#include <dl-dst.h>
#include <dl-prop.h>
#include <dl-delayed-reloc.h>
/* We must be careful not to leave us in an inconsistent state. Thus we
catch any error and re-raise it after cleaning up. */
@@ -327,6 +328,9 @@ dl_open_worker (void *a)
while (l != NULL);
_dl_sort_maps (maps, nmaps, NULL, false);
struct dl_delayed_reloc_global delayed_relocations;
_dl_delayed_reloc_init (&delayed_relocations);
int relocation_in_progress = 0;
for (unsigned int i = nmaps; i-- > 0; )
@@ -494,6 +498,9 @@ TLS generation counter wrapped! Please report this."));
}
}
_dl_delayed_reloc_apply ();
_dl_relocate_apply_relro (new);
/* Notify the debugger all new objects have been relocated. */
if (relocation_in_progress)
LIBC_PROBE (reloc_complete, 3, args->nsid, r, new);
@@ -609,6 +616,7 @@ no more namespaces available for dlmopen()"));
if ((mode & __RTLD_AUDIT) == 0)
GL(dl_tls_dtv_gaps) = true;
_dl_delayed_reloc_clear ();
_dl_close_worker (args.map, true);
}
+13 -6
View File
@@ -295,14 +295,8 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
textrels = textrels->next;
}
/* In case we can protect the data now that the relocations are
done, do it. */
if (l->l_relro_size != 0)
_dl_protect_relro (l);
}
void
_dl_protect_relro (struct link_map *l)
{
@@ -322,6 +316,19 @@ cannot apply additional memory protection after relocation");
}
}
void
_dl_relocate_apply_relro (struct link_map *new)
{
struct link_map **lp = new->l_searchlist.r_list;
struct link_map **end = lp + new->l_searchlist.r_nlist;
for (; lp < end; ++lp)
{
struct link_map *l = *lp;
if (l->l_relro_size)
_dl_protect_relro (l);
}
}
void
__attribute_noinline__
_dl_reloc_bad_type (struct link_map *map, unsigned int type, int plt)
+20
View File
@@ -43,6 +43,7 @@
#include <stap-probe.h>
#include <stackinfo.h>
#include <not-cancel.h>
#include <dl-delayed-reloc.h>
#include <assert.h>
@@ -591,6 +592,7 @@ struct version_check_args
int dotrace;
};
/* Callback function used during tracing. */
static void
relocate_doit (void *a)
{
@@ -2184,6 +2186,9 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
_rtld_main_check (main_map, _dl_argv[0]);
/* Used to keep track of delayed IFUNC relocations. */
struct dl_delayed_reloc_global delayed_relocations;
if (prelinked)
{
if (main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)] != NULL)
@@ -2235,6 +2240,9 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
RTLD_TIMING_VAR (start);
rtld_timer_start (&start);
_dl_delayed_reloc_init (&delayed_relocations);
unsigned i = main_map->l_searchlist.r_nlist;
while (i-- > 0)
{
@@ -2314,6 +2322,18 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
rtld_timer_accum (&relocate_time, start);
}
/* Perform delayed IFUNC relocations and activate RELRO protection.
In the prelink case, this was already done earlier. */
if (! prelinked)
{
_dl_delayed_reloc_apply ();
/* Make sure that this covers the dynamic linker as well.
TODO: rtld_multiple_ref is always true because libc.so needs
the dynamic linker internally. */
assert (rtld_multiple_ref);
_dl_relocate_apply_relro (main_map);
}
/* Do any necessary cleanups for the startup OS interface code.
We do these now so that no calls are made after rtld re-relocation
which might be resolved to different functions than we expect.
+3
View File
@@ -202,6 +202,9 @@ struct link_map
unsigned int l_free_initfini:1; /* Nonzero if l_initfini can be
freed, ie. not allocated with
the dummy malloc in ld.so. */
/* Link maps has pending delayed (IFUNC) relocations. Only used
during relocation. */
unsigned int l_delayed_relocations:1;
#include <link_map.h>
@@ -0,0 +1,30 @@
/* Delayed relocation processing. Generic version.
Copyright (C) 2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef _DL_DELAYED_RELOC_MACHINE_H
#define _DL_DELAYED_RELOC_MACHINE_H
/* Process a delayed relocation. In the default implementation, there
are no delayed relocations, so this implementation does
nothing. */
static inline void
_dl_delayed_reloc_machine (const struct dl_delayed_reloc *reloc)
{
}
#endif /* _DL_DELAYED_RELOC_MACHINE_H */
+5
View File
@@ -928,6 +928,11 @@ extern void _dl_relocate_object (struct link_map *map,
int reloc_mode, int consider_profiling)
attribute_hidden;
/* Apply RELRO protection for all objects on the search path of NEW.
This is the final step of relocation processing for freshly loaded
objects. */
void _dl_relocate_apply_relro (struct link_map *new) attribute_hidden;
/* Protect PT_GNU_RELRO area. */
extern void _dl_protect_relro (struct link_map *map) attribute_hidden;
+97
View File
@@ -0,0 +1,97 @@
/* Delayed relocation processing. x86-64 version.
Copyright (C) 2001-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef _DL_DELAYED_RELOC_MACHINE_H
#define _DL_DELAYED_RELOC_MACHINE_H
/* This needs to be kept in sync with elf_machine_rela in
dl-machine.h. */
static inline void
_dl_delayed_reloc_machine (const struct dl_delayed_reloc *dr)
{
const ElfW(Rela) *reloc = dr->reloc;
const unsigned long int r_type = ELFW(R_TYPE) (reloc->r_info);
ElfW(Addr) *const reloc_addr = dr->reloc_addr;
const ElfW(Sym) *refsym = dr->refsym;
const ElfW(Sym) *sym = dr->sym;
ElfW(Addr) value = SYMBOL_ADDRESS (dr->sym_map, sym, true);
if (r_type == R_X86_64_IRELATIVE)
{
/* Special case: IRELATIVE relocations do not have an associated
symbol. */
value = dr->map->l_addr + reloc->r_addend;
value = ((ElfW(Addr) (*) (void)) value) ();
*reloc_addr = value;
return;
}
if (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC
&& sym->st_shndx != SHN_UNDEF)
value = ((ElfW(Addr) (*) (void)) value) ();
/* This switch statement needs to be kept in sync with the switch
statement in elf_machine_rela. */
switch (r_type)
{
case R_X86_64_GLOB_DAT:
case R_X86_64_JUMP_SLOT:
*reloc_addr = value + reloc->r_addend;
break;
case R_X86_64_64:
/* value + r_addend may be > 0xffffffff and R_X86_64_64
relocation updates the whole 64-bit entry. */
*(Elf64_Addr *) reloc_addr = (Elf64_Addr) value + reloc->r_addend;
break;
case R_X86_64_32:
value += reloc->r_addend;
*(unsigned int *) reloc_addr = value;
const char *fmt;
if (__glibc_unlikely (value > UINT_MAX))
{
const char *strtab;
fmt = "\
%s: Symbol `%s' causes overflow in R_X86_64_32 relocation\n";
print_err:
strtab = (const char *) D_PTR (dr->map, l_info[DT_STRTAB]);
_dl_error_printf (fmt, RTLD_PROGNAME, strtab + refsym->st_name);
}
break;
case R_X86_64_PC32:
value += reloc->r_addend - (ElfW(Addr)) reloc_addr;
*(unsigned int *) reloc_addr = value;
if (__glibc_unlikely (value != (int) value))
{
fmt = "\
%s: Symbol `%s' causes overflow in R_X86_64_PC32 relocation\n";
goto print_err;
}
case R_X86_64_COPY:
memcpy (reloc_addr, (void *) value,
MIN (sym->st_size, refsym->st_size));
break;
default:
_dl_reloc_bad_type (dr->map, r_type, 0);
}
}
#endif /* _DL_DELAYED_RELOC_MACHINE_H */
+31 -15
View File
@@ -27,6 +27,7 @@
#include <tls.h>
#include <dl-tlsdesc.h>
#include <cpu-features.c>
#include <dl-delayed-reloc.h>
/* Return nonzero iff ELF header is compatible with the running host. */
static inline int __attribute__ ((unused))
@@ -314,22 +315,21 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
&& __glibc_likely (!skip_ifunc))
{
# ifndef RTLD_BOOTSTRAP
if (sym_map != map
&& sym_map->l_type != lt_executable
&& !sym_map->l_relocated)
if (!sym_map->l_relocated || sym_map->l_delayed_relocations)
{
const char *strtab
= (const char *) D_PTR (map, l_info[DT_STRTAB]);
_dl_error_printf ("\
%s: Relink `%s' with `%s' for IFUNC symbol `%s'\n",
RTLD_PROGNAME, map->l_name,
sym_map->l_name,
strtab + refsym->st_name);
/* If the target map has not yet been fully relocated,
delay the processing of the relocation until
later. */
_dl_delayed_reloc_record
(map, refsym, reloc, reloc_addr, sym_map, sym);
return;
}
# endif
value = ((ElfW(Addr) (*) (void)) value) ();
}
/* This switch statement needs to be kept in sync with the
switch statement in _dl_delayed_reloc_machine. */
switch (r_type)
{
# ifndef RTLD_BOOTSTRAP
@@ -498,8 +498,14 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
/* This can happen in trace mode if an object could not be
found. */
break;
memcpy (reloc_addr_arg, (void *) value,
MIN (sym->st_size, refsym->st_size));
if (sym_map->l_delayed_relocations)
/* The relocation result could depend on earlier delayed
relocations. */
_dl_delayed_reloc_record
(map, refsym, reloc, reloc_addr, sym_map, sym);
else
memcpy (reloc_addr_arg, (void *) value,
MIN (sym->st_size, refsym->st_size));
if (__glibc_unlikely (sym->st_size > refsym->st_size)
|| (__glibc_unlikely (sym->st_size < refsym->st_size)
&& GLRO(dl_verbose)))
@@ -511,9 +517,19 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
break;
# endif
case R_X86_64_IRELATIVE:
value = map->l_addr + reloc->r_addend;
value = ((ElfW(Addr) (*) (void)) value) ();
*reloc_addr = value;
if (map->l_delayed_relocations)
/* We need to delay these IFUNC relocation because the
IFUNC resolver may pick up the address of a symbol
which in turn is determined by a (delayed) IFUNC
relocation. */
_dl_delayed_reloc_record
(map, refsym, reloc, reloc_addr, map, NULL);
else
{
value = map->l_addr + reloc->r_addend;
value = ((ElfW(Addr) (*) (void)) value) ();
*reloc_addr = value;
}
break;
default:
_dl_reloc_bad_type (map, r_type, 0);