1 /* Copyright (C) 2011-2021 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #define E(name) E_(name, CLASS)
19 #define E_(name, cl) E__(name, cl)
20 #define E__(name, cl) name##cl
21 #define EW(type) EW_(Elf, CLASS, type)
22 #define EW_(e, w, t) EW__(e, w, _##t)
23 #define EW__(e, w, t) e##w##t
24 
E(link_map)25 struct E(link_map)
26 {
27   EW(Addr) l_addr;
28   EW(Addr) l_name;
29   EW(Addr) l_ld;
30   EW(Addr) l_next;
31   EW(Addr) l_prev;
32   EW(Addr) l_real;
33   Lmid_t l_ns;
34   EW(Addr) l_libname;
35 };
36 #if CLASS == __ELF_NATIVE_CLASS
37 _Static_assert (offsetof (struct link_map, l_addr)
38 		== offsetof (struct E(link_map), l_addr), "l_addr");
39 _Static_assert (offsetof (struct link_map, l_name)
40 		== offsetof (struct E(link_map), l_name), "l_name");
41 _Static_assert (offsetof (struct link_map, l_next)
42 		== offsetof (struct E(link_map), l_next), "l_next");
43 #endif
44 
45 
E(libname_list)46 struct E(libname_list)
47 {
48   EW(Addr) name;
49   EW(Addr) next;
50 };
51 #if CLASS == __ELF_NATIVE_CLASS
52 _Static_assert (offsetof (struct libname_list, name)
53 		== offsetof (struct E(libname_list), name), "name");
54 _Static_assert (offsetof (struct libname_list, next)
55 		== offsetof (struct E(libname_list), next), "next");
56 #endif
57 
E(r_debug)58 struct E(r_debug)
59 {
60   int r_version;
61 #if CLASS == 64
62   int pad;
63 #endif
64   EW(Addr) r_map;
65 };
66 #if CLASS == __ELF_NATIVE_CLASS
67 _Static_assert (offsetof (struct r_debug, r_version)
68 		== offsetof (struct E(r_debug), r_version), "r_version");
69 _Static_assert (offsetof (struct r_debug, r_map)
70 		== offsetof (struct E(r_debug), r_map), "r_map");
71 #endif
72 
73 
74 static int
75 
E(find_maps)76 E(find_maps) (const char *exe, int memfd, pid_t pid, void *auxv,
77 	      size_t auxv_size)
78 {
79   EW(Addr) phdr = 0;
80   unsigned int phnum = 0;
81   unsigned int phent = 0;
82 
83   EW(auxv_t) *auxvXX = (EW(auxv_t) *) auxv;
84   for (int i = 0; i < auxv_size / sizeof (EW(auxv_t)); ++i)
85     switch (auxvXX[i].a_type)
86       {
87       case AT_PHDR:
88 	phdr = auxvXX[i].a_un.a_val;
89 	break;
90       case AT_PHNUM:
91 	phnum = auxvXX[i].a_un.a_val;
92 	break;
93       case AT_PHENT:
94 	phent = auxvXX[i].a_un.a_val;
95 	break;
96       default:
97 	break;
98       }
99 
100   if (phdr == 0 || phnum == 0 || phent == 0)
101     error (EXIT_FAILURE, 0, gettext ("cannot find program header of process"));
102 
103   EW(Phdr) *p = xmalloc (phnum * phent);
104   if (pread (memfd, p, phnum * phent, phdr) != phnum * phent)
105     error (EXIT_FAILURE, 0, gettext ("cannot read program header"));
106 
107   /* Determine the load offset.  We need this for interpreting the
108      other program header entries so we do this in a separate loop.
109      Fortunately it is the first time unless someone does something
110      stupid when linking the application.  */
111   EW(Addr) offset = 0;
112   for (unsigned int i = 0; i < phnum; ++i)
113     if (p[i].p_type == PT_PHDR)
114       {
115 	offset = phdr - p[i].p_vaddr;
116 	break;
117       }
118 
119   EW(Addr) list = 0;
120   char *interp = NULL;
121   for (unsigned int i = 0; i < phnum; ++i)
122     if (p[i].p_type == PT_DYNAMIC)
123       {
124 	EW(Dyn) *dyn = xmalloc (p[i].p_filesz);
125 	if (pread (memfd, dyn, p[i].p_filesz, offset + p[i].p_vaddr)
126 	    != p[i].p_filesz)
127 	  error (EXIT_FAILURE, 0, gettext ("cannot read dynamic section"));
128 
129 	/* Search for the DT_DEBUG entry.  */
130 	for (unsigned int j = 0; j < p[i].p_filesz / sizeof (EW(Dyn)); ++j)
131 	  if (dyn[j].d_tag == DT_DEBUG && dyn[j].d_un.d_ptr != 0)
132 	    {
133 	      struct E(r_debug) r;
134 	      if (pread (memfd, &r, sizeof (r), dyn[j].d_un.d_ptr)
135 		  != sizeof (r))
136 		error (EXIT_FAILURE, 0, gettext ("cannot read r_debug"));
137 
138 	      if (r.r_map != 0)
139 		{
140 		  list = r.r_map;
141 		  break;
142 		}
143 	    }
144 
145 	free (dyn);
146 	break;
147       }
148     else if (p[i].p_type == PT_INTERP)
149       {
150 	interp = xmalloc (p[i].p_filesz);
151 	if (pread (memfd, interp, p[i].p_filesz, offset + p[i].p_vaddr)
152 	    != p[i].p_filesz)
153 	  error (EXIT_FAILURE, 0, gettext ("cannot read program interpreter"));
154       }
155 
156   if (list == 0)
157     {
158       if (interp == NULL)
159 	{
160 	  // XXX check whether the executable itself is the loader
161 	  exit (EXIT_FAILURE);
162 	}
163 
164       // XXX perhaps try finding ld.so and _r_debug in it
165       exit (EXIT_FAILURE);
166     }
167 
168   free (p);
169   free (interp);
170 
171   /* Print the PID and program name first.  */
172   printf ("%lu:\t%s\n", (unsigned long int) pid, exe);
173 
174   /* Iterate over the list of objects and print the information.  */
175   struct scratch_buffer tmpbuf;
176   scratch_buffer_init (&tmpbuf);
177   int status = 0;
178   do
179     {
180       struct E(link_map) m;
181       if (pread (memfd, &m, sizeof (m), list) != sizeof (m))
182 	error (EXIT_FAILURE, 0, gettext ("cannot read link map"));
183 
184       EW(Addr) name_offset = m.l_name;
185       while (1)
186 	{
187 	  ssize_t n = pread (memfd, tmpbuf.data, tmpbuf.length, name_offset);
188 	  if (n == -1)
189 	    error (EXIT_FAILURE, 0, gettext ("cannot read object name"));
190 
191 	  if (memchr (tmpbuf.data, '\0', n) != NULL)
192 	    break;
193 
194 	  if (!scratch_buffer_grow (&tmpbuf))
195 	    error (EXIT_FAILURE, 0,
196 		   gettext ("cannot allocate buffer for object name"));
197 	}
198 
199       /* The m.l_name and m.l_libname.name for loader linkmap points to same
200 	 values (since BZ#387 fix).  Trying to use l_libname name as the
201 	 shared object name might lead to an infinite loop (BZ#18035).  */
202 
203       /* Skip over the executable.  */
204       if (((char *)tmpbuf.data)[0] != '\0')
205 	printf ("%s\n", (char *)tmpbuf.data);
206 
207       list = m.l_next;
208     }
209   while (list != 0);
210 
211   scratch_buffer_free (&tmpbuf);
212   return status;
213 }
214 
215 
216 #undef CLASS
217