Rework rtld's TLS Variant I implementation to match r326794

The above commit fixed handling overaligned TLS segments in libc's
TLS Variant I implementation, but rtld provides its own implementation
for dynamically-linked executables which lacks these fixes.  Thus,
port these changes to rtld.

This was previously commited as r337978 and reverted in r338149 due to
exposing a bug the ARM rtld.  This bug was fixed in r338317 by mmel.

Submitted by:	James Clarke
Approved by:	re (kib)
Reviewed by:	kbowling
Testing by:	kbowling (powerpc64), br (riscv), kevans (armv7)
Obtained from:	CheriBSD
Sponsored by:	DARPA, AFRL
Differential Revision:	https://reviews.freebsd.org/D16510
This commit is contained in:
Brooks Davis 2018-09-05 23:23:16 +00:00
parent 9f45c6c124
commit 17fb2856c3
7 changed files with 73 additions and 20 deletions

View File

@ -69,6 +69,8 @@ Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
#define calculate_tls_offset(prev_offset, prev_size, size, align) \
round(prev_offset + prev_size, align)
#define calculate_tls_end(off, size) ((off) + (size))
#define calculate_tls_post_size(align) \
round(TLS_TCB_SIZE, align) - TLS_TCB_SIZE
#define TLS_TCB_SIZE 16
typedef struct {

View File

@ -69,6 +69,8 @@ typedef struct {
#define calculate_tls_offset(prev_offset, prev_size, size, align) \
round(prev_offset + prev_size, align)
#define calculate_tls_end(off, size) ((off) + (size))
#define calculate_tls_post_size(align) \
round(TLS_TCB_SIZE, align) - TLS_TCB_SIZE
extern void *__tls_get_addr(tls_index *ti);

View File

@ -64,10 +64,11 @@ typedef struct {
#define round(size, align) \
(((size) + (align) - 1) & ~((align) - 1))
#define calculate_first_tls_offset(size, align) \
round(TLS_TCB_SIZE, align)
TLS_TCB_SIZE
#define calculate_tls_offset(prev_offset, prev_size, size, align) \
round(prev_offset + prev_size, align)
#define calculate_tls_end(off, size) ((off) + (size))
#define calculate_tls_post_size(align) 0
extern void *__tls_get_addr(tls_index *ti);

View File

@ -74,10 +74,11 @@ void _rtld_powerpc_pltcall(void);
#define round(size, align) \
(((size) + (align) - 1) & ~((align) - 1))
#define calculate_first_tls_offset(size, align) \
round(8, align)
TLS_TCB_SIZE
#define calculate_tls_offset(prev_offset, prev_size, size, align) \
round(prev_offset + prev_size, align)
#define calculate_tls_end(off, size) ((off) + (size))
#define calculate_tls_post_size(align) 0
typedef struct {
unsigned long ti_module;

View File

@ -66,10 +66,11 @@ Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
#define round(size, align) \
(((size) + (align) - 1) & ~((align) - 1))
#define calculate_first_tls_offset(size, align) \
round(16, align)
TLS_TCB_SIZE
#define calculate_tls_offset(prev_offset, prev_size, size, align) \
round(prev_offset + prev_size, align)
#define calculate_tls_end(off, size) ((off) + (size))
#define calculate_tls_post_size(align) 0
typedef struct {
unsigned long ti_module;

View File

@ -89,10 +89,11 @@ Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
#define round(size, align) \
(((size) + (align) - 1) & ~((align) - 1))
#define calculate_first_tls_offset(size, align) \
round(16, align)
TLS_TCB_SIZE
#define calculate_tls_offset(prev_offset, prev_size, size, align) \
round(prev_offset + prev_size, align)
#define calculate_tls_end(off, size) ((off) + (size))
#define calculate_tls_post_size(align) 0
typedef struct {
unsigned long ti_module;

View File

@ -4692,48 +4692,88 @@ tls_get_addr_common(Elf_Addr **dtvp, int index, size_t offset)
#if defined(__aarch64__) || defined(__arm__) || defined(__mips__) || \
defined(__powerpc__) || defined(__riscv)
/*
* Return pointer to allocated TLS block
*/
static void *
get_tls_block_ptr(void *tcb, size_t tcbsize)
{
size_t extra_size, post_size, pre_size, tls_block_size;
size_t tls_init_align;
tls_init_align = MAX(obj_main->tlsalign, 1);
/* Compute fragments sizes. */
extra_size = tcbsize - TLS_TCB_SIZE;
post_size = calculate_tls_post_size(tls_init_align);
tls_block_size = tcbsize + post_size;
pre_size = roundup2(tls_block_size, tls_init_align) - tls_block_size;
return ((char *)tcb - pre_size - extra_size);
}
/*
* Allocate Static TLS using the Variant I method.
*
* For details on the layout, see lib/libc/gen/tls.c.
*
* NB: rtld's tls_static_space variable includes TLS_TCB_SIZE and post_size as
* it is based on tls_last_offset, and TLS offsets here are really TCB
* offsets, whereas libc's tls_static_space is just the executable's static
* TLS segment.
*/
void *
allocate_tls(Obj_Entry *objs, void *oldtcb, size_t tcbsize, size_t tcbalign)
{
Obj_Entry *obj;
char *tcb;
Elf_Addr **tls;
Elf_Addr *dtv;
char *tls_block;
Elf_Addr *dtv, **tcb;
Elf_Addr addr;
int i;
size_t extra_size, maxalign, post_size, pre_size, tls_block_size;
size_t tls_init_align;
if (oldtcb != NULL && tcbsize == TLS_TCB_SIZE)
return (oldtcb);
assert(tcbsize >= TLS_TCB_SIZE);
tcb = xcalloc(1, tls_static_space - TLS_TCB_SIZE + tcbsize);
tls = (Elf_Addr **)(tcb + tcbsize - TLS_TCB_SIZE);
maxalign = MAX(tcbalign, tls_static_max_align);
tls_init_align = MAX(obj_main->tlsalign, 1);
/* Compute fragmets sizes. */
extra_size = tcbsize - TLS_TCB_SIZE;
post_size = calculate_tls_post_size(tls_init_align);
tls_block_size = tcbsize + post_size;
pre_size = roundup2(tls_block_size, tls_init_align) - tls_block_size;
tls_block_size += pre_size + tls_static_space - TLS_TCB_SIZE - post_size;
/* Allocate whole TLS block */
tls_block = malloc_aligned(tls_block_size, maxalign);
tcb = (Elf_Addr **)(tls_block + pre_size + extra_size);
if (oldtcb != NULL) {
memcpy(tls, oldtcb, tls_static_space);
free(oldtcb);
memcpy(tls_block, get_tls_block_ptr(oldtcb, tcbsize),
tls_static_space);
free_aligned(get_tls_block_ptr(oldtcb, tcbsize));
/* Adjust the DTV. */
dtv = tls[0];
dtv = tcb[0];
for (i = 0; i < dtv[1]; i++) {
if (dtv[i+2] >= (Elf_Addr)oldtcb &&
dtv[i+2] < (Elf_Addr)oldtcb + tls_static_space) {
dtv[i+2] = dtv[i+2] - (Elf_Addr)oldtcb + (Elf_Addr)tls;
dtv[i+2] = dtv[i+2] - (Elf_Addr)oldtcb + (Elf_Addr)tcb;
}
}
} else {
dtv = xcalloc(tls_max_index + 2, sizeof(Elf_Addr));
tls[0] = dtv;
tcb[0] = dtv;
dtv[0] = tls_dtv_generation;
dtv[1] = tls_max_index;
for (obj = globallist_curr(objs); obj != NULL;
obj = globallist_next(obj)) {
if (obj->tlsoffset > 0) {
addr = (Elf_Addr)tls + obj->tlsoffset;
addr = (Elf_Addr)tcb + obj->tlsoffset;
if (obj->tlsinitsize > 0)
memcpy((void*) addr, obj->tlsinit, obj->tlsinitsize);
if (obj->tlssize > obj->tlsinitsize)
@ -4752,14 +4792,19 @@ free_tls(void *tcb, size_t tcbsize, size_t tcbalign)
{
Elf_Addr *dtv;
Elf_Addr tlsstart, tlsend;
int dtvsize, i;
size_t post_size;
size_t dtvsize, i, tls_init_align;
assert(tcbsize >= TLS_TCB_SIZE);
tls_init_align = MAX(obj_main->tlsalign, 1);
tlsstart = (Elf_Addr)tcb + tcbsize - TLS_TCB_SIZE;
tlsend = tlsstart + tls_static_space;
/* Compute fragments sizes. */
post_size = calculate_tls_post_size(tls_init_align);
dtv = *(Elf_Addr **)tlsstart;
tlsstart = (Elf_Addr)tcb + TLS_TCB_SIZE + post_size;
tlsend = (Elf_Addr)tcb + tls_static_space;
dtv = *(Elf_Addr **)tcb;
dtvsize = dtv[1];
for (i = 0; i < dtvsize; i++) {
if (dtv[i+2] && (dtv[i+2] < tlsstart || dtv[i+2] >= tlsend)) {
@ -4767,7 +4812,7 @@ free_tls(void *tcb, size_t tcbsize, size_t tcbalign)
}
}
free(dtv);
free(tcb);
free_aligned(get_tls_block_ptr(tcb, tcbsize));
}
#endif