提交 9bbddf73 编写于 作者: R Rich Felker

reprocess all libc/ldso symbolic relocations in dynamic linking stage 3

commit f3ddd173 introduced early
relocations and subsequent reprocessing as part of the dynamic linker
bootstrap overhaul, to allow use of arbitrary libc functions before
the main application and libraries are loaded, but only reprocessed
GOT/PLT relocation types.

commit c093e2e8 added reprocessing of
non-GOT/PLT relocations to fix an actual regression that was observed
on powerpc, but only for RELA format tables with out-of-line addends.
REL table (inline addends at the relocation address) reprocessing is
trickier because the first relocation pass clobbers the addends.

this patch extends symbolic relocation reprocessing for libc/ldso to
support all relocation types, whether REL or RELA format tables are
used. it is believed not to alter behavior on any existing archs for
the current dynamic linker and libc code. the motivations for this
change are consistency and future-proofing. it ensures that behavior
does not differ depending on whether REL or RELA tables are used,
which could lead to undetected arch-specific bugs. it also ensures
that, if in the future code depending on additional relocation types
is added to libc.so, either at the source level or as part of the
compiler runtime that gets pulled in (for example, soft-float with TLS
for fenv), the new code will work properly.

the implementation concept is simple: stage 2 of the dynamic linker
counts the number of symbolic relocations in the libc/ldso REL table
and allocates a VLA to save their addends into; stage 3 then uses the
saved addends in place of the inline ones which were clobbered. for
stack safety, a hard limit (currently 4k) is imposed on the number of
such addends; this should be a couple orders of magnitude larger than
the actual need. this number is not a runtime variable that could
break fail-safety; it is constant for a given libc.so build.
上级 768b82c6
......@@ -74,7 +74,6 @@ struct dso {
volatile int new_dtv_idx, new_tls_idx;
struct td_index *td_index;
struct dso *fini_next;
int rel_early_relative, rel_update_got;
char *shortname;
char buf[];
};
......@@ -96,6 +95,9 @@ static struct builtin_tls {
} builtin_tls[1];
#define MIN_TLS_ALIGN offsetof(struct builtin_tls, pt)
#define ADDEND_LIMIT 4096
static size_t *saved_addends, *apply_addends_to;
static struct dso ldso;
static struct dso *head, *tail, *fini_head;
static char *env_path, *sys_path;
......@@ -256,9 +258,17 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
size_t sym_val;
size_t tls_val;
size_t addend;
int skip_relative = 0, reuse_addends = 0, save_slot = 0;
if (dso == &ldso) {
/* Only ldso's REL table needs addend saving/reuse. */
if (rel == apply_addends_to)
reuse_addends = 1;
skip_relative = 1;
}
for (; rel_size; rel+=stride, rel_size-=stride*sizeof(size_t)) {
if (dso->rel_early_relative && IS_RELATIVE(rel[1])) continue;
if (skip_relative && IS_RELATIVE(rel[1])) continue;
type = R_TYPE(rel[1]);
sym_index = R_SYM(rel[1]);
reloc_addr = (void *)(base + rel[0]);
......@@ -280,12 +290,20 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
def.dso = dso;
}
int gotplt = (type == REL_GOT || type == REL_PLT);
if (dso->rel_update_got && !gotplt && stride==2) continue;
addend = stride>2 ? rel[2]
: gotplt || type==REL_COPY ? 0
: *reloc_addr;
if (stride > 2) {
addend = rel[2];
} else if (type==REL_GOT || type==REL_PLT|| type==REL_COPY) {
addend = 0;
} else if (reuse_addends) {
/* Save original addend in stage 2 where the dso
* chain consists of just ldso; otherwise read back
* saved addend since the inline one was clobbered. */
if (head==&ldso)
saved_addends[save_slot] = *reloc_addr;
addend = saved_addends[save_slot++];
} else {
addend = *reloc_addr;
}
sym_val = def.sym ? (size_t)def.dso->base+def.sym->st_value : 0;
tls_val = def.sym ? def.sym->st_value : 0;
......@@ -879,7 +897,7 @@ static void do_mips_relocs(struct dso *p, size_t *got)
size_t i, j, rel[2];
unsigned char *base = p->base;
i=0; search_vec(p->dynv, &i, DT_MIPS_LOCAL_GOTNO);
if (p->rel_early_relative) {
if (p==&ldso) {
got += i;
} else {
while (i--) *got++ += (size_t)base;
......@@ -1125,15 +1143,29 @@ void __dls2(unsigned char *base, size_t *sp)
ldso.phnum = ehdr->e_phnum;
ldso.phdr = (void *)(base + ehdr->e_phoff);
ldso.phentsize = ehdr->e_phentsize;
ldso.rel_early_relative = 1;
kernel_mapped_dso(&ldso);
decode_dyn(&ldso);
/* Prepare storage for to save clobbered REL addends so they
* can be reused in stage 3. There should be very few. If
* something goes wrong and there are a huge number, abort
* instead of risking stack overflow. */
size_t dyn[DYN_CNT];
decode_vec(ldso.dynv, dyn, DYN_CNT);
size_t *rel = (void *)(base+dyn[DT_REL]);
size_t rel_size = dyn[DT_RELSZ];
size_t symbolic_rel_cnt = 0;
apply_addends_to = rel;
for (; rel_size; rel+=2, rel_size-=2*sizeof(size_t))
if (!IS_RELATIVE(rel[1])) symbolic_rel_cnt++;
if (symbolic_rel_cnt >= ADDEND_LIMIT) a_crash();
size_t addends[symbolic_rel_cnt+1];
saved_addends = addends;
head = &ldso;
reloc_all(&ldso);
ldso.relocated = 0;
ldso.rel_update_got = 1;
/* Call dynamic linker stage-3, __dls3, looking it up
* symbolically as a barrier against moving the address
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册