Fix handling of DT_TEXTREL for an object with more than one read-only
segment. According to gABI spec, presence of the tag indicates that dynamic linker must be prepared to handle relocations against any read-only segment, not only the segment which we, somewhat arbitrary, declared the text. For each read-only segment, add write permission before relocs are processed, and return to the mapping mode requested by the phdr, after relocs are done. Reported, tested, and reviewed by: emaste PR: 207631 Sponsored by: The FreeBSD Foundation MFC after: 2 weeks
This commit is contained in:
parent
788c03c3d8
commit
ca8c8dc3eb
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=296319
@ -39,7 +39,6 @@
|
|||||||
#include "rtld.h"
|
#include "rtld.h"
|
||||||
|
|
||||||
static Elf_Ehdr *get_elf_header(int, const char *, const struct stat *);
|
static Elf_Ehdr *get_elf_header(int, const char *, const struct stat *);
|
||||||
static int convert_prot(int); /* Elf flags -> mmap protection */
|
|
||||||
static int convert_flags(int); /* Elf flags -> mmap flags */
|
static int convert_flags(int); /* Elf flags -> mmap flags */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -445,7 +444,7 @@ obj_new(void)
|
|||||||
* Given a set of ELF protection flags, return the corresponding protection
|
* Given a set of ELF protection flags, return the corresponding protection
|
||||||
* flags for MMAP.
|
* flags for MMAP.
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
convert_prot(int elfflags)
|
convert_prot(int elfflags)
|
||||||
{
|
{
|
||||||
int prot = 0;
|
int prot = 0;
|
||||||
|
@ -2626,6 +2626,40 @@ relocate_object_dag(Obj_Entry *root, bool bind_now, Obj_Entry *rtldobj,
|
|||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare for, or clean after, relocating an object marked with
|
||||||
|
* DT_TEXTREL or DF_TEXTREL. Before relocating, all read-only
|
||||||
|
* segments are remapped read-write. After relocations are done, the
|
||||||
|
* segment's permissions are returned back to the modes specified in
|
||||||
|
* the phdrs. If any relocation happened, or always for wired
|
||||||
|
* program, COW is triggered.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
reloc_textrel_prot(Obj_Entry *obj, bool before)
|
||||||
|
{
|
||||||
|
const Elf_Phdr *ph;
|
||||||
|
void *base;
|
||||||
|
size_t l, sz;
|
||||||
|
int prot;
|
||||||
|
|
||||||
|
for (l = obj->phsize / sizeof(*ph), ph = obj->phdr; l > 0;
|
||||||
|
l--, ph++) {
|
||||||
|
if (ph->p_type != PT_LOAD || (ph->p_flags & PF_W) != 0)
|
||||||
|
continue;
|
||||||
|
base = obj->relocbase + trunc_page(ph->p_vaddr);
|
||||||
|
sz = round_page(ph->p_vaddr + ph->p_filesz) -
|
||||||
|
trunc_page(ph->p_vaddr);
|
||||||
|
prot = convert_prot(ph->p_flags) | (before ? PROT_WRITE : 0);
|
||||||
|
if (mprotect(base, sz, prot) == -1) {
|
||||||
|
_rtld_error("%s: Cannot write-%sable text segment: %s",
|
||||||
|
obj->path, before ? "en" : "dis",
|
||||||
|
rtld_strerror(errno));
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Relocate single object.
|
* Relocate single object.
|
||||||
* Returns 0 on success, or -1 on failure.
|
* Returns 0 on success, or -1 on failure.
|
||||||
@ -2648,28 +2682,17 @@ relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj,
|
|||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj->textrel) {
|
|
||||||
/* There are relocations to the write-protected text segment. */
|
/* There are relocations to the write-protected text segment. */
|
||||||
if (mprotect(obj->mapbase, obj->textsize,
|
if (obj->textrel && reloc_textrel_prot(obj, true) != 0)
|
||||||
PROT_READ|PROT_WRITE|PROT_EXEC) == -1) {
|
|
||||||
_rtld_error("%s: Cannot write-enable text segment: %s",
|
|
||||||
obj->path, rtld_strerror(errno));
|
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Process the non-PLT non-IFUNC relocations. */
|
/* Process the non-PLT non-IFUNC relocations. */
|
||||||
if (reloc_non_plt(obj, rtldobj, flags, lockstate))
|
if (reloc_non_plt(obj, rtldobj, flags, lockstate))
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
if (obj->textrel) { /* Re-protected the text segment. */
|
/* Re-protected the text segment. */
|
||||||
if (mprotect(obj->mapbase, obj->textsize,
|
if (obj->textrel && reloc_textrel_prot(obj, false) != 0)
|
||||||
PROT_READ|PROT_EXEC) == -1) {
|
|
||||||
_rtld_error("%s: Cannot write-protect text segment: %s",
|
|
||||||
obj->path, rtld_strerror(errno));
|
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the special PLT or GOT entries. */
|
/* Set the special PLT or GOT entries. */
|
||||||
init_pltgot(obj);
|
init_pltgot(obj);
|
||||||
|
@ -385,6 +385,7 @@ void *allocate_module_tls(int index);
|
|||||||
bool allocate_tls_offset(Obj_Entry *obj);
|
bool allocate_tls_offset(Obj_Entry *obj);
|
||||||
void free_tls_offset(Obj_Entry *obj);
|
void free_tls_offset(Obj_Entry *obj);
|
||||||
const Ver_Entry *fetch_ventry(const Obj_Entry *obj, unsigned long);
|
const Ver_Entry *fetch_ventry(const Obj_Entry *obj, unsigned long);
|
||||||
|
int convert_prot(int elfflags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MD function declarations.
|
* MD function declarations.
|
||||||
|
Loading…
Reference in New Issue
Block a user