mirror of
https://sourceware.org/git/glibc.git
synced 2025-10-27 12:15:39 +03:00
elf: Handle ld.so with LOAD segment gaps in _dl_find_object (bug 31943)
Detect if ld.so not contiguous and handle that case in _dl_find_object. Set l_find_object_processed even for initially loaded link maps, otherwise dlopen of an initially loaded object adds it to _dlfo_loaded_mappings (where maps are expected to be contiguous), in addition to _dlfo_nodelete_mappings. Test elf/tst-link-map-contiguous-ldso iterates over the loader image, reading every word to make sure memory is actually mapped. It only does that if the l_contiguous flag is set for the link map. Otherwise, it finds gaps with mmap and checks that _dl_find_object does not return the ld.so mapping for them. The test elf/tst-link-map-contiguous-main does the same thing for the libc.so shared object. This only works if the kernel loaded the main program because the glibc dynamic loader may fill the gaps with PROT_NONE mappings in some cases, making it contiguous, but accesses to individual words may still fault. Test elf/tst-link-map-contiguous-libc is again slightly different because the dynamic loader always fills the gaps with PROT_NONE mappings, so a different form of probing has to be used. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
This commit is contained in:
@@ -465,6 +465,37 @@ _dl_find_object (void *pc1, struct dl_find_object *result)
|
||||
}
|
||||
rtld_hidden_def (_dl_find_object)
|
||||
|
||||
/* Subroutine of _dlfo_process_initial to split out noncontigous link
|
||||
maps. NODELETE is the number of used _dlfo_nodelete_mappings
|
||||
elements. It is incremented as needed, and the new NODELETE value
|
||||
is returned. */
|
||||
static size_t
|
||||
_dlfo_process_initial_noncontiguous_map (struct link_map *map,
|
||||
size_t nodelete)
|
||||
{
|
||||
struct dl_find_object_internal dlfo;
|
||||
_dl_find_object_from_map (map, &dlfo);
|
||||
|
||||
/* PT_LOAD segments for a non-contiguous link map are added to the
|
||||
non-closeable mappings. */
|
||||
const ElfW(Phdr) *ph = map->l_phdr;
|
||||
const ElfW(Phdr) *ph_end = map->l_phdr + map->l_phnum;
|
||||
for (; ph < ph_end; ++ph)
|
||||
if (ph->p_type == PT_LOAD)
|
||||
{
|
||||
if (_dlfo_nodelete_mappings != NULL)
|
||||
{
|
||||
/* Second pass only. */
|
||||
_dlfo_nodelete_mappings[nodelete] = dlfo;
|
||||
ElfW(Addr) start = ph->p_vaddr + map->l_addr;
|
||||
_dlfo_nodelete_mappings[nodelete].map_start = start;
|
||||
_dlfo_nodelete_mappings[nodelete].map_end = start + ph->p_memsz;
|
||||
}
|
||||
++nodelete;
|
||||
}
|
||||
return nodelete;
|
||||
}
|
||||
|
||||
/* _dlfo_process_initial is called twice. First to compute the array
|
||||
sizes from the initial loaded mappings. Second to fill in the
|
||||
bases and infos arrays with the (still unsorted) data. Returns the
|
||||
@@ -476,29 +507,8 @@ _dlfo_process_initial (void)
|
||||
|
||||
size_t nodelete = 0;
|
||||
if (!main_map->l_contiguous)
|
||||
{
|
||||
struct dl_find_object_internal dlfo;
|
||||
_dl_find_object_from_map (main_map, &dlfo);
|
||||
|
||||
/* PT_LOAD segments for a non-contiguous are added to the
|
||||
non-closeable mappings. */
|
||||
for (const ElfW(Phdr) *ph = main_map->l_phdr,
|
||||
*ph_end = main_map->l_phdr + main_map->l_phnum;
|
||||
ph < ph_end; ++ph)
|
||||
if (ph->p_type == PT_LOAD)
|
||||
{
|
||||
if (_dlfo_nodelete_mappings != NULL)
|
||||
{
|
||||
/* Second pass only. */
|
||||
_dlfo_nodelete_mappings[nodelete] = dlfo;
|
||||
_dlfo_nodelete_mappings[nodelete].map_start
|
||||
= ph->p_vaddr + main_map->l_addr;
|
||||
_dlfo_nodelete_mappings[nodelete].map_end
|
||||
= _dlfo_nodelete_mappings[nodelete].map_start + ph->p_memsz;
|
||||
}
|
||||
++nodelete;
|
||||
}
|
||||
}
|
||||
/* Contiguous case already handled in _dl_find_object_init. */
|
||||
nodelete = _dlfo_process_initial_noncontiguous_map (main_map, nodelete);
|
||||
|
||||
size_t loaded = 0;
|
||||
for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns)
|
||||
@@ -510,11 +520,18 @@ _dlfo_process_initial (void)
|
||||
/* lt_library link maps are implicitly NODELETE. */
|
||||
if (l->l_type == lt_library || l->l_nodelete_active)
|
||||
{
|
||||
if (_dlfo_nodelete_mappings != NULL)
|
||||
/* Second pass only. */
|
||||
_dl_find_object_from_map
|
||||
(l, _dlfo_nodelete_mappings + nodelete);
|
||||
++nodelete;
|
||||
/* The kernel may have loaded ld.so with gaps. */
|
||||
if (!l->l_contiguous && is_rtld_link_map (l))
|
||||
nodelete
|
||||
= _dlfo_process_initial_noncontiguous_map (l, nodelete);
|
||||
else
|
||||
{
|
||||
if (_dlfo_nodelete_mappings != NULL)
|
||||
/* Second pass only. */
|
||||
_dl_find_object_from_map
|
||||
(l, _dlfo_nodelete_mappings + nodelete);
|
||||
++nodelete;
|
||||
}
|
||||
}
|
||||
else if (l->l_type == lt_loaded)
|
||||
{
|
||||
@@ -764,7 +781,6 @@ _dl_find_object_update_1 (struct link_map **loaded, size_t count)
|
||||
/* Prefer newly loaded link map. */
|
||||
assert (loaded_index1 > 0);
|
||||
_dl_find_object_from_map (loaded[loaded_index1 - 1], dlfo);
|
||||
loaded[loaded_index1 - 1]->l_find_object_processed = 1;
|
||||
--loaded_index1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user