mirror of
https://github.com/clearlinux/graphene.git
synced 2026-08-26 18:17:23 +00:00
[LibOS] Remove dynamic linking
After this change, LibOS will no longer perform dynamic linking. The ELF loading code executes load commands and passes control to interpreter (ld.so), which handles necessary relocations and loading additional libraries. In this way, the code resembles what Linux kernel does when executing a new program. Before, dynamic linking was necessary for making LibOS entry point (syscalldb function) available for applications. However, that meant duplicating the work already done by ld.so, and introduced a lot of unnecessary complexity. After changing LibOS entry API to use the GS register, it's possible to omit dynamic linking entirely. The main function (__load_elf_object()) still needs cleanup and possibly rewriting from scratch. However, this change prepares ground for that rewrite. Summary of changes: - Remove dynamic relocation step (DO_DYNAMIC_RELOCATE()). - Don't call __load_elf_object() again for ELFs reported via register_library(). We only need to notify GDB about these. - Remove fields related to dynamic linking from link_map (dynamic section address, hashes, etc.), and setup for these fields. - need_interp(): To check if we need an interpreter, check only if the binary requests one (PT_INTERP), instead of traversing the dynamic section (before, we ignored dynamic dependencies on LibOS itself, but now there shouldn't be any). - RELOCATE(): always adjust addresses, instead of checking if they're already inside the mapped range. I think the previous behaviour was a workaround to make repeated relocations work. - Get rid of load modes that are no longer used (OBJECT_REMAP, OBJECT_USER). - Remove the workaround for repeated relocation in glibc patches (R_X86_64_NONE). Signed-off-by: Paweł Marczewski <pawel@invisiblethingslab.com>
This commit is contained in:
@@ -325,20 +325,6 @@ index e4c8269e3ddafed75be2c6259cc695fa9487a109..c23cda4b3b8e75e1d199df181bee8f88
|
||||
|
||||
/* Push back the return PC. */
|
||||
pushq %rdi
|
||||
diff --git a/sysdeps/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h
|
||||
index f525600071f0693fc11ee0c7891a5302c8dc3a35..491c5c918d536ddd1a955b33b0a4ed92675caa77 100644
|
||||
--- a/sysdeps/x86_64/dl-machine.h
|
||||
+++ b/sysdeps/x86_64/dl-machine.h
|
||||
@@ -577,7 +577,8 @@ elf_machine_lazy_rel (struct link_map *map,
|
||||
value = ((ElfW(Addr) (*) (void)) value) ();
|
||||
*reloc_addr = value;
|
||||
}
|
||||
- else
|
||||
+ /* for graphene, get around R_X86_64_NONE */
|
||||
+ else if (__builtin_expect (r_type != R_X86_64_NONE, 1))
|
||||
_dl_reloc_bad_type (map, r_type, 1);
|
||||
}
|
||||
|
||||
diff --git a/sysdeps/x86_64/nptl/tls.h b/sysdeps/x86_64/nptl/tls.h
|
||||
index bdd02376f985b102ea76b54fb5b4f0f4fcbc19bb..68b45092ca67bdf6ff24f5375951cdc1e33284b7 100644
|
||||
--- a/sysdeps/x86_64/nptl/tls.h
|
||||
|
||||
@@ -316,20 +316,6 @@ index c549dce4dbc5937d819b2db35cb69672eb9c4c8b..eb12d98b1874db11eea8a2ff1b7d463e
|
||||
|
||||
#define r0 %rax /* Normal return-value register. */
|
||||
#define r1 %rbx /* Secondary return-value register. */
|
||||
diff --git a/sysdeps/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h
|
||||
index 8e9baffeb4b2d5101cd1effa89bdd14a48b42aac..60ae1d1fd6275eda2e6c5e00a384264be3b64760 100644
|
||||
--- a/sysdeps/x86_64/dl-machine.h
|
||||
+++ b/sysdeps/x86_64/dl-machine.h
|
||||
@@ -579,7 +579,8 @@ elf_machine_lazy_rel (struct link_map *map,
|
||||
value = ((ElfW(Addr) (*) (void)) value) ();
|
||||
*reloc_addr = value;
|
||||
}
|
||||
- else
|
||||
+ /* for Graphene, get around R_X86_64_NONE */
|
||||
+ else if (__builtin_expect (r_type != R_X86_64_NONE, 1))
|
||||
_dl_reloc_bad_type (map, r_type, 1);
|
||||
}
|
||||
|
||||
diff --git a/sysdeps/x86_64/nptl/tls.h b/sysdeps/x86_64/nptl/tls.h
|
||||
index e7c1416eec4a490312ed56cc51a03a33eaa8e222..1cd560a95df7fca6762cd90f50f2ef7d8bbb493c 100644
|
||||
--- a/sysdeps/x86_64/nptl/tls.h
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-3.0-or-later */
|
||||
/* Copyright (C) 2014 Stony Brook University */
|
||||
|
||||
/*
|
||||
* This file contains x64-specific code for relocating ELF binaries. Most of the source code was
|
||||
* imported from GNU C library.
|
||||
*/
|
||||
|
||||
#ifndef __DL_MACHINE_H__
|
||||
#define __DL_MACHINE_H__
|
||||
|
||||
#define ELF_MACHINE_NAME "x86_64"
|
||||
|
||||
#include "elf/ldsodefs.h"
|
||||
|
||||
/* Return nonzero iff ELF header is compatible with the running host. */
|
||||
static inline int __attribute__((unused)) elf_machine_matches_host(const Elf64_Ehdr* ehdr) {
|
||||
return ehdr->e_machine == EM_X86_64;
|
||||
}
|
||||
|
||||
/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or
|
||||
TLS variable, so undefined references should not be allowed to
|
||||
define the value.
|
||||
ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
|
||||
of the main executable's symbols, as for a COPY reloc. */
|
||||
#define elf_machine_type_class(type) \
|
||||
((((type) == R_X86_64_JUMP_SLOT \
|
||||
|| (type) == R_X86_64_DTPMOD64 \
|
||||
|| (type) == R_X86_64_DTPOFF64 \
|
||||
|| (type) == R_X86_64_TPOFF64 \
|
||||
|| (type) == R_X86_64_TLSDESC) \
|
||||
* ELF_RTYPE_CLASS_PLT) \
|
||||
| (((type) == R_X86_64_COPY) * ELF_RTYPE_CLASS_COPY))
|
||||
|
||||
/* The x86-64 never uses Elf64_Rel relocations. */
|
||||
#define ELF_MACHINE_NO_REL 1
|
||||
|
||||
/* Perform the relocation specified by RELOC and SYM (which is fully resolved).
|
||||
MAP is the object containing the reloc. */
|
||||
|
||||
//#define DEBUG_RELOC
|
||||
|
||||
static bool elf_machine_rela(struct link_map* l, ElfW(Rela) * reloc, Elf64_Sym* sym,
|
||||
void* const reloc_addr_arg) {
|
||||
Elf64_Addr* const reloc_addr = reloc_addr_arg;
|
||||
const unsigned long int r_type = ELF64_R_TYPE(reloc->r_info);
|
||||
|
||||
const char* __attribute__((unused)) strtab = (const void*)D_PTR(l->l_info[DT_STRTAB]);
|
||||
|
||||
#ifdef DEBUG_RELOC
|
||||
#define debug_reloc(r_type, sym, value) \
|
||||
({ \
|
||||
if (strtab && (sym) && (sym)->st_name) \
|
||||
debug(#r_type ": %s\n", strtab + (sym)->st_name); \
|
||||
else if (value) \
|
||||
debug(#r_type ": %p\n", (value)); \
|
||||
else \
|
||||
debug(#r_type "\n", (value)); \
|
||||
})
|
||||
#else
|
||||
#define debug_reloc(...) ({})
|
||||
#endif
|
||||
|
||||
if (r_type == R_X86_64_RELATIVE || r_type == R_X86_64_NONE)
|
||||
return false;
|
||||
|
||||
Elf64_Sym* refsym = sym;
|
||||
Elf64_Addr value;
|
||||
Elf64_Addr sym_map = RESOLVE_MAP(&strtab, &sym);
|
||||
|
||||
if (!sym_map || !sym || refsym == sym)
|
||||
return false;
|
||||
|
||||
value = sym_map + sym->st_value;
|
||||
|
||||
/* We do a very special relocation for loaded libraries */
|
||||
PROTECT_PAGE(l, refsym, sizeof(*refsym));
|
||||
PROTECT_PAGE(l, reloc_addr, sizeof(*reloc_addr));
|
||||
|
||||
refsym->st_info = sym->st_info;
|
||||
refsym->st_size = sym->st_size;
|
||||
|
||||
if (ELFW(ST_TYPE)(sym->st_info) == STT_GNU_IFUNC && sym->st_shndx != SHN_UNDEF) {
|
||||
value = ((Elf64_Addr(*)(void))value)();
|
||||
|
||||
refsym->st_info ^= ELFW(ST_TYPE)(sym->st_info);
|
||||
refsym->st_info |= STT_FUNC;
|
||||
}
|
||||
|
||||
debug_reloc("shim symbol", sym, value);
|
||||
|
||||
refsym->st_value = value - l->l_addr;
|
||||
*reloc_addr = value + ((r_type == R_X86_64_GLOB_DAT || r_type == R_X86_64_JUMP_SLOT ||
|
||||
r_type == R_X86_64_64)
|
||||
? reloc->r_addend
|
||||
: 0);
|
||||
|
||||
/* We have relocated the symbol, we don't want the
|
||||
interpreter to relocate it again. */
|
||||
if (r_type != R_X86_64_NONE) {
|
||||
PROTECT_PAGE(l, reloc, sizeof(*reloc));
|
||||
reloc->r_info = (reloc->r_info ^ ELF64_R_TYPE(reloc->r_info)) | R_X86_64_NONE;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* !DL_MACHINE_H */
|
||||
@@ -29,4 +29,6 @@
|
||||
: "a"(ENTRY), "b"(ARGP), "d"(0) \
|
||||
: "memory", "cc")
|
||||
|
||||
#define SHIM_ELF_HOST_MACHINE EM_X86_64
|
||||
|
||||
#endif /* _SHIM_INTERNAL_ARCH_H_ */
|
||||
|
||||
@@ -150,7 +150,7 @@ int remove_loaded_libraries(void);
|
||||
|
||||
/* gdb debugging support */
|
||||
void remove_r_debug(void* addr);
|
||||
void append_r_debug(const char* uri, void* addr, void* dyn_addr);
|
||||
void append_r_debug(const char* uri, void* addr);
|
||||
void clean_link_map_list(void);
|
||||
|
||||
/* create unique files/pipes */
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-3.0-or-later */
|
||||
/* Copyright (C) 2014 Stony Brook University */
|
||||
|
||||
/*
|
||||
* This file contains architecture-independent code for relocating ELF binaries. Most of the source
|
||||
* code was imported from GNU C library.
|
||||
*/
|
||||
|
||||
#include "shim_dl-machine.h"
|
||||
|
||||
#define elf_dynamic_do_rel elf_dynamic_do_rela
|
||||
#define RELCOUNT_IDX VERSYMIDX(DT_RELACOUNT)
|
||||
#define Rel Rela
|
||||
#define elf_machine_rel elf_machine_rela
|
||||
#define elf_machine_rel_relative elf_machine_rela_relative
|
||||
#define elf_dynamic_redo_rel elf_dynamic_redo_rela
|
||||
|
||||
#ifndef DO_ELF_MACHINE_REL_RELATIVE
|
||||
#define DO_ELF_MACHINE_REL_RELATIVE(l, relative) \
|
||||
elf_machine_rel_relative(l, relative, (void*)((l)->l_addr + relative->r_offset))
|
||||
#endif
|
||||
|
||||
#ifndef VERSYMIDX
|
||||
#define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX(sym))
|
||||
#endif
|
||||
|
||||
#ifndef VALIDX
|
||||
#define VALIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALTAGIDX(tag))
|
||||
#endif
|
||||
|
||||
#define elf_dynamic_copy_rel elf_dynamic_copy_rela
|
||||
#define dt_reloc DT_RELA
|
||||
#define dt_reloc_sz DT_RELASZ
|
||||
|
||||
/* Perform the relocations in MAP on the running program image as specified
|
||||
by RELTAG, SZTAG. If LAZY is nonzero, this is the first pass on PLT
|
||||
relocations; they should be set up to call _dl_runtime_resolve, rather
|
||||
than fully resolved now. */
|
||||
static void __attribute__((unused))
|
||||
elf_dynamic_do_rel(struct link_map* l, ElfW(Addr) reladdr, size_t relsize) {
|
||||
if (!l->l_info[DT_SYMTAB])
|
||||
return;
|
||||
|
||||
ElfW(Sym)* symtab = (void*)D_PTR(l->l_info[DT_SYMTAB]);
|
||||
ElfW(Rel)* r = (void*)reladdr;
|
||||
ElfW(Rel)* end = (void*)(reladdr + relsize);
|
||||
ElfW(Word) nrelative =
|
||||
l->l_info[RELCOUNT_IDX] == NULL ? 0 : l->l_info[RELCOUNT_IDX]->d_un.d_val;
|
||||
size_t nrelsize = relsize / sizeof(ElfW(Rel));
|
||||
|
||||
r = r + (nrelative < nrelsize ? nrelative : nrelsize);
|
||||
for (; r < end; ++r) {
|
||||
ElfW(Sym)* sym = &symtab[ELFW(R_SYM)(r->r_info)];
|
||||
void* reloc = (void*)l->l_addr + r->r_offset;
|
||||
if (elf_machine_rel(l, r, sym, reloc)) {
|
||||
assert(l->nlinksyms < MAX_LINKSYMS);
|
||||
l->linksyms[l->nlinksyms].rel = r;
|
||||
l->linksyms[l->nlinksyms].sym = sym;
|
||||
l->linksyms[l->nlinksyms].reloc = reloc;
|
||||
l->nlinksyms++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) elf_dynamic_redo_rel(struct link_map* l) {
|
||||
for (int i = 0; i < l->nlinksyms; i++)
|
||||
elf_machine_rel(l, l->linksyms[i].rel, l->linksyms[i].sym, l->linksyms[i].reloc);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void inline elf_copy_rel (struct link_map * l1, struct link_map * l2,
|
||||
int reloc, int reloc_sz)
|
||||
{
|
||||
if (!l1->l_info[reloc] || !l2->l_info[reloc])
|
||||
return;
|
||||
|
||||
ElfW(Sym) * symtab1 = (void *) D_PTR (l1->l_info[DT_SYMTAB]);
|
||||
const char * strtab1 = (void *) D_PTR (l1->l_info[DT_STRTAB]);
|
||||
ElfW(Sym) * symtab2 = (void *) D_PTR (l2->l_info[DT_SYMTAB]);
|
||||
const char * strtab2 = (void *) D_PTR (l2->l_info[DT_STRTAB]);
|
||||
|
||||
ElfW(Rel) * r1, * r2, * end1, * end2;
|
||||
|
||||
r1 = (ElfW(Rel) *) D_PTR (l1->l_info[reloc]);
|
||||
end1 = ((void *) r1 + l1->l_info[reloc_sz]->d_un.d_val);
|
||||
r1 += l1->l_info[RELCOUNT_IDX] ? l1->l_info[RELCOUNT_IDX]->d_un.d_val : 0;
|
||||
|
||||
r2 = (ElfW(Rel) *) D_PTR (l2->l_info[reloc]);
|
||||
end2 = ((void *) r2 + l2->l_info[reloc_sz]->d_un.d_val);
|
||||
r2 += l2->l_info[RELCOUNT_IDX] ? l2->l_info[RELCOUNT_IDX]->d_un.d_val : 0;
|
||||
|
||||
for (; r1 < end1 && r2 < end2; ++r1, ++r2) {
|
||||
debug("copy %s from %s\n",
|
||||
strtab1 + symtab1[ELFW(R_SYM) (r1->r_info)].st_name,
|
||||
strtab2 + symtab2[ELFW(R_SYM) (r2->r_info)].st_name);
|
||||
|
||||
r1->r_info = r2->r_info;
|
||||
|
||||
ElfW(Addr) * reladdr1 = (void *) l1->l_addr + r1->r_offset;
|
||||
ElfW(Addr) * reladdr2 = (void *) l2->l_addr + r2->r_offset;
|
||||
|
||||
if (*reladdr1 != *reladdr2)
|
||||
*reladdr1 = *reladdr2;
|
||||
}
|
||||
}
|
||||
|
||||
/* copy the relocation done by PAL */
|
||||
static void __attribute__((unused))
|
||||
elf_dynamic_copy_rel (struct link_map * l1, struct link_map * l2)
|
||||
{
|
||||
elf_copy_rel(l1, l2, dt_reloc, dt_reloc_sz);
|
||||
elf_copy_rel(l1, l2, DT_JMPREL, DT_PLTRELSZ);
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef elf_dynamic_do_rel
|
||||
#undef Rel
|
||||
#undef elf_machine_rel
|
||||
#undef elf_machine_rel_relative
|
||||
#undef DO_ELF_MACHINE_REL_RELATIVE
|
||||
#undef RELCOUNT_IDX
|
||||
//#undef elf_dynamic_copy_rel
|
||||
#undef dt_reloc
|
||||
#undef dt_reloc_sz
|
||||
#undef elf_dynamic_redo_rel
|
||||
@@ -1,240 +0,0 @@
|
||||
#include "elf.h"
|
||||
|
||||
#ifndef VERSYMIDX
|
||||
#define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX(sym))
|
||||
#endif
|
||||
|
||||
#ifndef DT_THISPROCNUM
|
||||
#define DT_THISPROCNUM 0
|
||||
#endif
|
||||
|
||||
#if __ELF_NATIVE_CLASS == 32
|
||||
typedef Elf32_Word d_tag_utype, d_val_utype;
|
||||
#elif __ELF_NATIVE_CLASS == 64
|
||||
typedef Elf64_Xword d_tag_utype, d_val_utype;
|
||||
#endif
|
||||
|
||||
#define IN_RANGE(l, addr) \
|
||||
((ElfW(Addr))(addr) >= (l)->l_map_start && (ElfW(Addr))(addr) < (l)->l_map_end)
|
||||
|
||||
#define RELOCATE(l, addr) \
|
||||
((__typeof__(addr))(IN_RANGE((l), (addr)) ? (ElfW(Addr))(addr) \
|
||||
: (ElfW(Addr))(addr) + (ElfW(Addr))((l)->l_addr)))
|
||||
|
||||
#include "shim_dl-machine.h"
|
||||
|
||||
/* Read the dynamic section at DYN and fill in INFO with indices DT_*. */
|
||||
static inline void __attribute__((unused, always_inline)) elf_get_dynamic_info(struct link_map* l) {
|
||||
ElfW(Dyn)* dyn = l->l_ld;
|
||||
|
||||
if (dyn == NULL)
|
||||
return;
|
||||
|
||||
while (dyn->d_tag != DT_NULL) {
|
||||
int tag = 0;
|
||||
|
||||
if ((d_tag_utype)dyn->d_tag < DT_NUM)
|
||||
tag = dyn->d_tag;
|
||||
|
||||
else if (dyn->d_tag >= DT_LOPROC && dyn->d_tag < DT_LOPROC + DT_THISPROCNUM)
|
||||
tag = dyn->d_tag - DT_LOPROC + DT_NUM;
|
||||
|
||||
else if ((d_tag_utype)DT_VERSIONTAGIDX(dyn->d_tag) < DT_VERSIONTAGNUM)
|
||||
tag = VERSYMIDX(dyn->d_tag);
|
||||
|
||||
else if ((d_tag_utype)DT_EXTRATAGIDX(dyn->d_tag) < DT_EXTRANUM)
|
||||
tag = DT_EXTRATAGIDX(dyn->d_tag) + DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM;
|
||||
|
||||
else if ((d_tag_utype)DT_VALTAGIDX(dyn->d_tag) < DT_VALNUM)
|
||||
tag =
|
||||
DT_VALTAGIDX(dyn->d_tag) + DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM;
|
||||
|
||||
else if ((d_tag_utype)DT_ADDRTAGIDX(dyn->d_tag) < DT_ADDRNUM)
|
||||
tag = DT_ADDRTAGIDX(dyn->d_tag) + DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM +
|
||||
DT_EXTRANUM + DT_VALNUM;
|
||||
|
||||
if (tag)
|
||||
l->l_info[tag] = dyn;
|
||||
|
||||
++dyn;
|
||||
}
|
||||
|
||||
if (l->l_addr) {
|
||||
#define ADJUST_DYN_INFO(tag) \
|
||||
do { \
|
||||
if (l->l_info[tag] != NULL) { \
|
||||
l->l_info[tag]->d_un.d_ptr = RELOCATE(l, l->l_info[tag]->d_un.d_ptr); \
|
||||
/* debug("relocate info[%d] = %p\n", \
|
||||
tag, l->l_info[tag]->d_un.d_ptr); */ \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
ADJUST_DYN_INFO(DT_HASH);
|
||||
ADJUST_DYN_INFO(DT_PLTGOT);
|
||||
ADJUST_DYN_INFO(DT_STRTAB);
|
||||
ADJUST_DYN_INFO(DT_SYMTAB);
|
||||
|
||||
#if !ELF_MACHINE_NO_RELA
|
||||
ADJUST_DYN_INFO(DT_RELA);
|
||||
#endif
|
||||
|
||||
#if !ELF_MACHINE_NO_REL
|
||||
ADJUST_DYN_INFO(DT_REL);
|
||||
#endif
|
||||
|
||||
ADJUST_DYN_INFO(DT_JMPREL);
|
||||
ADJUST_DYN_INFO(VERSYMIDX(DT_VERSYM));
|
||||
ADJUST_DYN_INFO(DT_ADDRTAGIDX(DT_GNU_HASH) + DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM +
|
||||
DT_EXTRANUM + DT_VALNUM);
|
||||
#undef ADJUST_DYN_INFO
|
||||
}
|
||||
|
||||
/* Then a bunch of assertion, we could kind of ignore them */
|
||||
if (l->l_info[DT_PLTREL] != NULL) {
|
||||
#if ELF_MACHINE_NO_RELA
|
||||
assert(l->l_info[DT_PLTREL]->d_un.d_val == DT_REL);
|
||||
|
||||
#elif ELF_MACHINE_NO_REL
|
||||
assert(l->l_info[DT_PLTREL]->d_un.d_val == DT_RELA);
|
||||
|
||||
#else
|
||||
assert(l->l_info[DT_PLTREL]->d_un.d_val == DT_REL ||
|
||||
l->l_info[DT_PLTREL]->d_un.d_val == DT_RELA);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !ELF_MACHINE_NO_RELA
|
||||
if (l->l_info[DT_RELA] != NULL)
|
||||
assert(l->l_info[DT_RELAENT]->d_un.d_val == sizeof(ElfW(Rela)));
|
||||
#endif
|
||||
|
||||
#if !ELF_MACHINE_NO_REL
|
||||
if (l->l_info[DT_REL] != NULL)
|
||||
assert(l->l_info[DT_RELENT]->d_un.d_val == sizeof(ElfW(Rel)));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Get the definitions of `elf_dynamic_do_rel' and `elf_dynamic_do_rela'.
|
||||
These functions are almost identical, so we use cpp magic to avoid
|
||||
duplicating their code. It cannot be done in a more general function
|
||||
because we must be able to completely inline. */
|
||||
|
||||
/* On some machines, notably SPARC, DT_REL* includes DT_JMPREL in its
|
||||
range. Note that according to the ELF spec, this is completely legal!
|
||||
But conditionally define things so that on machines we know this will
|
||||
not happen we do something more optimal. */
|
||||
|
||||
#ifdef ELF_MACHINE_PLTREL_OVERLAP
|
||||
/* ELF_MACHINE_PLTREL_OVERLAP is only used for s390, powerpc and sparc.
|
||||
We will keep it for now */
|
||||
|
||||
static void _elf_dynamic_do_reloc(struct link_map* l, d_val_utype dt_reloc, d_val_utype dt_reloc_sz,
|
||||
void (*do_reloc)(struct link_map*, ElfW(Addr), size_t)) {
|
||||
struct {
|
||||
ElfW(Addr) start, size;
|
||||
} ranges[3];
|
||||
|
||||
ranges[0].size = ranges[1].size = ranges[2].size = 0;
|
||||
|
||||
if (l->l_info[dt_reloc]) {
|
||||
ranges[0].start = D_PTR(l->l_info[dt_reloc]);
|
||||
ranges[0].size = l->l_info[dt_reloc_sz]->d_un.d_val;
|
||||
}
|
||||
|
||||
for (int ranges_index = 0; ranges_index < 3; ++ranges_index)
|
||||
(*do_reloc)(l, ranges[ranges_index].start, ranges[ranges_index].size);
|
||||
}
|
||||
#else
|
||||
/* Now this part is for our x86s machines */
|
||||
|
||||
static void __attribute__((unused))
|
||||
_elf_dynamic_do_reloc(struct link_map* l, d_val_utype dt_reloc, d_val_utype dt_reloc_sz,
|
||||
void (*do_reloc)(struct link_map*, ElfW(Addr), size_t)) {
|
||||
struct {
|
||||
ElfW(Addr) start, size;
|
||||
} ranges[2];
|
||||
ranges[0].size = ranges[1].size = 0;
|
||||
ranges[0].start = ranges[1].start = 0;
|
||||
|
||||
if (l->l_info[dt_reloc]) {
|
||||
ranges[0].start = D_PTR(l->l_info[dt_reloc]);
|
||||
ranges[0].size = l->l_info[dt_reloc_sz]->d_un.d_val;
|
||||
}
|
||||
|
||||
if (l->l_info[DT_PLTREL] && l->l_info[DT_PLTREL]->d_un.d_val == dt_reloc) {
|
||||
ElfW(Addr) start = D_PTR(l->l_info[DT_JMPREL]);
|
||||
|
||||
/* This test does not only detect whether the relocation
|
||||
sections are in the right order, it also checks whether
|
||||
there is a DT_REL/DT_RELA section. */
|
||||
if (ranges[0].start + ranges[0].size != start) {
|
||||
ranges[1].start = start;
|
||||
ranges[1].size = l->l_info[DT_PLTRELSZ]->d_un.d_val;
|
||||
} else {
|
||||
/* Combine processing the sections. */
|
||||
assert(ranges[0].start + ranges[0].size == start);
|
||||
ranges[0].size += l->l_info[DT_PLTRELSZ]->d_un.d_val;
|
||||
}
|
||||
}
|
||||
|
||||
for (int ranges_index = 0; ranges_index < 2; ++ranges_index)
|
||||
(*do_reloc)(l, ranges[ranges_index].start, ranges[ranges_index].size);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, l) \
|
||||
_elf_dynamic_do_reloc(l, DT_##RELOC, DT_##RELOC##SZ, &elf_dynamic_do_##reloc)
|
||||
#define _ELF_DYNAMIC_REDO_RELOC(RELOC, reloc, l) elf_dynamic_redo_##reloc(l)
|
||||
|
||||
#if ELF_MACHINE_NO_REL || ELF_MACHINE_NO_RELA
|
||||
#define _ELF_CHECK_REL 0
|
||||
#else
|
||||
#define _ELF_CHECK_REL 1
|
||||
#endif
|
||||
|
||||
#if !ELF_MACHINE_NO_REL
|
||||
#include "do-rel.h"
|
||||
#define ELF_DYNAMIC_DO_REL(l) _ELF_DYNAMIC_DO_RELOC(REL, rel, l)
|
||||
#define ELF_DYNAMIC_COPY_REL(l1, l2) elf_dynamic_copy_rel(l1, l2)
|
||||
#define ELF_DYNAMIC_REDO_REL(l) _ELF_DYNAMIC_REDO_RELOC(REL, rel, l)
|
||||
#else
|
||||
/* nothing to do */
|
||||
#define ELF_DYNAMIC_DO_REL(l)
|
||||
//# define ELF_DYNAMIC_COPY_REL(l1, l2)
|
||||
#define ELF_DYNAMIC_REDO_REL(l)
|
||||
#endif
|
||||
|
||||
#if !ELF_MACHINE_NO_RELA
|
||||
#define DO_RELA
|
||||
#include "do-rel.h"
|
||||
#define ELF_DYNAMIC_DO_RELA(l) _ELF_DYNAMIC_DO_RELOC(RELA, rela, l)
|
||||
//# define ELF_DYNAMIC_COPY_RELA(l1, l2) elf_dynamic_copy_rela(l, l2)
|
||||
#define ELF_DYNAMIC_REDO_RELA(l) _ELF_DYNAMIC_REDO_RELOC(RELA, rela, l)
|
||||
#else
|
||||
/* nothing to do */
|
||||
#define ELF_DYNAMIC_DO_RELA(l)
|
||||
//# define ELF_DYNAMIC_COPY_RELA(l1, l2)
|
||||
#define ELF_DYNAMIC_REDO_RELA(l)
|
||||
#endif
|
||||
|
||||
/* This can't just be an inline function because GCC is too dumb
|
||||
to inline functions containing inlines themselves. */
|
||||
#define ELF_DYNAMIC_RELOCATE(l) \
|
||||
do { \
|
||||
ELF_DYNAMIC_DO_REL(l); \
|
||||
ELF_DYNAMIC_DO_RELA(l); \
|
||||
} while (0)
|
||||
|
||||
#if 0
|
||||
#define ELF_DYNAMIC_COPY(l1, l2) \
|
||||
do { \
|
||||
ELF_DYNAMIC_COPY_REL(l1, l2); \
|
||||
ELF_DYNAMIC_COPY_RELA(l1, l2); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define ELF_REDO_DYNAMIC_RELOCATE(l) \
|
||||
do { \
|
||||
ELF_DYNAMIC_REDO_REL(l); \
|
||||
ELF_DYNAMIC_REDO_RELA(l); \
|
||||
} while (0)
|
||||
+20
-548
@@ -38,9 +38,7 @@ enum object_type {
|
||||
OBJECT_INTERNAL = 0,
|
||||
OBJECT_LOAD = 1,
|
||||
OBJECT_MAPPED = 2,
|
||||
OBJECT_REMAP = 3,
|
||||
OBJECT_USER = 4,
|
||||
OBJECT_VDSO = 5,
|
||||
OBJECT_VDSO = 3,
|
||||
};
|
||||
|
||||
/* Structure describing a loaded shared object. The `l_next' and `l_prev'
|
||||
@@ -59,48 +57,20 @@ struct link_map {
|
||||
|
||||
ElfW(Addr) l_addr; /* Base address shared object is loaded at. */
|
||||
const char* l_name; /* Absolute file name object was found in. */
|
||||
ElfW(Dyn)* l_real_ld; /* Dynamic section of the shared object. */
|
||||
struct link_map* l_next; /* Chain of loaded objects. */
|
||||
struct link_map* l_prev;
|
||||
|
||||
/* All following members are internal to the dynamic linker.
|
||||
They may change without notice. */
|
||||
ElfW(Dyn)* l_ld;
|
||||
char* l_soname;
|
||||
|
||||
ElfW(Dyn)*
|
||||
l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM];
|
||||
const ElfW(Phdr)* l_phdr; /* Pointer to program header table in core. */
|
||||
ElfW(Addr) l_entry; /* Entry point location. */
|
||||
ElfW(Half) l_phnum; /* Number of program header entries. */
|
||||
ElfW(Half) l_ldnum; /* Number of dynamic segment entries. */
|
||||
|
||||
/* Start and finish of memory map for this object. l_map_start
|
||||
need not be the same as l_addr. */
|
||||
ElfW(Addr) l_map_start, l_map_end;
|
||||
|
||||
bool l_resolved;
|
||||
ElfW(Addr) l_resolved_map;
|
||||
const char* l_interp_libname;
|
||||
ElfW(Addr) l_main_entry;
|
||||
|
||||
/* Information used to change permission after the relocations are
|
||||
done. */
|
||||
ElfW(Addr) l_relro_addr;
|
||||
size_t l_relro_size;
|
||||
|
||||
/* For DT_HASH */
|
||||
Elf_Symndx l_nbuckets;
|
||||
const Elf_Symndx* l_buckets;
|
||||
const Elf_Symndx* l_chain;
|
||||
|
||||
/* For DT_GNU_HASH */
|
||||
Elf32_Word l_gnu_bitmask_idxbits;
|
||||
Elf32_Word l_gnu_shift;
|
||||
const ElfW(Addr)* l_gnu_bitmask;
|
||||
const Elf32_Word* l_gnu_buckets;
|
||||
const Elf32_Word* l_gnu_chain_zero;
|
||||
|
||||
/* pointer to related file */
|
||||
struct shim_handle* l_file;
|
||||
|
||||
@@ -114,136 +84,15 @@ struct link_map {
|
||||
struct shim_vma* vma;
|
||||
} loadcmds[MAX_LOADCMDS];
|
||||
int nloadcmds;
|
||||
|
||||
struct textrel {
|
||||
ElfW(Addr) start, end;
|
||||
int prot;
|
||||
struct textrel* next;
|
||||
} * textrels;
|
||||
|
||||
#define MAX_LINKSYMS 32
|
||||
struct linksym {
|
||||
void* rel;
|
||||
ElfW(Sym)* sym;
|
||||
void* reloc;
|
||||
} linksyms[MAX_LINKSYMS];
|
||||
int nlinksyms;
|
||||
};
|
||||
|
||||
struct link_map* lookup_symbol(const char* undef_name, ElfW(Sym)** ref);
|
||||
#define RELOCATE(l, addr) ((ElfW(Addr))(addr) + (ElfW(Addr))((l)->l_addr))
|
||||
|
||||
static struct link_map* loaded_libraries = NULL;
|
||||
static struct link_map* internal_map = NULL;
|
||||
static struct link_map* interp_map = NULL;
|
||||
static struct link_map* vdso_map = NULL;
|
||||
|
||||
/* This macro is used as a callback from the ELF_DYNAMIC_RELOCATE code. */
|
||||
static ElfW(Addr) resolve_map(const char** strtab, ElfW(Sym)** ref) {
|
||||
if (ELFW(ST_BIND)((*ref)->st_info) != STB_LOCAL) {
|
||||
struct link_map* l = lookup_symbol((*strtab) + (*ref)->st_name, ref);
|
||||
if (l) {
|
||||
*strtab = (const void*)D_PTR(l->l_info[DT_STRTAB]);
|
||||
return l->l_addr;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int protect_page(struct link_map* l, void* addr, size_t size) {
|
||||
struct loadcmd* c = l->loadcmds;
|
||||
int prot = 0;
|
||||
|
||||
for (; c < &l->loadcmds[l->nloadcmds]; c++)
|
||||
if ((void*)l->l_addr + c->mapstart <= addr && addr + size <= (void*)l->l_addr + c->mapend)
|
||||
break;
|
||||
|
||||
if (c < &l->loadcmds[l->nloadcmds])
|
||||
prot = c->prot;
|
||||
|
||||
struct textrel* t = l->textrels;
|
||||
struct textrel** loc = &l->textrels;
|
||||
|
||||
for (; t; t = t->next) {
|
||||
if ((void*)t->start <= addr && addr + size <= (void*)t->end)
|
||||
return 0;
|
||||
|
||||
loc = &t->next;
|
||||
}
|
||||
|
||||
if ((prot & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) {
|
||||
struct shim_vma_info vma_info;
|
||||
|
||||
/* the actual protection of the vma might be changed */
|
||||
if (lookup_vma(addr, &vma_info) < 0)
|
||||
return 0;
|
||||
|
||||
prot = vma_info.prot;
|
||||
if (vma_info.file) {
|
||||
put_handle(vma_info.file);
|
||||
}
|
||||
|
||||
if ((prot & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE))
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* start = ALLOC_ALIGN_DOWN_PTR(addr);
|
||||
void* end = ALLOC_ALIGN_UP_PTR(addr + size);
|
||||
|
||||
if (!DkVirtualMemoryProtect(start, end - start,
|
||||
PAL_PROT_READ | PAL_PROT_WRITE | LINUX_PROT_TO_PAL(prot, /*map_flags=*/0)))
|
||||
return -PAL_ERRNO();
|
||||
|
||||
if (!c)
|
||||
return 0;
|
||||
|
||||
t = malloc(sizeof(struct textrel));
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
t->start = (ElfW(Addr))start;
|
||||
t->end = (ElfW(Addr))end;
|
||||
t->prot = prot;
|
||||
t->next = NULL;
|
||||
*loc = t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reprotect_map(struct link_map* l) {
|
||||
struct textrel* t = l->textrels;
|
||||
struct textrel* next;
|
||||
int ret = 0;
|
||||
|
||||
while (t) {
|
||||
struct loadcmd* c = l->loadcmds;
|
||||
|
||||
for (; c < &l->loadcmds[l->nloadcmds]; c++)
|
||||
if (l->l_addr + c->mapstart <= t->start && t->end <= l->l_addr + c->mapend)
|
||||
break;
|
||||
|
||||
ElfW(Addr) start = t->start, end = t->end;
|
||||
int prot = t->prot;
|
||||
next = t->next;
|
||||
free(t);
|
||||
t = next;
|
||||
l->textrels = t;
|
||||
|
||||
if (c && !DkVirtualMemoryProtect((void*)start, end - start,
|
||||
LINUX_PROT_TO_PAL(prot, /*map_flags=*/0))) {
|
||||
ret = -PAL_ERRNO();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define RESOLVE_MAP(strtab, ref) resolve_map(strtab, ref)
|
||||
#define PROTECT_PAGE(map, addr, size) protect_page(map, addr, size)
|
||||
#define USE__THREAD 0 /* disable TLS support */
|
||||
|
||||
#include "rel.h"
|
||||
|
||||
static struct link_map* new_elf_object(const char* realname, int type) {
|
||||
struct link_map* new;
|
||||
|
||||
@@ -274,60 +123,6 @@ static struct link_map* new_elf_object(const char* realname, int type) {
|
||||
#define FILEBUF_SIZE 832
|
||||
#endif
|
||||
|
||||
/* Cache the location of MAP's hash table. */
|
||||
static void setup_elf_hash(struct link_map* map) {
|
||||
Elf_Symndx* hash;
|
||||
|
||||
if (map->l_info[DT_ADDRTAGIDX(DT_GNU_HASH) + DT_NUM + DT_THISPROCNUM +
|
||||
DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM
|
||||
] != NULL) {
|
||||
Elf32_Word* hash32 =
|
||||
(void*)D_PTR(map->l_info[DT_ADDRTAGIDX(DT_GNU_HASH) + DT_NUM + DT_THISPROCNUM +
|
||||
DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM]);
|
||||
|
||||
map->l_nbuckets = *hash32++;
|
||||
|
||||
Elf32_Word symbias = *hash32++;
|
||||
Elf32_Word bitmask_nwords = *hash32++;
|
||||
|
||||
assert(IS_POWER_OF_2(bitmask_nwords));
|
||||
map->l_gnu_bitmask_idxbits = bitmask_nwords - 1;
|
||||
map->l_gnu_shift = *hash32++;
|
||||
|
||||
map->l_gnu_bitmask = (ElfW(Addr)*)hash32;
|
||||
hash32 += __ELF_NATIVE_CLASS / 32 * bitmask_nwords;
|
||||
|
||||
map->l_gnu_buckets = hash32;
|
||||
hash32 += map->l_nbuckets;
|
||||
map->l_gnu_chain_zero = hash32 - symbias;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!map->l_info[DT_HASH])
|
||||
return;
|
||||
|
||||
hash = (void*)D_PTR(map->l_info[DT_HASH]);
|
||||
|
||||
/* Structure of DT_HASH:
|
||||
The bucket array forms the hast table itself. The entries in the
|
||||
chain array parallel the symbol table.
|
||||
[ nbucket ]
|
||||
[ nchain ]
|
||||
[ bucket[0] ]
|
||||
[ ... ]
|
||||
[ bucket[nbucket-1] ]
|
||||
[ chain[0] ]
|
||||
[ ... ]
|
||||
[ chain[nchain-1] ] */
|
||||
|
||||
map->l_nbuckets = *hash++;
|
||||
hash++;
|
||||
map->l_buckets = hash;
|
||||
hash += map->l_nbuckets;
|
||||
map->l_chain = hash;
|
||||
}
|
||||
|
||||
/* TODO: This function needs a cleanup and to be split into smaller parts. It is impossible to do
|
||||
* a proper cleanup on any failure right now. */
|
||||
/* Map in the shared object NAME, actually located in REALNAME, and already
|
||||
@@ -379,9 +174,6 @@ static struct link_map* __map_elf_object(struct shim_handle* file, const void* f
|
||||
size_t maplength = header->e_phnum * sizeof(ElfW(Phdr));
|
||||
const ElfW(Phdr)* phdr = (fbp + header->e_phoff);
|
||||
|
||||
if (type == OBJECT_REMAP)
|
||||
goto do_remap;
|
||||
|
||||
if (type == OBJECT_LOAD && header->e_phoff + maplength <= (size_t)fbp_len) {
|
||||
new_phdr = (ElfW(Phdr)*)malloc(maplength);
|
||||
if (!new_phdr) {
|
||||
@@ -401,15 +193,10 @@ static struct link_map* __map_elf_object(struct shim_handle* file, const void* f
|
||||
|
||||
const ElfW(Phdr)* ph;
|
||||
for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph) {
|
||||
/* These entries tell us where to find things once the file's
|
||||
segments are mapped in. We record the addresses it says
|
||||
verbatim, and later correct for the run-time load address. */
|
||||
switch (ph->p_type) {
|
||||
/* These entries tell us where to find things once the file's
|
||||
segments are mapped in. We record the addresses it says
|
||||
verbatim, and later correct for the run-time load address. */
|
||||
case PT_DYNAMIC:
|
||||
l->l_ld = (void*)ph->p_vaddr;
|
||||
l->l_ldnum = ph->p_memsz / sizeof(ElfW(Dyn));
|
||||
break;
|
||||
|
||||
case PT_INTERP:
|
||||
l->l_interp_libname = (const char*)ph->p_vaddr;
|
||||
break;
|
||||
@@ -462,11 +249,6 @@ static struct link_map* __map_elf_object(struct shim_handle* file, const void* f
|
||||
#endif
|
||||
c->flags = MAP_PRIVATE | MAP_FILE;
|
||||
break;
|
||||
|
||||
case PT_GNU_RELRO:
|
||||
l->l_relro_addr = ph->p_vaddr;
|
||||
l->l_relro_size = ph->p_memsz;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,7 +342,7 @@ do_remap:
|
||||
type = OBJECT_MAPPED;
|
||||
}
|
||||
|
||||
if (type != OBJECT_INTERNAL && type != OBJECT_USER && type != OBJECT_VDSO) {
|
||||
if (type != OBJECT_INTERNAL && type != OBJECT_VDSO) {
|
||||
ret = bkeep_mmap_fixed(mapaddr, c->mapend - c->mapstart, c->prot,
|
||||
c->flags | MAP_FIXED | MAP_PRIVATE
|
||||
| (type == OBJECT_INTERNAL ? VMA_INTERNAL : 0),
|
||||
@@ -571,7 +353,7 @@ do_remap:
|
||||
}
|
||||
}
|
||||
|
||||
if (type == OBJECT_LOAD || type == OBJECT_REMAP) {
|
||||
if (type == OBJECT_LOAD) {
|
||||
if ((*mmap)(file, &mapaddr, c->mapend - c->mapstart, c->prot,
|
||||
c->flags | MAP_FIXED | MAP_PRIVATE, c->mapoff) < 0) {
|
||||
errstring = "failed to map segment from shared object";
|
||||
@@ -600,7 +382,7 @@ do_remap:
|
||||
We can just zero it. */
|
||||
zeropage = zeroend;
|
||||
|
||||
if (type != OBJECT_MAPPED && type != OBJECT_INTERNAL && type != OBJECT_USER &&
|
||||
if (type != OBJECT_MAPPED && type != OBJECT_INTERNAL &&
|
||||
type != OBJECT_VDSO && zeropage > zero) {
|
||||
/* Zero the final part of the last page of the segment. */
|
||||
if ((c->prot & PROT_WRITE) == 0) {
|
||||
@@ -623,7 +405,7 @@ do_remap:
|
||||
}
|
||||
|
||||
if (zeroend > zeropage) {
|
||||
if (type != OBJECT_INTERNAL && type != OBJECT_USER && type != OBJECT_VDSO) {
|
||||
if (type != OBJECT_INTERNAL && type != OBJECT_VDSO) {
|
||||
ret = bkeep_mmap_fixed((void*)zeropage, zeroend - zeropage, c->prot,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED
|
||||
| (type == OBJECT_INTERNAL ? VMA_INTERNAL : 0),
|
||||
@@ -634,7 +416,7 @@ do_remap:
|
||||
}
|
||||
}
|
||||
|
||||
if (type != OBJECT_MAPPED && type != OBJECT_INTERNAL && type != OBJECT_USER &&
|
||||
if (type != OBJECT_MAPPED && type != OBJECT_INTERNAL &&
|
||||
type != OBJECT_VDSO) {
|
||||
PAL_PTR mapat =
|
||||
DkVirtualMemoryAlloc((void*)zeropage, zeroend - zeropage, /*alloc_type=*/0,
|
||||
@@ -650,29 +432,6 @@ do_remap:
|
||||
++c;
|
||||
}
|
||||
|
||||
if (type == OBJECT_REMAP)
|
||||
goto success;
|
||||
|
||||
if (l->l_ld == 0) {
|
||||
if (e_type == ET_DYN) {
|
||||
errstring = "object file has no dynamic section";
|
||||
goto call_lose;
|
||||
}
|
||||
} else {
|
||||
l->l_real_ld = (ElfW(Dyn)*)RELOCATE(l, l->l_ld);
|
||||
l->l_ld = malloc_copy(l->l_real_ld, sizeof(ElfW(Dyn)) * l->l_ldnum);
|
||||
}
|
||||
|
||||
elf_get_dynamic_info(l);
|
||||
|
||||
/* When we profile the SONAME might be needed for something else but
|
||||
loading. Add it right away. */
|
||||
if (l->l_info[DT_STRTAB] && l->l_info[DT_SONAME]) {
|
||||
/* DEP 3/12/18: This string is not stable; copy it. */
|
||||
char* tmp = (char*)(D_PTR(l->l_info[DT_STRTAB]) + D_PTR(l->l_info[DT_SONAME]));
|
||||
l->l_soname = malloc_copy(tmp, strlen(tmp) + 1);
|
||||
}
|
||||
|
||||
if (l->l_phdr == NULL) {
|
||||
/* The program header is not contained in any of the segments.
|
||||
We have to allocate memory ourself and copy it over from out
|
||||
@@ -691,10 +450,6 @@ do_remap:
|
||||
|
||||
l->l_entry = RELOCATE(l, l->l_entry);
|
||||
|
||||
/* Set up the symbol hash table. */
|
||||
setup_elf_hash(l);
|
||||
|
||||
success:
|
||||
free(new_phdr);
|
||||
return l;
|
||||
|
||||
@@ -820,7 +575,7 @@ static int __check_elf_header(void* fbp, size_t len) {
|
||||
}
|
||||
|
||||
/* Now we check if the host match the elf machine profile */
|
||||
if (!elf_machine_matches_host(ehdr)) {
|
||||
if (ehdr->e_machine != EM_X86_64) {
|
||||
errstring = "ELF file does not match with the host";
|
||||
goto verify_failed;
|
||||
}
|
||||
@@ -928,13 +683,11 @@ static void replace_link_map(struct link_map* new, struct link_map* old) {
|
||||
loaded_libraries = new;
|
||||
}
|
||||
|
||||
static int do_relocate_object(struct link_map* l);
|
||||
|
||||
static int __load_elf_object(struct shim_handle* file, void* addr, int type) {
|
||||
char* hdr = addr;
|
||||
int len = 0, ret = 0;
|
||||
|
||||
if (type == OBJECT_LOAD || type == OBJECT_REMAP) {
|
||||
if (type == OBJECT_LOAD) {
|
||||
hdr = __alloca(FILEBUF_SIZE);
|
||||
if ((ret = __load_elf_header(file, hdr, &len)) < 0)
|
||||
goto out;
|
||||
@@ -947,35 +700,20 @@ static int __load_elf_object(struct shim_handle* file, void* addr, int type) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (type != OBJECT_INTERNAL && type != OBJECT_VDSO)
|
||||
do_relocate_object(map);
|
||||
|
||||
if (internal_map) {
|
||||
map->l_resolved = true;
|
||||
map->l_resolved_map = internal_map->l_addr;
|
||||
}
|
||||
|
||||
if (type == OBJECT_INTERNAL)
|
||||
internal_map = map;
|
||||
if (type == OBJECT_VDSO)
|
||||
vdso_map = map;
|
||||
|
||||
if (type != OBJECT_REMAP) {
|
||||
if (file) {
|
||||
get_handle(file);
|
||||
map->l_file = file;
|
||||
}
|
||||
|
||||
add_link_map(map);
|
||||
if (file) {
|
||||
get_handle(file);
|
||||
map->l_file = file;
|
||||
}
|
||||
|
||||
if ((type == OBJECT_LOAD || type == OBJECT_REMAP || type == OBJECT_USER) && map->l_file &&
|
||||
!qstrempty(&map->l_file->uri)) {
|
||||
if (type == OBJECT_REMAP)
|
||||
remove_r_debug((void*)map->l_addr);
|
||||
add_link_map(map);
|
||||
|
||||
append_r_debug(qstrgetstr(&map->l_file->uri), (void*)map->l_addr,
|
||||
(void*)map->l_real_ld);
|
||||
if (type == OBJECT_LOAD && map->l_file && !qstrempty(&map->l_file->uri)) {
|
||||
append_r_debug(qstrgetstr(&map->l_file->uri), (void*)map->l_addr);
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -987,242 +725,8 @@ struct sym_val {
|
||||
struct link_map* m;
|
||||
};
|
||||
|
||||
static uint_fast32_t elf_fast_hash(const char* s) {
|
||||
uint_fast32_t h = 5381;
|
||||
for (unsigned char c = *s; c != '\0'; c = *++s) {
|
||||
h = h * 33 + c;
|
||||
}
|
||||
return h & 0xffffffff;
|
||||
}
|
||||
|
||||
/* This is the hashing function specified by the ELF ABI. In the
|
||||
first five operations no overflow is possible so we optimized it a
|
||||
bit. */
|
||||
static unsigned long int elf_hash(const char* name_arg) {
|
||||
const unsigned char* name = (const unsigned char*)name_arg;
|
||||
unsigned long int hash = 0;
|
||||
|
||||
if (*name == '\0')
|
||||
return hash;
|
||||
|
||||
hash = *name++;
|
||||
if (*name == '\0')
|
||||
return hash;
|
||||
|
||||
hash = (hash << 4) + *name++;
|
||||
if (*name == '\0')
|
||||
return hash;
|
||||
|
||||
hash = (hash << 4) + *name++;
|
||||
if (*name == '\0')
|
||||
return hash;
|
||||
|
||||
hash = (hash << 4) + *name++;
|
||||
if (*name == '\0')
|
||||
return hash;
|
||||
|
||||
hash = (hash << 4) + *name++;
|
||||
while (*name != '\0') {
|
||||
unsigned long int hi;
|
||||
hash = (hash << 4) + *name++;
|
||||
hi = hash & 0xf0000000;
|
||||
|
||||
/* The algorithm specified in the ELF ABI is as follows:
|
||||
if (hi != 0)
|
||||
hash ^= hi >> 24;
|
||||
|
||||
hash &= ~hi;
|
||||
But the following is equivalent and a lot faster, especially on
|
||||
modern processors. */
|
||||
|
||||
hash ^= hi;
|
||||
hash ^= hi >> 24;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Check whether the symbol matches. */
|
||||
static ElfW(Sym)* check_match(ElfW(Sym)* sym, ElfW(Sym)* ref, const char* strtab,
|
||||
const char* undef_name, int len) {
|
||||
unsigned int stt = ELFW(ST_TYPE)(sym->st_info);
|
||||
|
||||
if ((sym->st_value == 0 /* No value */ && stt != STT_TLS) || sym->st_shndx == SHN_UNDEF)
|
||||
return NULL;
|
||||
|
||||
/* Ignore all but STT_NOTYPE, STT_OBJECT, STT_FUNC,
|
||||
STT_COMMON, STT_TLS, and STT_GNU_IFUNC since these are no
|
||||
code/data definitions. */
|
||||
#define ALLOWED_STT \
|
||||
((1 << STT_NOTYPE) | (1 << STT_OBJECT) | (1 << STT_FUNC) | (1 << STT_COMMON) | \
|
||||
(1 << STT_TLS) | (1 << STT_GNU_IFUNC))
|
||||
|
||||
if (((1 << stt) & ALLOWED_STT) == 0)
|
||||
return NULL;
|
||||
|
||||
if (sym != ref && memcmp(strtab + sym->st_name, undef_name, len + 1))
|
||||
/* Not the symbol we are looking for. */
|
||||
return NULL;
|
||||
|
||||
/* There cannot be another entry for this symbol so stop here. */
|
||||
return sym;
|
||||
}
|
||||
|
||||
|
||||
static ElfW(Sym)* do_lookup_map(ElfW(Sym)* ref, const char* undef_name, const uint_fast32_t hash,
|
||||
unsigned long int elf_hash, const struct link_map* map) {
|
||||
/* These variables are used in the nested function. */
|
||||
Elf_Symndx symidx;
|
||||
ElfW(Sym)* sym;
|
||||
/* The tables for this map. */
|
||||
ElfW(Sym)* symtab = (void*)D_PTR(map->l_info[DT_SYMTAB]);
|
||||
const char* strtab = (const void*)D_PTR(map->l_info[DT_STRTAB]);
|
||||
int len = strlen(undef_name);
|
||||
|
||||
const ElfW(Addr)* bitmask = map->l_gnu_bitmask;
|
||||
|
||||
if (bitmask != NULL) {
|
||||
ElfW(Addr) bitmask_word = bitmask[(hash / __ELF_NATIVE_CLASS) & map->l_gnu_bitmask_idxbits];
|
||||
|
||||
unsigned int hashbit1 = hash & (__ELF_NATIVE_CLASS - 1);
|
||||
unsigned int hashbit2 = (hash >> map->l_gnu_shift) & (__ELF_NATIVE_CLASS - 1);
|
||||
|
||||
if ((bitmask_word >> hashbit1) & (bitmask_word >> hashbit2) & 1) {
|
||||
Elf32_Word bucket = map->l_gnu_buckets[hash % map->l_nbuckets];
|
||||
|
||||
if (bucket != 0) {
|
||||
const Elf32_Word* hasharr = &map->l_gnu_chain_zero[bucket];
|
||||
|
||||
do {
|
||||
if (((*hasharr ^ hash) >> 1) == 0) {
|
||||
symidx = hasharr - map->l_gnu_chain_zero;
|
||||
sym = check_match(&symtab[symidx], ref, strtab, undef_name, len);
|
||||
if (sym != NULL)
|
||||
return sym;
|
||||
}
|
||||
} while ((*hasharr++ & 1u) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* No symbol found. */
|
||||
symidx = SHN_UNDEF;
|
||||
} else {
|
||||
/* Use the old SysV-style hash table. Search the appropriate
|
||||
hash bucket in this object's symbol table for a definition
|
||||
for the same symbol name. */
|
||||
for (symidx = map->l_buckets[elf_hash % map->l_nbuckets]; symidx != STN_UNDEF;
|
||||
symidx = map->l_chain[symidx]) {
|
||||
sym = check_match(&symtab[symidx], ref, strtab, undef_name, len);
|
||||
if (sym != NULL)
|
||||
return sym;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Inner part of the lookup functions. We return a value > 0 if we
|
||||
found the symbol, the value 0 if nothing is found and < 0 if
|
||||
something bad happened. */
|
||||
static ElfW(Sym)* __do_lookup(const char* undef_name, ElfW(Sym)* ref, struct link_map* map) {
|
||||
const uint_fast32_t fast_hash = elf_fast_hash(undef_name);
|
||||
const long int hash = elf_hash(undef_name);
|
||||
return do_lookup_map(ref, undef_name, fast_hash, hash, map);
|
||||
}
|
||||
|
||||
static int do_lookup(const char* undef_name, ElfW(Sym)* ref, struct sym_val* result) {
|
||||
ElfW(Sym)* sym = NULL;
|
||||
|
||||
sym = __do_lookup(undef_name, ref, internal_map);
|
||||
|
||||
if (!sym)
|
||||
return 0;
|
||||
|
||||
switch (ELFW(ST_BIND)(sym->st_info)) {
|
||||
case STB_WEAK:
|
||||
/* Weak definition. Use this value if we don't find another. */
|
||||
if (!result->s) {
|
||||
result->s = sym;
|
||||
result->m = (struct link_map*)internal_map;
|
||||
}
|
||||
break;
|
||||
|
||||
/* FALLTHROUGH */
|
||||
case STB_GLOBAL:
|
||||
case STB_GNU_UNIQUE:
|
||||
/* success: */
|
||||
/* Global definition. Just what we need. */
|
||||
result->s = sym;
|
||||
result->m = (struct link_map*)internal_map;
|
||||
return 1;
|
||||
|
||||
default:
|
||||
/* Local symbols are ignored. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* We have not found anything until now. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Search loaded objects' symbol tables for a definition of the symbol
|
||||
UNDEF_NAME, perhaps with a requested version for the symbol.
|
||||
|
||||
We must never have calls to the audit functions inside this function
|
||||
or in any function which gets called. If this would happen the audit
|
||||
code might create a thread which can throw off all the scope locking. */
|
||||
struct link_map* lookup_symbol(const char* undef_name, ElfW(Sym)** ref) {
|
||||
struct sym_val current_value = {NULL, NULL};
|
||||
|
||||
do_lookup(undef_name, *ref, ¤t_value);
|
||||
|
||||
if (current_value.s == NULL) {
|
||||
*ref = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*ref = current_value.s;
|
||||
return current_value.m;
|
||||
}
|
||||
|
||||
static int do_relocate_object(struct link_map* l) {
|
||||
int ret = 0;
|
||||
|
||||
if (l->l_resolved)
|
||||
ELF_REDO_DYNAMIC_RELOCATE(l);
|
||||
else
|
||||
ELF_DYNAMIC_RELOCATE(l);
|
||||
|
||||
if ((ret = reprotect_map(l)) < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool __need_interp(struct link_map* exec_map) {
|
||||
if (!exec_map->l_interp_libname)
|
||||
return false;
|
||||
|
||||
const char* strtab = (const void*)D_PTR(exec_map->l_info[DT_STRTAB]);
|
||||
const ElfW(Dyn)* d;
|
||||
|
||||
for (d = exec_map->l_ld; d->d_tag != DT_NULL; d++)
|
||||
if (d->d_tag == DT_NEEDED) {
|
||||
const char* name = strtab + d->d_un.d_val;
|
||||
int len = strlen(name);
|
||||
const char* filename = name + len - 1;
|
||||
while (filename > name && *filename != '/') {
|
||||
filename--;
|
||||
}
|
||||
if (*filename == '/')
|
||||
filename++;
|
||||
|
||||
/* if we find a dependency besides libsysdb.so, the
|
||||
interpreter is necessary */
|
||||
if (memcmp(filename, "libsysdb", 8))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return exec_map->l_interp_libname != NULL;
|
||||
}
|
||||
|
||||
extern const char** library_paths;
|
||||
@@ -1448,7 +952,7 @@ int register_library(const char* name, unsigned long load_address) {
|
||||
return err;
|
||||
}
|
||||
|
||||
__load_elf_object(hdl, (void*)load_address, OBJECT_USER);
|
||||
append_r_debug(qstrgetstr(&hdl->uri), (void*)load_address);
|
||||
put_handle(hdl);
|
||||
return 0;
|
||||
}
|
||||
@@ -1546,25 +1050,10 @@ BEGIN_CP_FUNC(library) {
|
||||
|
||||
new_map->l_prev = NULL;
|
||||
new_map->l_next = NULL;
|
||||
new_map->textrels = NULL;
|
||||
|
||||
if (map->l_file)
|
||||
DO_CP_MEMBER(handle, map, new_map, l_file);
|
||||
|
||||
if (map->l_ld) {
|
||||
size_t size = sizeof(ElfW(Dyn)) * map->l_ldnum;
|
||||
ElfW(Dyn)* ld = (void*)(base + ADD_CP_OFFSET(size));
|
||||
memcpy(ld, map->l_ld, size);
|
||||
new_map->l_ld = ld;
|
||||
|
||||
ElfW(Dyn)** start = new_map->l_info;
|
||||
ElfW(Dyn)** end = (void*)start + sizeof(new_map->l_info);
|
||||
ElfW(Dyn)** dyn;
|
||||
for (dyn = start; dyn < end; dyn++)
|
||||
if (*dyn)
|
||||
*dyn = (void*)*dyn + ((void*)ld - (void*)map->l_ld);
|
||||
}
|
||||
|
||||
if (map->l_name) {
|
||||
size_t namelen = strlen(map->l_name);
|
||||
char* name = (char*)(base + ADD_CP_OFFSET(namelen + 1));
|
||||
@@ -1572,13 +1061,6 @@ BEGIN_CP_FUNC(library) {
|
||||
new_map->l_name = name;
|
||||
}
|
||||
|
||||
if (map->l_soname) {
|
||||
size_t sonamelen = strlen(map->l_soname);
|
||||
char* soname = (char*)(base + ADD_CP_OFFSET(sonamelen + 1));
|
||||
memcpy(soname, map->l_soname, sonamelen + 1);
|
||||
new_map->l_soname = soname;
|
||||
}
|
||||
|
||||
ADD_CP_FUNC_ENTRY(off);
|
||||
} else {
|
||||
new_map = (struct link_map*)(base + off);
|
||||
@@ -1594,23 +1076,13 @@ BEGIN_RS_FUNC(library) {
|
||||
struct link_map* map = (void*)(base + GET_CP_FUNC_ENTRY());
|
||||
|
||||
CP_REBASE(map->l_name);
|
||||
CP_REBASE(map->l_soname);
|
||||
CP_REBASE(map->l_file);
|
||||
|
||||
if (map->l_ld && map->l_ld != map->l_real_ld) {
|
||||
CP_REBASE(map->l_ld);
|
||||
CP_REBASE(map->l_info);
|
||||
}
|
||||
|
||||
struct link_map* old_map = __search_map_by_name(map->l_name);
|
||||
|
||||
if (old_map)
|
||||
remove_r_debug((void*)old_map->l_addr);
|
||||
|
||||
if (internal_map && (!map->l_resolved || map->l_resolved_map != internal_map->l_addr)) {
|
||||
do_relocate_object(map);
|
||||
}
|
||||
|
||||
if (old_map)
|
||||
replace_link_map(map, old_map);
|
||||
else
|
||||
|
||||
@@ -26,10 +26,9 @@ void remove_r_debug(void* addr) {
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
void append_r_debug(const char* uri, void* addr, void* dyn_addr) {
|
||||
void append_r_debug(const char* uri, void* addr) {
|
||||
__UNUSED(uri);
|
||||
__UNUSED(addr);
|
||||
__UNUSED(dyn_addr);
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
@@ -38,7 +37,6 @@ void append_r_debug(const char* uri, void* addr, void* dyn_addr) {
|
||||
struct gdb_link_map {
|
||||
void* l_addr;
|
||||
char* l_name;
|
||||
void* l_ld;
|
||||
struct gdb_link_map *l_next, *l_prev;
|
||||
};
|
||||
|
||||
@@ -87,7 +85,7 @@ void remove_r_debug(void* addr) {
|
||||
free(m);
|
||||
}
|
||||
|
||||
void append_r_debug(const char* uri, void* addr, void* dyn_addr) {
|
||||
void append_r_debug(const char* uri, void* addr) {
|
||||
struct gdb_link_map* new = malloc(sizeof(struct gdb_link_map));
|
||||
if (!new)
|
||||
return;
|
||||
@@ -99,7 +97,6 @@ void append_r_debug(const char* uri, void* addr, void* dyn_addr) {
|
||||
}
|
||||
|
||||
new->l_addr = addr;
|
||||
new->l_ld = dyn_addr;
|
||||
new->l_name = new_uri;
|
||||
|
||||
struct gdb_link_map* prev = NULL;
|
||||
|
||||
@@ -26,8 +26,6 @@ fs.mount.tmp.type = "chroot"
|
||||
fs.mount.tmp.path = "/tmp"
|
||||
fs.mount.tmp.uri = "file:/tmp"
|
||||
|
||||
# The line below may be causing problems on SGX, see
|
||||
# https://github.com/oscarlab/graphene/issues/1408.
|
||||
sys.brk.max_size = "32M"
|
||||
sys.stack.size = "4M"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user