1212#include " log.h"
1313#include " safeAccess.h"
1414#include " symbols.h"
15+ #include < dlfcn.h>
1516#include < elf.h>
1617#include < errno.h>
1718#include < fcntl.h>
1819#include < link.h>
1920#include < linux/limits.h>
20- #include < set>
2121#include < stdio.h>
2222#include < stdlib.h>
2323#include < string.h>
24+ #include < sys/auxv.h>
2425#include < sys/mman.h>
2526#include < sys/stat.h>
2627#include < sys/types.h>
28+ #include < unordered_map>
29+ #include < unordered_set>
2730
2831// make sure lseek will use 64 bits offset
2932#define _FILE_OFFSET_BITS 64
@@ -481,13 +484,19 @@ void ElfParser::addRelocationSymbols(ElfSection *reltab, const char *plt) {
481484 }
482485}
483486
487+ struct SharedLibrary {
488+ char * file;
489+ const char * map_start;
490+ const char * map_end;
491+ const char * image_base;
492+ };
493+
484494Mutex Symbols::_parse_lock;
485495bool Symbols::_have_kernel_symbols = false ;
486- static std::set< const void *> _parsed_libraries ;
487- static std::set <u64 > _parsed_inodes;
496+ bool Symbols::_libs_limit_reported = false ;
497+ static std::unordered_set <u64 > _parsed_inodes;
488498
489499void Symbols::clearParsingCaches () {
490- _parsed_libraries.clear ();
491500 _parsed_inodes.clear ();
492501}
493502
@@ -531,22 +540,20 @@ void Symbols::parseKernelSymbols(CodeCache *cc) {
531540 fclose (f);
532541}
533542
534- static int parseLibrariesCallback (struct dl_phdr_info *info, size_t size,
535- void *data) {
543+ static void collectSharedLibraries (std::unordered_map<u64 , SharedLibrary>& libs, int max_count) {
536544
537545 FILE *f = fopen (" /proc/self/maps" , " r" );
538546 if (f == NULL ) {
539- return 1 ;
547+ return ;
540548 }
541549
542- CodeCacheArray *array = (CodeCacheArray *)data;
543550 const char *image_base = NULL ;
544551 u64 last_inode = 0 ;
545552 char *str = NULL ;
546553 size_t str_size = 0 ;
547554 ssize_t len;
548555
549- while ((len = getline (&str, &str_size, f)) > 0 ) {
556+ while (max_count > 0 && (len = getline (&str, &str_size, f)) > 0 ) {
550557 str[len - 1 ] = 0 ;
551558 MemoryMapDesc map (str);
552559 if (!map.isReadable () || map.file () == NULL || map.file ()[0 ] == 0 ) {
@@ -557,63 +564,46 @@ static int parseLibrariesCallback(struct dl_phdr_info *info, size_t size,
557564 continue ;
558565 }
559566
560- const char *map_start = map.addr ();
561- unsigned long map_offs = map.offs ();
562-
563- if (map_offs == 0 ) {
564- image_base = map_start;
565- last_inode = u64 (map.dev ()) << 32 | map.inode ();
567+ u64 inode = u64 (map.dev ()) << 32 | map.inode ();
568+ if (_parsed_inodes.find (inode) != _parsed_inodes.end ()) {
569+ continue ; // shared object is already parsed
566570 }
567-
568- if (!map.isExecutable () || !_parsed_libraries.insert (map_start).second ) {
569- // Not an executable segment or it has been already parsed
570- continue ;
571+ if (inode == 0 && strcmp (map.file (), " [vdso]" ) != 0 ) {
572+ continue ; // all shared libraries have inode, except vDSO
571573 }
572574
573- int count = array->count ();
574- if (count >= MAX_NATIVE_LIBS) {
575- break ;
575+ const char * map_start = map.addr ();
576+ const char *map_end = map.end ();
577+ if (inode != last_inode && map.offs () == 0 ) {
578+ image_base = map_start;
579+ last_inode = inode;
576580 }
577581
578- const char *map_end = map.end ();
579- // Do not try to parse pseudofiles like anon_inode:name, /memfd:name
580- if (strchr (map.file (), ' :' ) == NULL ) {
581- CodeCache *cc = new CodeCache (map.file (), count, false , map_start, map_end);
582- TEST_LOG (" Procesing library: %s" , map.file ());
583- u64 inode = u64 (map.dev ()) << 32 | map.inode ();
584- if (inode != 0 ) {
585- // Do not parse the same executable twice, e.g. on Alpine Linux
586- if (_parsed_inodes.insert (inode).second ) {
587- if (inode == last_inode) {
588- // If last_inode is set, image_base is known to be valid and
589- // readable
590- ElfParser::parseFile (cc, image_base, map.file (), true );
591- // Parse program headers after the file to ensure debug symbols are
592- // parsed first
593- ElfParser::parseProgramHeaders (cc, image_base, map_end, MUSL);
594- } else if ((unsigned long )map_start > map_offs) {
595- // Unlikely case when image_base has not been found.
596- // Be careful: executable file is not always ELF, e.g. classes.jsa
597- ElfParser::parseFile (cc, map_start - map_offs, map.file (), true );
598- }
582+ if (map.isExecutable ()) {
583+ SharedLibrary& lib = libs[inode];
584+ if (lib.file == nullptr ) {
585+ lib.file = strdup (map.file ());
586+ lib.map_start = map_start;
587+ lib.map_end = map_end;
588+ lib.image_base = inode == last_inode ? image_base : NULL ;
589+ max_count--;
590+ } else {
591+ // The same library may have multiple executable segments mapped
592+ lib.map_end = map_end;
599593 }
600- } else if (strcmp (map.file (), " [vdso]" ) == 0 ) {
601- ElfParser::parseProgramHeaders (cc, map_start, map_end, true );
602- }
603-
604- cc->sort ();
605- array->add (cc);
606594 }
607595 }
608-
609596 free (str);
610597 fclose (f);
611- return 1 ; // stop at first iteration
612598}
613599
614600void Symbols::parseLibraries (CodeCacheArray *array, bool kernel_symbols) {
615601 MutexLocker ml (_parse_lock);
616602
603+ if (array->count () >= MAX_NATIVE_LIBS) {
604+ return ;
605+ }
606+
617607 if (kernel_symbols && !haveKernelSymbols ()) {
618608 CodeCache *cc = new CodeCache (" [kernel]" );
619609 parseKernelSymbols (cc);
@@ -625,12 +615,48 @@ void Symbols::parseLibraries(CodeCacheArray *array, bool kernel_symbols) {
625615 delete cc;
626616 }
627617 }
618+ std::unordered_map<u64 , SharedLibrary> libs;
619+ collectSharedLibraries (libs, MAX_NATIVE_LIBS - array->count ());
620+
621+ for (auto & it : libs) {
622+ u64 inode = it.first ;
623+ _parsed_inodes.insert (inode);
624+
625+ SharedLibrary& lib = it.second ;
626+ CodeCache* cc = new CodeCache (lib.file , array->count (), false , lib.map_start , lib.map_end , lib.image_base );
627+
628+ // Strip " (deleted)" suffix so that removed library can be reopened
629+ size_t len = strlen (lib.file );
630+ if (len > 10 && strcmp (lib.file + len - 10 , " (deleted)" ) == 0 ) {
631+ lib.file [len - 10 ] = 0 ;
632+ }
633+
634+ if (strcmp (lib.file , " [vdso]" ) == 0 ) {
635+ ElfParser::parseProgramHeaders (cc, lib.map_start , lib.map_end , true );
636+ } else if (lib.image_base == NULL ) {
637+ // Unlikely case when image base has not been found: not safe to access program headers.
638+ // Be careful: executable file is not always ELF, e.g. classes.jsa
639+ ElfParser::parseFile (cc, lib.map_start , lib.file , true );
640+ } else {
641+ // Parse debug symbols first
642+ ElfParser::parseFile (cc, lib.image_base , lib.file , true );
643+
644+ UnloadProtection handle (cc);
645+ if (handle.isValid ()) {
646+ ElfParser::parseProgramHeaders (cc, lib.image_base , lib.map_end , MUSL);
647+ }
648+ }
649+
650+ free (lib.file );
628651
629- // In glibc, dl_iterate_phdr() holds dl_load_write_lock, therefore preventing
630- // concurrent loading and unloading of shared libraries.
631- // Without it, we may access memory of a library that is being unloaded.
632- dl_iterate_phdr (parseLibrariesCallback, array);
633- TEST_LOG (" Parsed %d libraries" , array->count ());
652+ cc->sort ();
653+ array->add (cc);
654+ }
655+
656+ if (array->count () >= MAX_NATIVE_LIBS && !_libs_limit_reported) {
657+ Log::warn (" Number of parsed libraries reached the limit of %d" , MAX_NATIVE_LIBS);
658+ _libs_limit_reported = true ;
659+ }
634660}
635661
636662bool Symbols::isRootSymbol (const void * address) {
@@ -642,4 +668,64 @@ bool Symbols::isRootSymbol(const void* address) {
642668 return false ;
643669}
644670
671+ // Check that the base address of the shared object has not changed
672+ static bool verifyBaseAddress (const CodeCache* cc, void * lib_handle) {
673+ Dl_info dl_info;
674+ struct link_map * map;
675+
676+ if (dlinfo (lib_handle, RTLD_DI_LINKMAP, &map) != 0 || dladdr (map->l_ld , &dl_info) == 0 ) {
677+ return false ;
678+ }
679+
680+ return cc->imageBase () == (const char *)dl_info.dli_fbase ;
681+ }
682+
683+ static const void * getMainPhdr () {
684+ void * main_phdr = NULL ;
685+ dl_iterate_phdr ([](struct dl_phdr_info * info, size_t size, void * data) {
686+ *(const void **)data = info->dlpi_phdr ;
687+ return 1 ;
688+ }, &main_phdr);
689+ return main_phdr;
690+ }
691+
692+ static const void * _main_phdr = getMainPhdr();
693+ static const char * _ld_base = (const char *)getauxval(AT_BASE);
694+
695+ static bool isMainExecutable (const char * image_base, const void * map_end) {
696+ return _main_phdr != NULL && _main_phdr >= image_base && _main_phdr < map_end;
697+ }
698+
699+ static bool isLoader (const char * image_base) {
700+ return _ld_base == image_base;
701+ }
702+
703+ UnloadProtection::UnloadProtection (const CodeCache *cc) {
704+ if (MUSL || isMainExecutable (cc->imageBase (), cc->maxAddress ()) || isLoader (cc->imageBase ())) {
705+ _lib_handle = NULL ;
706+ _valid = true ;
707+ return ;
708+ }
709+
710+ // dlopen() can reopen previously loaded libraries even if the underlying file has been deleted
711+ const char * stripped_name = cc->name ();
712+ size_t name_len = strlen (stripped_name);
713+ if (name_len > 10 && strcmp (stripped_name + name_len - 10 , " (deleted)" ) == 0 ) {
714+ char * buf = (char *) alloca (name_len - 9 );
715+ *stpncpy (buf, stripped_name, name_len - 10 ) = 0 ;
716+ stripped_name = buf;
717+ }
718+
719+ // Protect library from unloading while parsing in-memory ELF program headers.
720+ // Also, dlopen() ensures the library is fully loaded.
721+ _lib_handle = dlopen (stripped_name, RTLD_LAZY | RTLD_NOLOAD);
722+ _valid = _lib_handle != NULL && verifyBaseAddress (cc, _lib_handle);
723+ }
724+
725+ UnloadProtection::~UnloadProtection () {
726+ if (_lib_handle != NULL ) {
727+ dlclose (_lib_handle);
728+ }
729+ }
730+
645731#endif // __linux__
0 commit comments