aboutsummaryrefslogtreecommitdiff
path: root/libexec
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2016-03-02 16:36:24 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2016-03-02 16:36:24 +0000
commitca8c8dc3ebe7a92dd6d7951231769154a0454b52 (patch)
tree4c7171dd11c887faf6dbb1a4729031038557b9d2 /libexec
parent788c03c3d8ac06d5ab9e7e6801a9b19337f7a763 (diff)
downloadsrc-ca8c8dc3ebe7a92dd6d7951231769154a0454b52.tar.gz
src-ca8c8dc3ebe7a92dd6d7951231769154a0454b52.zip
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
Notes
Notes: svn path=/head/; revision=296319
Diffstat (limited to 'libexec')
-rw-r--r--libexec/rtld-elf/map_object.c3
-rw-r--r--libexec/rtld-elf/rtld.c57
-rw-r--r--libexec/rtld-elf/rtld.h1
3 files changed, 42 insertions, 19 deletions
diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c
index f4f6f4221816..30af4cefddbe 100644
--- a/libexec/rtld-elf/map_object.c
+++ b/libexec/rtld-elf/map_object.c
@@ -39,7 +39,6 @@
#include "rtld.h"
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 */
/*
@@ -445,7 +444,7 @@ obj_new(void)
* Given a set of ELF protection flags, return the corresponding protection
* flags for MMAP.
*/
-static int
+int
convert_prot(int elfflags)
{
int prot = 0;
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 912358c0c1d1..6a405c30f508 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -2627,6 +2627,40 @@ relocate_object_dag(Obj_Entry *root, bool bind_now, Obj_Entry *rtldobj,
}
/*
+ * 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.
* 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);
}
- if (obj->textrel) {
- /* There are relocations to the write-protected text segment. */
- if (mprotect(obj->mapbase, obj->textsize,
- PROT_READ|PROT_WRITE|PROT_EXEC) == -1) {
- _rtld_error("%s: Cannot write-enable text segment: %s",
- obj->path, rtld_strerror(errno));
- return (-1);
- }
- }
+ /* There are relocations to the write-protected text segment. */
+ if (obj->textrel && reloc_textrel_prot(obj, true) != 0)
+ return (-1);
/* Process the non-PLT non-IFUNC relocations. */
if (reloc_non_plt(obj, rtldobj, flags, lockstate))
return (-1);
- if (obj->textrel) { /* Re-protected the text segment. */
- if (mprotect(obj->mapbase, obj->textsize,
- PROT_READ|PROT_EXEC) == -1) {
- _rtld_error("%s: Cannot write-protect text segment: %s",
- obj->path, rtld_strerror(errno));
- return (-1);
- }
- }
+ /* Re-protected the text segment. */
+ if (obj->textrel && reloc_textrel_prot(obj, false) != 0)
+ return (-1);
/* Set the special PLT or GOT entries. */
init_pltgot(obj);
diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h
index 5fbfb271a3bf..8b7024b841ad 100644
--- a/libexec/rtld-elf/rtld.h
+++ b/libexec/rtld-elf/rtld.h
@@ -385,6 +385,7 @@ void *allocate_module_tls(int index);
bool allocate_tls_offset(Obj_Entry *obj);
void free_tls_offset(Obj_Entry *obj);
const Ver_Entry *fetch_ventry(const Obj_Entry *obj, unsigned long);
+int convert_prot(int elfflags);
/*
* MD function declarations.