rtld: Fix i386/amd64 TP offset when p_vaddr % p_align != 0

For a Variant II architecture, the TP offset of a TLS symbol is st_value -
tlsoffset + r_addend. tlsoffset is computed by either calculate_tls_offset
or calculate_first_tls_offset.

The return value of calculate_first_tls_offset is the smallest integer
satisfying res >= size and (-res) % p_align = p_vaddr % p_align
(= p_offset % p_align).  (The formula is a bit contrived. The basic idea
is to subtract the minimum integer from size + align - 1 so that the result
ihas the expected remainder.)

Reviewed by:	kib
MFC after:	1 week
Differential revision:	https://reviews.freebsd.org/D31538
Differential revision:	https://reviews.freebsd.org/D31541
This commit is contained in:
Fangrui Song 2021-08-14 19:56:58 +03:00 committed by Konstantin Belousov
parent cc1345056b
commit e6c7696203
2 changed files with 24 additions and 30 deletions

View File

@ -548,29 +548,26 @@ __tls_get_addr(tls_index *ti)
}
size_t
calculate_first_tls_offset(size_t size, size_t align, size_t offset)
calculate_tls_offset(size_t prev_offset, size_t prev_size __unused,
size_t size, size_t align, size_t offset)
{
size_t res;
res = roundup(size, align);
offset &= align - 1;
if (offset != 0)
res += align - offset;
return (res);
/*
* res is the smallest integer satisfying res - prev_offset >= size
* and (-res) % p_align = p_vaddr % p_align (= p_offset % p_align).
*/
res = prev_offset + size + align - 1;
res -= (res + offset) & (align - 1);
return (res);
}
size_t
calculate_tls_offset(size_t prev_offset, size_t prev_size __unused, size_t size,
size_t align, size_t offset)
calculate_first_tls_offset(size_t size, size_t align, size_t offset)
{
size_t res;
res = roundup(prev_offset + size, align);
offset &= align - 1;
if (offset != 0)
res += align - offset;
return (res);
return (calculate_tls_offset(0, 0, size, align, offset));
}
size_t
calculate_tls_end(size_t off, size_t size __unused)
{

View File

@ -539,29 +539,26 @@ __tls_get_addr(tls_index *ti)
}
size_t
calculate_first_tls_offset(size_t size, size_t align, size_t offset)
calculate_tls_offset(size_t prev_offset, size_t prev_size __unused,
size_t size, size_t align, size_t offset)
{
size_t res;
res = roundup(size, align);
offset &= align - 1;
if (offset != 0)
res += align - offset;
return (res);
/*
* res is the smallest integer satisfying res - prev_offset >= size
* and (-res) % p_align = p_vaddr % p_align (= p_offset % p_align).
*/
res = prev_offset + size + align - 1;
res -= (res + offset) & (align - 1);
return (res);
}
size_t
calculate_tls_offset(size_t prev_offset, size_t prev_size __unused, size_t size,
size_t align, size_t offset)
calculate_first_tls_offset(size_t size, size_t align, size_t offset)
{
size_t res;
res = roundup(prev_offset + size, align);
offset &= align - 1;
if (offset != 0)
res += align - offset;
return (res);
return (calculate_tls_offset(0, 0, size, align, offset));
}
size_t
calculate_tls_end(size_t off, size_t size __unused)
{