xref: /aosp_15_r20/external/elfutils/libdwfl/offline.c (revision 7304104da70ce23c86437a01be71edd1a2d7f37e)
1 /* Recover relocatibility for addresses computed from debug information.
2    Copyright (C) 2005-2009, 2012 Red Hat, Inc.
3    Copyright (C) 2022 Mark J. Wielaard <[email protected]>
4    Copyright (C) 2022 Google LLC
5    This file is part of elfutils.
6 
7    This file is free software; you can redistribute it and/or modify
8    it under the terms of either
9 
10      * the GNU Lesser General Public License as published by the Free
11        Software Foundation; either version 3 of the License, or (at
12        your option) any later version
13 
14    or
15 
16      * the GNU General Public License as published by the Free
17        Software Foundation; either version 2 of the License, or (at
18        your option) any later version
19 
20    or both in parallel, as here.
21 
22    elfutils is distributed in the hope that it will be useful, but
23    WITHOUT ANY WARRANTY; without even the implied warranty of
24    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25    General Public License for more details.
26 
27    You should have received copies of the GNU General Public License and
28    the GNU Lesser General Public License along with this program.  If
29    not, see <http://www.gnu.org/licenses/>.  */
30 
31 #ifdef HAVE_CONFIG_H
32 # include <config.h>
33 #endif
34 
35 #include "libelfP.h"
36 #include "libdwflP.h"
37 #include <fcntl.h>
38 
39 /* Since dwfl_report_elf lays out the sections already, this will only be
40    called when the section headers of the debuginfo file are being
41    consulted instead, or for the section placed at 0.  With binutils
42    strip-to-debug, the symbol table is in the debuginfo file and relocation
43    looks there.  */
44 int
dwfl_offline_section_address(Dwfl_Module * mod,void ** userdata,const char * modname,Dwarf_Addr base,const char * secname,Elf32_Word shndx,const GElf_Shdr * shdr,Dwarf_Addr * addr)45 dwfl_offline_section_address (Dwfl_Module *mod,
46 			      void **userdata __attribute__ ((unused)),
47 			      const char *modname __attribute__ ((unused)),
48 			      Dwarf_Addr base __attribute__ ((unused)),
49 			      const char *secname __attribute__ ((unused)),
50 			      Elf32_Word shndx,
51 			      const GElf_Shdr *shdr __attribute__ ((unused)),
52 			      Dwarf_Addr *addr)
53 {
54   if (mod->e_type != ET_REL
55       || shdr->sh_addr != 0
56       || !(shdr->sh_flags & SHF_ALLOC)
57       || shndx == 0)
58     return -1;
59 
60   if (mod->debug.elf == NULL)
61     /* We are only here because sh_addr is zero even though layout is complete.
62        The first section in the first file under -e is placed at 0.  */
63     return 0;
64 
65   /* The section numbers might not match between the two files.
66      The best we can rely on is the order of SHF_ALLOC sections.  */
67 
68   Elf_Scn *ourscn = elf_getscn (mod->debug.elf, shndx);
69   Elf_Scn *scn = NULL;
70   uint_fast32_t skip_alloc = 0;
71   while ((scn = elf_nextscn (mod->debug.elf, scn)) != ourscn)
72     {
73       assert (scn != NULL);
74       GElf_Shdr shdr_mem;
75       GElf_Shdr *sh = gelf_getshdr (scn, &shdr_mem);
76       if (unlikely (sh == NULL))
77 	return -1;
78       if (sh->sh_flags & SHF_ALLOC)
79 	++skip_alloc;
80     }
81 
82   scn = NULL;
83   while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
84     {
85       GElf_Shdr shdr_mem;
86       GElf_Shdr *main_shdr = gelf_getshdr (scn, &shdr_mem);
87       if (unlikely (main_shdr == NULL))
88 	return -1;
89       if ((main_shdr->sh_flags & SHF_ALLOC) && skip_alloc-- == 0)
90 	{
91 	  assert (main_shdr->sh_flags == shdr->sh_flags);
92 	  *addr = main_shdr->sh_addr;
93 	  return 0;
94 	}
95     }
96 
97   /* This should never happen.  */
98   return -1;
99 }
100 INTDEF (dwfl_offline_section_address)
101 
102 /* Forward declarations.  */
103 static Dwfl_Module *process_elf (Dwfl *dwfl, const char *name,
104 				 const char *file_name, int fd, Elf *elf);
105 static Dwfl_Module *process_archive (Dwfl *dwfl, const char *name,
106 				     const char *file_name, int fd, Elf *elf,
107 				     int (*predicate) (const char *module,
108 						       const char *file));
109 
110 /* Report one module for an ELF file, or many for an archive.
111    Always consumes ELF and FD.  */
112 static Dwfl_Module *
process_file(Dwfl * dwfl,const char * name,const char * file_name,int fd,Elf * elf,int (* predicate)(const char * module,const char * file))113 process_file (Dwfl *dwfl, const char *name, const char *file_name, int fd,
114 	      Elf *elf, int (*predicate) (const char *module,
115 					  const char *file))
116 {
117   switch (elf_kind (elf))
118     {
119     default:
120     case ELF_K_NONE:
121       __libdwfl_seterrno (elf == NULL ? DWFL_E_LIBELF : DWFL_E_BADELF);
122       return NULL;
123 
124     case ELF_K_ELF:
125       return process_elf (dwfl, name, file_name, fd, elf);
126 
127     case ELF_K_AR:
128       return process_archive (dwfl, name, file_name, fd, elf, predicate);
129     }
130 }
131 
132 /* Report the open ELF file as a module.  Always consumes ELF and FD.  */
133 static Dwfl_Module *
process_elf(Dwfl * dwfl,const char * name,const char * file_name,int fd,Elf * elf)134 process_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
135 	     Elf *elf)
136 {
137   Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name, fd, elf,
138 					   dwfl->offline_next_address, true,
139 					   false);
140   if (mod != NULL)
141     {
142       /* If this is an ET_EXEC file with fixed addresses, the address range
143 	 it consumed may or may not intersect with the arbitrary range we
144 	 will use for relocatable modules.  Make sure we always use a free
145 	 range for the offline allocations.  If this module did use
146 	 offline_next_address, it may have rounded it up for the module's
147 	 alignment requirements.  */
148       if ((dwfl->offline_next_address >= mod->low_addr
149 	   || mod->low_addr - dwfl->offline_next_address < OFFLINE_REDZONE)
150 	  && dwfl->offline_next_address < mod->high_addr + OFFLINE_REDZONE)
151 	dwfl->offline_next_address = mod->high_addr + OFFLINE_REDZONE;
152 
153       /* Don't keep the file descriptor around.  */
154       if (mod->main.fd != -1 && elf_cntl (mod->main.elf, ELF_C_FDREAD) == 0)
155 	{
156 	  /* Grab the path in case we want to report this file as
157 	     Dwarf later.  */
158 	  mod->elfpath = __libdw_elfpath (mod->main.fd);
159 	  close (mod->main.fd);
160 	  mod->main.fd = -1;
161 	}
162     }
163 
164   return mod;
165 }
166 
167 /* Always consumes MEMBER.  Returns elf_next result on success.
168    For errors returns ELF_C_NULL with *MOD set to null.  */
169 static Elf_Cmd
process_archive_member(Dwfl * dwfl,const char * name,const char * file_name,int (* predicate)(const char * module,const char * file),int fd,Elf * member,Dwfl_Module ** mod)170 process_archive_member (Dwfl *dwfl, const char *name, const char *file_name,
171 			int (*predicate) (const char *module, const char *file),
172 			int fd, Elf *member, Dwfl_Module **mod)
173 {
174   const Elf_Arhdr *h = elf_getarhdr (member);
175   if (unlikely (h == NULL))
176     {
177       __libdwfl_seterrno (DWFL_E_LIBELF);
178     fail:
179       elf_end (member);
180       *mod = NULL;
181       return ELF_C_NULL;
182     }
183 
184   if (!strcmp (h->ar_name, "/") || !strcmp (h->ar_name, "//")
185       || !strcmp (h->ar_name, "/SYM64/"))
186     {
187     skip:;
188       /* Skip this and go to the next.  */
189       Elf_Cmd result = elf_next (member);
190       elf_end (member);
191       return result;
192     }
193 
194   char *member_name;
195   if (unlikely (asprintf (&member_name, "%s(%s)", file_name, h->ar_name) < 0))
196     {
197     nomem:
198       __libdwfl_seterrno (DWFL_E_NOMEM);
199       elf_end (member);
200       *mod = NULL;
201       return ELF_C_NULL;
202     }
203 
204   char *module_name = NULL;
205   if (name == NULL || name[0] == '\0')
206     name = h->ar_name;
207   else if (unlikely (asprintf (&module_name, "%s:%s", name, h->ar_name) < 0))
208     {
209       free (member_name);
210       goto nomem;
211     }
212   else
213     name = module_name;
214 
215   if (predicate != NULL)
216     {
217       /* Let the predicate decide whether to use this one.  */
218       int want = (*predicate) (name, member_name);
219       if (want <= 0)
220 	{
221 	  free (member_name);
222 	  free (module_name);
223 	  if (unlikely (want < 0))
224 	    {
225 	      __libdwfl_seterrno (DWFL_E_CB);
226 	      goto fail;
227 	    }
228 	  goto skip;
229 	}
230     }
231 
232   /* We let __libdwfl_report_elf cache the fd in mod->main.fd,
233      though it's the same fd for all the members.
234      On module teardown we will close it only on the last Elf reference.  */
235   *mod = process_file (dwfl, name, member_name, fd, member, predicate);
236   free (member_name);
237   free (module_name);
238 
239   if (*mod == NULL)
240     {
241       elf_end (member);
242       return ELF_C_NULL;
243     }
244 
245   /* Advance the archive-reading offset for the next iteration.  */
246   return elf_next (member);
247 }
248 
249 /* Report each member of the archive as its own module.  */
250 static Dwfl_Module *
process_archive(Dwfl * dwfl,const char * name,const char * file_name,int fd,Elf * archive,int (* predicate)(const char * module,const char * file))251 process_archive (Dwfl *dwfl, const char *name, const char *file_name, int fd,
252 		 Elf *archive,
253 		 int (*predicate) (const char *module, const char *file))
254 
255 {
256   Dwfl_Module *mod = NULL;
257   /* elf_begin supports opening archives even with fd == -1 passed.  */
258   Elf *member = elf_begin (fd, archive->cmd, archive);
259   if (unlikely (member == NULL)) /* Empty archive.  */
260     {
261       __libdwfl_seterrno (DWFL_E_BADELF);
262       return NULL;
263     }
264 
265   while (process_archive_member (dwfl, name, file_name, predicate,
266 				 fd, member, &mod) != ELF_C_NULL)
267     member = elf_begin (fd, archive->cmd, archive);
268 
269   /* We can drop the archive Elf handle even if we're still using members
270      in live modules.  When the last module's elf_end on a member returns
271      zero, that module will close FD.  If no modules survived the predicate,
272      we are all done with the file right here.  */
273   if (mod != NULL		/* If no modules, caller will clean up.  */
274       && elf_end (archive) == 0)
275     close (fd);
276 
277   return mod;
278 }
279 
280 Dwfl_Module *
281 internal_function
__libdwfl_report_offline(Dwfl * dwfl,const char * name,const char * file_name,int fd,bool closefd,int (* predicate)(const char * module,const char * file))282 __libdwfl_report_offline (Dwfl *dwfl, const char *name,
283 			  const char *file_name, int fd, bool closefd,
284 			  int (*predicate) (const char *module,
285 					    const char *file))
286 {
287   Elf *elf;
288   Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, true);
289   if (error != DWFL_E_NOERROR)
290     {
291       __libdwfl_seterrno (error);
292       return NULL;
293     }
294   Dwfl_Module *mod = process_file (dwfl, name, file_name, fd, elf, predicate);
295   if (mod == NULL)
296     {
297       elf_end (elf);
298       if (closefd)
299 	close (fd);
300     }
301   return mod;
302 }
303 
304 Dwfl_Module *
dwfl_report_offline(Dwfl * dwfl,const char * name,const char * file_name,int fd)305 dwfl_report_offline (Dwfl *dwfl, const char *name,
306 		     const char *file_name, int fd)
307 {
308   if (dwfl == NULL)
309     return NULL;
310 
311   bool closefd = false;
312   if (fd < 0)
313     {
314       closefd = true;
315       fd = open (file_name, O_RDONLY);
316       if (fd < 0)
317 	{
318 	  __libdwfl_seterrno (DWFL_E_ERRNO);
319 	  return NULL;
320 	}
321     }
322 
323   return __libdwfl_report_offline (dwfl, name, file_name, fd, closefd, NULL);
324 }
INTDEF(dwfl_report_offline)325 INTDEF (dwfl_report_offline)
326 
327 Dwfl_Module *
328 dwfl_report_offline_memory (Dwfl *dwfl, const char *name,
329 			    const char *file_name, char *data, size_t size)
330 {
331   if (dwfl == NULL)
332     return NULL;
333 
334   Elf *elf;
335   Dwfl_Error error = __libdw_open_elf_memory (data, size, &elf, true);
336   if (error != DWFL_E_NOERROR)
337     {
338       __libdwfl_seterrno (error);
339       return NULL;
340     }
341   /* It is ok to pass fd == -1 here, because libelf uses it as a value for
342      "no file opened" and supports working with files without fd, thanks to
343      the existence of the elf_memory function.  */
344   Dwfl_Module *mod = process_file (dwfl, name, file_name, -1, elf, NULL);
345   if (mod == NULL)
346     elf_end (elf);
347   return mod;
348 }
349 INTDEF (dwfl_report_offline_memory)
350