Add comments to vDSO hwcap loading process.

Loading of the vDSO pseudo-hwcap from the type 2 GNU note is
a rather arcane and poorly documented process. Given that I had
a chance to review this code today I thought I would add all
of the things I had to lookup to verify the validity of the
process.

With a single .note.GNU the vDSO can register up to 64 flags,
though in practice you are limited to 64 - _DL_FIRST_EXTRA
bits which on x86 is 12 bits.

The only use of this that I know of is in the Xen support
in Linux where they use the 1st bit to indicate "nosegneg".
I see "We use bit 1 to avoid bugs in some versions of glibc
when bit 0 is used; the choice is otherwise arbitrary.", but
no reference to a glibc bug anywhere. The code as-is should
support bit zero, so we still have that free for future use.

The kernel, glibc, and ld.so.cache must coordinate to ensure
that bit values don't go too high and are used consistently.

---

2013-05-13  Carlos O'Donell  <carlos@redhat.com>

	* elf/dl-hwcaps.c (_dl_important_hwcaps): Comment vDSO hwcap loading.
	* elf/ldconfig.c (is_hwcap_platform): Comment each hwcap check.
	(main): Comment "tls" pseudo-hwcap.
This commit is contained in:
Carlos O'Donell 2013-05-14 00:06:35 -04:00
parent 5d5ef5dbfc
commit 141af660d8
3 changed files with 25 additions and 1 deletions

View File

@ -1,3 +1,9 @@
2013-05-13 Carlos O'Donell <carlos@redhat.com>
* elf/dl-hwcaps.c (_dl_important_hwcaps): Comment vDSO hwcap loading.
* elf/ldconfig.c (is_hwcap_platform): Comment each hwcap check.
(main): Comment "tls" pseudo-hwcap.
2013-05-13 Joseph Myers <joseph@codesourcery.com> 2013-05-13 Joseph Myers <joseph@codesourcery.com>
* math/libm-test.inc (struct test_fl_f_data): New type. * math/libm-test.inc (struct test_fl_f_data): New type.

View File

@ -66,6 +66,11 @@ _dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz,
{ {
const ElfW(Addr) start = (phdr[i].p_vaddr const ElfW(Addr) start = (phdr[i].p_vaddr
+ GLRO(dl_sysinfo_map)->l_addr); + GLRO(dl_sysinfo_map)->l_addr);
/* The standard ELF note layout is exactly as the anonymous struct.
The next element is a variable length vendor name of length
VENDORLEN (with a real length rounded to ElfW(Addr)), followed
by the data of length DATALEN (with a real length rounded to
ElfW(Addr)). */
const struct const struct
{ {
ElfW(Word) vendorlen; ElfW(Word) vendorlen;
@ -75,6 +80,11 @@ _dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz,
while ((ElfW(Addr)) (note + 1) - start < phdr[i].p_memsz) while ((ElfW(Addr)) (note + 1) - start < phdr[i].p_memsz)
{ {
#define ROUND(len) (((len) + sizeof (ElfW(Word)) - 1) & -sizeof (ElfW(Word))) #define ROUND(len) (((len) + sizeof (ElfW(Word)) - 1) & -sizeof (ElfW(Word)))
/* The layout of the type 2, vendor "GNU" note is as follows:
.long <Number of capabilities enabled by this note>
.long <Capabilities mask> (as mask >> _DL_FIRST_EXTRA).
.byte <The bit number for the next capability>
.asciz <The name of the capability>. */
if (note->type == NT_GNU_HWCAP if (note->type == NT_GNU_HWCAP
&& note->vendorlen == sizeof "GNU" && note->vendorlen == sizeof "GNU"
&& !memcmp ((note + 1), "GNU", sizeof "GNU") && !memcmp ((note + 1), "GNU", sizeof "GNU")
@ -84,7 +94,7 @@ _dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz,
+ ROUND (sizeof "GNU")); + ROUND (sizeof "GNU"));
cnt += *p++; cnt += *p++;
++p; /* Skip mask word. */ ++p; /* Skip mask word. */
dsocaps = (const char *) p; dsocaps = (const char *) p; /* Pseudo-string "<b>name" */
dsocapslen = note->datalen - sizeof *p * 2; dsocapslen = note->datalen - sizeof *p * 2;
break; break;
} }
@ -107,6 +117,8 @@ _dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz,
#ifdef NEED_DL_SYSINFO_DSO #ifdef NEED_DL_SYSINFO_DSO
if (dsocaps != NULL) if (dsocaps != NULL)
{ {
/* dsocaps points to the .asciz string, and -1 points to the mask
.long just before the string. */
const ElfW(Word) mask = ((const ElfW(Word) *) dsocaps)[-1]; const ElfW(Word) mask = ((const ElfW(Word) *) dsocaps)[-1];
GLRO(dl_hwcap) |= (uint64_t) mask << _DL_FIRST_EXTRA; GLRO(dl_hwcap) |= (uint64_t) mask << _DL_FIRST_EXTRA;
/* Note that we add the dsocaps to the set already chosen by the /* Note that we add the dsocaps to the set already chosen by the

View File

@ -173,13 +173,17 @@ is_hwcap_platform (const char *name)
{ {
int hwcap_idx = _dl_string_hwcap (name); int hwcap_idx = _dl_string_hwcap (name);
/* Is this a normal hwcap for the machine e.g. fpu? */
if (hwcap_idx != -1 && ((1 << hwcap_idx) & hwcap_mask)) if (hwcap_idx != -1 && ((1 << hwcap_idx) & hwcap_mask))
return 1; return 1;
/* ... Or is it a platform pseudo-hwcap e.g. i686? */
hwcap_idx = _dl_string_platform (name); hwcap_idx = _dl_string_platform (name);
if (hwcap_idx != -1) if (hwcap_idx != -1)
return 1; return 1;
/* ... Or is this one of the extra pseudo-hwcaps that we map beyond
_DL_FIRST_EXTRA e.g. tls, or nosegneg? */
for (hwcap_idx = _DL_FIRST_EXTRA; hwcap_idx < 64; ++hwcap_idx) for (hwcap_idx = _DL_FIRST_EXTRA; hwcap_idx < 64; ++hwcap_idx)
if (hwcap_extra[hwcap_idx - _DL_FIRST_EXTRA] != NULL if (hwcap_extra[hwcap_idx - _DL_FIRST_EXTRA] != NULL
&& !strcmp (name, hwcap_extra[hwcap_idx - _DL_FIRST_EXTRA])) && !strcmp (name, hwcap_extra[hwcap_idx - _DL_FIRST_EXTRA]))
@ -1265,6 +1269,8 @@ main (int argc, char **argv)
add_dir (argv[i]); add_dir (argv[i]);
} }
/* The last entry in hwcap_extra is reserved for the "tls"
pseudo-hwcap which indicates support for TLS. */
hwcap_extra[63 - _DL_FIRST_EXTRA] = "tls"; hwcap_extra[63 - _DL_FIRST_EXTRA] = "tls";
set_hwcap (); set_hwcap ();