LCOV - code coverage report
Current view: top level - libdwfl - linux-proc-maps.c (source / functions) Coverage Total Hit
Test: elfutils-0.193 Lines: 78.6 % 187 147
Test Date: 2025-08-30 14:31:09 Functions: 87.5 % 8 7
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 52.7 % 150 79

             Branch data     Line data    Source code
       1                 :             : /* Standard libdwfl callbacks for debugging a live Linux process.
       2                 :             :    Copyright (C) 2005-2010, 2013, 2014, 2016 Red Hat, Inc.
       3                 :             :    This file is part of elfutils.
       4                 :             : 
       5                 :             :    This file is free software; you can redistribute it and/or modify
       6                 :             :    it under the terms of either
       7                 :             : 
       8                 :             :      * the GNU Lesser General Public License as published by the Free
       9                 :             :        Software Foundation; either version 3 of the License, or (at
      10                 :             :        your option) any later version
      11                 :             : 
      12                 :             :    or
      13                 :             : 
      14                 :             :      * the GNU General Public License as published by the Free
      15                 :             :        Software Foundation; either version 2 of the License, or (at
      16                 :             :        your option) any later version
      17                 :             : 
      18                 :             :    or both in parallel, as here.
      19                 :             : 
      20                 :             :    elfutils is distributed in the hope that it will be useful, but
      21                 :             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      22                 :             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      23                 :             :    General Public License for more details.
      24                 :             : 
      25                 :             :    You should have received copies of the GNU General Public License and
      26                 :             :    the GNU Lesser General Public License along with this program.  If
      27                 :             :    not, see <http://www.gnu.org/licenses/>.  */
      28                 :             : 
      29                 :             : #ifdef HAVE_CONFIG_H
      30                 :             : # include <config.h>
      31                 :             : #endif
      32                 :             : 
      33                 :             : #include "libdwflP.h"
      34                 :             : #include <inttypes.h>
      35                 :             : #include <sys/types.h>
      36                 :             : #include <sys/stat.h>
      37                 :             : #include <errno.h>
      38                 :             : #include <stdio.h>
      39                 :             : #include <stdio_ext.h>
      40                 :             : #include <stdbool.h>
      41                 :             : #include <string.h>
      42                 :             : #include <stdlib.h>
      43                 :             : #include <fcntl.h>
      44                 :             : #include <unistd.h>
      45                 :             : #include <assert.h>
      46                 :             : #include <endian.h>
      47                 :             : #include "system.h"
      48                 :             : 
      49                 :             : 
      50                 :             : #define PROCMAPSFMT     "/proc/%d/maps"
      51                 :             : #define PROCMEMFMT      "/proc/%d/mem"
      52                 :             : #define PROCAUXVFMT     "/proc/%d/auxv"
      53                 :             : #define PROCEXEFMT      "/proc/%d/exe"
      54                 :             : 
      55                 :             : 
      56                 :             : /* Return ELFCLASS64 or ELFCLASS32 for the main ELF executable.  Return
      57                 :             :    ELFCLASSNONE for an error.  */
      58                 :             : 
      59                 :             : static unsigned char
      60                 :           0 : get_pid_class (pid_t pid)
      61                 :             : {
      62                 :           0 :   char *fname;
      63         [ #  # ]:           0 :   if (asprintf (&fname, PROCEXEFMT, pid) < 0)
      64                 :             :     return ELFCLASSNONE;
      65                 :             : 
      66                 :           0 :   int fd = open (fname, O_RDONLY);
      67                 :           0 :   free (fname);
      68         [ #  # ]:           0 :   if (fd < 0)
      69                 :             :     return ELFCLASSNONE;
      70                 :             : 
      71                 :           0 :   unsigned char buf[EI_CLASS + 1];
      72                 :           0 :   ssize_t nread = pread_retry (fd, &buf, sizeof buf, 0);
      73                 :           0 :   close (fd);
      74   [ #  #  #  # ]:           0 :   if (nread != sizeof buf || buf[EI_MAG0] != ELFMAG0
      75   [ #  #  #  # ]:           0 :       || buf[EI_MAG1] != ELFMAG1 || buf[EI_MAG2] != ELFMAG2
      76         [ #  # ]:           0 :       || buf[EI_MAG3] != ELFMAG3
      77         [ #  # ]:           0 :       || (buf[EI_CLASS] != ELFCLASS64 && buf[EI_CLASS] != ELFCLASS32))
      78                 :             :     return ELFCLASSNONE;
      79                 :             : 
      80                 :             :   return buf[EI_CLASS];
      81                 :             : }
      82                 :             : 
      83                 :             : /* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag.
      84                 :             : 
      85                 :             :    It would be easiest to call get_pid_class and parse everything according to
      86                 :             :    the 32-bit or 64-bit class.  But this would bring the overhead of syscalls
      87                 :             :    to open and read the "/proc/%d/exe" file.
      88                 :             : 
      89                 :             :    Therefore this function tries to parse the "/proc/%d/auxv" content both
      90                 :             :    ways, as if it were the 32-bit format and also if it were the 64-bit format.
      91                 :             :    Only if it gives some valid data in both cases get_pid_class gets called.
      92                 :             :    In most cases only one of the format bit sizes gives valid data and the
      93                 :             :    get_pid_class call overhead can be saved.  */
      94                 :             : 
      95                 :             : static int
      96                 :       10026 : grovel_auxv (pid_t pid, Dwfl *dwfl, GElf_Addr *sysinfo_ehdr)
      97                 :             : {
      98                 :       10026 :   char *fname;
      99         [ +  - ]:       10026 :   if (asprintf (&fname, PROCAUXVFMT, pid) < 0)
     100                 :             :     return ENOMEM;
     101                 :             : 
     102                 :       10026 :   int fd = open (fname, O_RDONLY);
     103                 :       10026 :   free (fname);
     104         [ -  + ]:       10026 :   if (fd < 0)
     105         [ #  # ]:           0 :     return errno == ENOENT ? 0 : errno;
     106                 :             : 
     107                 :       10026 :   GElf_Addr sysinfo_ehdr64 = 0;
     108                 :       10026 :   GElf_Addr sysinfo_ehdr32 = 0;
     109                 :       10026 :   GElf_Addr segment_align64 = dwfl->segment_align;
     110                 :       10026 :   GElf_Addr segment_align32 = dwfl->segment_align;
     111                 :       10026 :   off_t offset = 0;
     112                 :       10026 :   ssize_t nread;
     113                 :       10026 :   union
     114                 :             :   {
     115                 :             :     Elf64_auxv_t a64[64];
     116                 :             :     Elf32_auxv_t a32[128];
     117                 :             :   } d;
     118                 :       10026 :   do
     119                 :             :     {
     120                 :       10026 :       eu_static_assert (sizeof d.a64 == sizeof d.a32);
     121                 :       10026 :       nread = pread_retry (fd, d.a64, sizeof d.a64, offset);
     122         [ -  + ]:       10026 :       if (nread < 0)
     123                 :             :         {
     124                 :           0 :           int ret = errno;
     125                 :           0 :           close (fd);
     126                 :           0 :           return ret;
     127                 :             :         }
     128         [ +  + ]:      431118 :       for (size_t a32i = 0; a32i < nread / sizeof d.a32[0]; a32i++)
     129                 :             :         {
     130                 :      421092 :           const Elf32_auxv_t *a32 = d.a32 + a32i;
     131      [ +  +  + ]:      421092 :           switch (a32->a_type)
     132                 :             :           {
     133                 :       10026 :             case AT_SYSINFO_EHDR:
     134                 :       10026 :               sysinfo_ehdr32 = a32->a_un.a_val;
     135                 :       10026 :               break;
     136                 :       10026 :             case AT_PAGESZ:
     137                 :       10026 :               segment_align32 = a32->a_un.a_val;
     138                 :       10026 :               break;
     139                 :             :           }
     140                 :             :         }
     141         [ +  + ]:      220572 :       for (size_t a64i = 0; a64i < nread / sizeof d.a64[0]; a64i++)
     142                 :             :         {
     143                 :      210546 :           const Elf64_auxv_t *a64 = d.a64 + a64i;
     144      [ +  +  + ]:      210546 :           switch (a64->a_type)
     145                 :             :           {
     146                 :       10026 :             case AT_SYSINFO_EHDR:
     147                 :       10026 :               sysinfo_ehdr64 = a64->a_un.a_val;
     148                 :       10026 :               break;
     149                 :       10026 :             case AT_PAGESZ:
     150                 :       10026 :               segment_align64 = a64->a_un.a_val;
     151                 :       10026 :               break;
     152                 :             :           }
     153                 :             :         }
     154                 :       10026 :       offset += nread;
     155                 :             :     }
     156         [ -  + ]:       10026 :   while (nread == sizeof d.a64);
     157                 :             : 
     158                 :       10026 :   close (fd);
     159                 :             : 
     160   [ -  +  -  - ]:       10026 :   bool valid64 = sysinfo_ehdr64 != 0 || segment_align64 != dwfl->segment_align;
     161   [ +  -  -  + ]:       10026 :   bool valid32 = sysinfo_ehdr32 != 0 || segment_align32 != dwfl->segment_align;
     162                 :             : 
     163                 :           0 :   unsigned char pid_class = ELFCLASSNONE;
     164         [ #  # ]:           0 :   if (valid64 && valid32)
     165                 :           0 :     pid_class = get_pid_class (pid);
     166                 :             : 
     167   [ +  -  +  - ]:       10026 :   if (pid_class == ELFCLASS64 || (valid64 && ! valid32))
     168                 :             :     {
     169                 :       10026 :       *sysinfo_ehdr = sysinfo_ehdr64;
     170                 :       10026 :       dwfl->segment_align = segment_align64;
     171                 :       10026 :       return 0;
     172                 :             :     }
     173   [ #  #  #  # ]:           0 :   if (pid_class == ELFCLASS32 || (! valid64 && valid32))
     174                 :             :     {
     175                 :           0 :       *sysinfo_ehdr = sysinfo_ehdr32;
     176                 :           0 :       dwfl->segment_align = segment_align32;
     177                 :           0 :       return 0;
     178                 :             :     }
     179                 :             :   return ENOEXEC;
     180                 :             : }
     181                 :             : 
     182                 :             : static inline bool
     183                 :      100226 : do_report (Dwfl *dwfl, char **plast_file, Dwarf_Addr low, Dwarf_Addr high)
     184                 :             : {
     185         [ +  + ]:      100226 :   if (*plast_file != NULL)
     186                 :             :     {
     187                 :       80162 :       Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, *plast_file,
     188                 :             :                                                      low, high);
     189                 :       80162 :       free (*plast_file);
     190                 :       80162 :       *plast_file = NULL;
     191         [ +  - ]:       80162 :       if (unlikely (mod == NULL))
     192                 :             :         return true;
     193                 :             :     }
     194                 :             :   return false;
     195                 :             : }
     196                 :             : 
     197                 :             : #define report() do_report(dwfl, &last_file, low, high)
     198                 :             : 
     199                 :             : static int
     200                 :       10038 : proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr sysinfo_ehdr, pid_t pid)
     201                 :             : {
     202                 :       10038 :   unsigned int last_dmajor = -1, last_dminor = -1;
     203                 :       10038 :   uint64_t last_ino = -1;
     204                 :       10038 :   char *last_file = NULL;
     205                 :       10038 :   Dwarf_Addr low = 0, high = 0;
     206                 :             : 
     207                 :       10038 :   char *line = NULL;
     208                 :       10038 :   size_t linesz;
     209                 :       10038 :   ssize_t len;
     210         [ +  + ]:      460924 :   while ((len = getline (&line, &linesz, f)) > 0)
     211                 :             :     {
     212         [ +  - ]:      450886 :       if (line[len - 1] == '\n')
     213                 :      450886 :         line[len - 1] = '\0';
     214                 :             : 
     215                 :      450886 :       Dwarf_Addr start, end, offset;
     216                 :      450886 :       unsigned int dmajor, dminor;
     217                 :      450886 :       uint64_t ino;
     218                 :      450886 :       int nread = -1;
     219         [ +  - ]:      450886 :       if (sscanf (line, "%" PRIx64 "-%" PRIx64 " %*s %" PRIx64
     220                 :             :                   " %x:%x %" PRIu64 " %n",
     221                 :             :                   &start, &end, &offset, &dmajor, &dminor, &ino, &nread) < 6
     222         [ -  + ]:      450886 :           || nread <= 0)
     223                 :             :         {
     224                 :           0 :           free (line);
     225                 :           0 :           free (last_file);
     226                 :           0 :           return ENOEXEC;
     227                 :             :         }
     228                 :             : 
     229                 :             :       /* If this is the special mapping AT_SYSINFO_EHDR pointed us at,
     230                 :             :          report the last one and then this special one.  */
     231   [ +  +  +  - ]:      450886 :       if (start == sysinfo_ehdr && start != 0)
     232                 :             :         {
     233         [ -  + ]:       10026 :           if (report ())
     234                 :             :             {
     235                 :           0 :             bad_report:
     236                 :           0 :               free (line);
     237                 :           0 :               return -1;
     238                 :             :             }
     239                 :             : 
     240                 :       10026 :           low = start;
     241                 :       10026 :           high = end;
     242         [ +  - ]:       10026 :           if (asprintf (&last_file, "[vdso: %d]", (int) pid) < 0
     243         [ -  + ]:       10026 :               || report ())
     244                 :           0 :             goto bad_report;
     245                 :             :         }
     246                 :             : 
     247                 :      450886 :       char *file = line + nread + strspn (line + nread, " \t");
     248   [ +  +  -  +  :      450886 :       if (file[0] != '/' || (ino == 0 && dmajor == 0 && dminor == 0))
             -  -  -  - ]
     249                 :             :         /* This line doesn't indicate a file mapping.  */
     250                 :      100286 :         continue;
     251                 :             : 
     252         [ +  + ]:      350600 :       if (last_file != NULL
     253   [ +  +  +  -  :      340562 :           && ino == last_ino && dmajor == last_dmajor && dminor == last_dminor)
                   +  - ]
     254                 :             :         {
     255                 :             :           /* This is another portion of the same file's mapping.  */
     256         [ -  + ]:      280464 :           if (strcmp (last_file, file) != 0)
     257                 :             :             {
     258                 :           0 :               free (last_file);
     259                 :           0 :               goto bad_report;
     260                 :             :             }
     261                 :      280464 :           high = end;
     262                 :             :         }
     263                 :             :       else
     264                 :             :         {
     265                 :             :           /* This is a different file mapping.  Report the last one.  */
     266         [ -  + ]:       70136 :           if (report ())
     267                 :           0 :             goto bad_report;
     268                 :       70136 :           low = start;
     269                 :       70136 :           high = end;
     270                 :       70136 :           last_file = strdup (file);
     271                 :       70136 :           last_ino = ino;
     272                 :       70136 :           last_dmajor = dmajor;
     273                 :       70136 :           last_dminor = dminor;
     274                 :             :         }
     275                 :             :     }
     276                 :       10038 :   free (line);
     277                 :             : 
     278   [ -  +  -  + ]:       10038 :   int result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
     279                 :             : 
     280                 :             :   /* Report the final one.  */
     281                 :       10038 :   bool lose = report ();
     282                 :             : 
     283   [ +  -  +  - ]:       10038 :   return result != 0 ? result : lose ? -1 : 0;
     284                 :             : }
     285                 :             : 
     286                 :             : int
     287                 :          12 : dwfl_linux_proc_maps_report (Dwfl *dwfl, FILE *f)
     288                 :             : {
     289                 :          12 :   return proc_maps_report (dwfl, f, 0, 0);
     290                 :             : }
     291                 :             : INTDEF (dwfl_linux_proc_maps_report)
     292                 :             : 
     293                 :             : int
     294                 :       10026 : dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid)
     295                 :             : {
     296         [ +  - ]:       10026 :   if (dwfl == NULL)
     297                 :             :     return -1;
     298                 :             : 
     299                 :             :   /* We'll notice the AT_SYSINFO_EHDR address specially when we hit it.  */
     300                 :       10026 :   GElf_Addr sysinfo_ehdr = 0;
     301                 :       10026 :   int result = grovel_auxv (pid, dwfl, &sysinfo_ehdr);
     302         [ +  - ]:       10026 :   if (result != 0)
     303                 :             :     return result;
     304                 :             : 
     305                 :       10026 :   char *fname;
     306         [ +  - ]:       10026 :   if (asprintf (&fname, PROCMAPSFMT, pid) < 0)
     307                 :             :     return ENOMEM;
     308                 :             : 
     309                 :       10026 :   FILE *f = fopen (fname, "r");
     310                 :       10026 :   free (fname);
     311         [ -  + ]:       10026 :   if (f == NULL)
     312                 :           0 :     return errno;
     313                 :             : 
     314                 :       10026 :   (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
     315                 :             : 
     316                 :       10026 :   result = proc_maps_report (dwfl, f, sysinfo_ehdr, pid);
     317                 :             : 
     318                 :       10026 :   fclose (f);
     319                 :             : 
     320                 :       10026 :   return result;
     321                 :             : }
     322                 :             : INTDEF (dwfl_linux_proc_report)
     323                 :             : 
     324                 :             : static ssize_t
     325                 :          54 : read_proc_memory (void *arg, void *data, GElf_Addr address,
     326                 :             :                   size_t minread, size_t maxread)
     327                 :             : {
     328                 :          54 :   const int fd = *(const int *) arg;
     329                 :             : 
     330                 :             :   /* This code relies on the fact the Linux kernel accepts negative
     331                 :             :      offsets when seeking /dev/$$/mem files, as a special case. In
     332                 :             :      particular pread cannot be used here, because it will always
     333                 :             :      return EINVAL when passed a negative offset.  */
     334                 :             : 
     335         [ +  - ]:          54 :   if (lseek (fd, (off_t) address, SEEK_SET) == -1)
     336                 :             :     return -1;
     337                 :             : 
     338         [ +  - ]:          54 :   ssize_t nread = read (fd, data, maxread);
     339                 :             : 
     340         [ -  + ]:          54 :   if (nread > 0 && (size_t) nread < minread)
     341                 :           0 :     nread = 0;
     342                 :             :   return nread;
     343                 :             : }
     344                 :             : 
     345                 :             : extern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma,
     346                 :             :                                     GElf_Xword pagesize,
     347                 :             :                                     GElf_Addr *loadbasep,
     348                 :             :                                     ssize_t (*read_memory) (void *arg,
     349                 :             :                                                             void *data,
     350                 :             :                                                             GElf_Addr address,
     351                 :             :                                                             size_t minread,
     352                 :             :                                                             size_t maxread),
     353                 :             :                                     void *arg);
     354                 :             : 
     355                 :             : 
     356                 :             : /* Dwfl_Callbacks.find_elf */
     357                 :             : 
     358                 :             : int
     359                 :       10110 : dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
     360                 :             :                           void **userdata __attribute__ ((unused)),
     361                 :             :                           const char *module_name, Dwarf_Addr base,
     362                 :             :                           char **file_name, Elf **elfp)
     363                 :             : {
     364                 :       10110 :   int pid = -1;
     365         [ +  + ]:       10110 :   if (module_name[0] == '/')
     366                 :             :     {
     367                 :             :       /* When this callback is used together with dwfl_linux_proc_report
     368                 :             :          then we might see mappings of special character devices.  Make
     369                 :             :          sure we only open and return regular files.  Special devices
     370                 :             :          might hang on open or read.  (deleted) files are super special.
     371                 :             :          The image might come from memory if we are attached.  */
     372                 :       10096 :       struct stat sb;
     373   [ +  +  -  + ]:       10096 :       if (stat (module_name, &sb) == -1 || (sb.st_mode & S_IFMT) != S_IFREG)
     374                 :             :         {
     375   [ +  +  +  + ]:          10 :           if (strcmp (strrchr (module_name, ' ') ?: "", " (deleted)") == 0)
     376                 :           2 :             pid = INTUSE(dwfl_pid) (mod->dwfl);
     377                 :             :           else
     378                 :       10094 :             return -1;
     379                 :             :         }
     380                 :             : 
     381         [ +  + ]:       10092 :       if (pid == -1)
     382                 :             :         {
     383                 :       10090 :           int fd = open (module_name, O_RDONLY);
     384         [ +  - ]:       10090 :           if (fd >= 0)
     385                 :             :             {
     386                 :       10090 :               *file_name = strdup (module_name);
     387         [ -  + ]:       10090 :               if (*file_name == NULL)
     388                 :             :                 {
     389                 :           0 :                   close (fd);
     390                 :           0 :                   return ENOMEM;
     391                 :             :                 }
     392                 :             :             }
     393                 :       10090 :           return fd;
     394                 :             :         }
     395                 :             :     }
     396                 :             : 
     397   [ +  +  +  - ]:          16 :   if (pid != -1 || sscanf (module_name, "[vdso: %d]", &pid) == 1)
     398                 :             :     {
     399                 :             :       /* Special case for in-memory ELF image.  */
     400                 :             : 
     401                 :          16 :       bool detach = false;
     402                 :          16 :       bool tid_was_stopped = false;
     403                 :          16 :       struct __libdwfl_pid_arg *pid_arg = __libdwfl_get_pid_arg (mod->dwfl);
     404   [ -  +  -  + ]:          16 :       if (pid_arg != NULL && ! pid_arg->assume_ptrace_stopped)
     405                 :             :         {
     406                 :             :           /* If any thread is already attached we are fine.  Read
     407                 :             :              through that thread.  It doesn't have to be the main
     408                 :             :              thread pid.  */
     409                 :          16 :           pid_t tid = pid_arg->tid_attached;
     410         [ +  + ]:          16 :           if (tid != 0)
     411                 :           2 :             pid = tid;
     412                 :             :           else
     413                 :          14 :             detach = __libdwfl_ptrace_attach (pid, &tid_was_stopped);
     414                 :             :         }
     415                 :             : 
     416                 :          16 :       char *fname;
     417         [ -  + ]:          16 :       if (asprintf (&fname, PROCMEMFMT, pid) < 0)
     418                 :           0 :         goto detach;
     419                 :             : 
     420                 :          16 :       int fd = open (fname, O_RDONLY);
     421                 :          16 :       free (fname);
     422         [ -  + ]:          16 :       if (fd < 0)
     423                 :           0 :         goto detach;
     424                 :             : 
     425                 :          16 :       *elfp = elf_from_remote_memory (base, sysconf (_SC_PAGESIZE), NULL,
     426                 :             :                                       &read_proc_memory, &fd);
     427                 :             : 
     428                 :          16 :       close (fd);
     429                 :             : 
     430                 :          16 :       *file_name = NULL;
     431                 :             : 
     432                 :          16 :     detach:
     433         [ +  + ]:          16 :       if (detach)
     434                 :          12 :         __libdwfl_ptrace_detach (pid, tid_was_stopped);
     435                 :          16 :       return -1;
     436                 :             :     }
     437                 :             : 
     438                 :             :   return -1;
     439                 :             : }
     440                 :             : INTDEF (dwfl_linux_proc_find_elf)
        

Generated by: LCOV version 2.0-1