xref: /aosp_15_r20/external/elfutils/libdw/libdw_find_split_unit.c (revision 7304104da70ce23c86437a01be71edd1a2d7f37e)
1 /* Find the split (or skeleton) unit for a given unit.
2    Copyright (C) 2018 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 "libdwP.h"
34 #include "libelfP.h"
35 
36 #include <limits.h>
37 #include <search.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43 
44 static void
try_split_file(Dwarf_CU * cu,const char * dwo_path)45 try_split_file (Dwarf_CU *cu, const char *dwo_path)
46 {
47   int split_fd = open (dwo_path, O_RDONLY);
48   if (split_fd != -1)
49     {
50       Dwarf *split_dwarf = dwarf_begin (split_fd, DWARF_C_READ);
51       if (split_dwarf != NULL)
52 	{
53 	  Dwarf_CU *split = NULL;
54 	  while (INTUSE(dwarf_get_units) (split_dwarf, split, &split,
55 					  NULL, NULL, NULL, NULL) == 0)
56 	    {
57 	      if (split->unit_type == DW_UT_split_compile
58 		  && cu->unit_id8 == split->unit_id8)
59 		{
60 		  if (tsearch (split->dbg, &cu->dbg->split_tree,
61 			       __libdw_finddbg_cb) == NULL)
62 		    {
63 		      /* Something went wrong.  Don't link.  */
64 		      __libdw_seterrno (DWARF_E_NOMEM);
65 		      break;
66 		    }
67 
68 		  /* Link skeleton and split compile units.  */
69 		  __libdw_link_skel_split (cu, split);
70 
71 		  /* We have everything we need from this ELF
72 		     file.  And we are going to close the fd to
73 		     not run out of file descriptors.  */
74 		  elf_cntl (split_dwarf->elf, ELF_C_FDDONE);
75 		  break;
76 		}
77 	    }
78 	  if (cu->split == (Dwarf_CU *) -1)
79 	    dwarf_end (split_dwarf);
80 	}
81       /* Always close, because we don't want to run out of file
82 	 descriptors.  See also the elf_fcntl ELF_C_FDDONE call
83 	 above.  */
84       close (split_fd);
85     }
86 }
87 
88 static void
try_dwp_file(Dwarf_CU * cu)89 try_dwp_file (Dwarf_CU *cu)
90 {
91   if (cu->dbg->dwp_dwarf == NULL)
92     {
93       if (cu->dbg->elfpath != NULL)
94 	{
95 	  /* The DWARF 5 standard says "the package file is typically placed in
96 	     the same directory as the application, and is given the same name
97 	     with a '.dwp' extension".  */
98 	  size_t elfpath_len = strlen (cu->dbg->elfpath);
99 	  char *dwp_path = malloc (elfpath_len + 5);
100 	  if (dwp_path == NULL)
101 	    {
102 	      __libdw_seterrno (DWARF_E_NOMEM);
103 	      return;
104 	    }
105 	  memcpy (dwp_path, cu->dbg->elfpath, elfpath_len);
106 	  strcpy (dwp_path + elfpath_len, ".dwp");
107 	  int dwp_fd = open (dwp_path, O_RDONLY);
108 	  free (dwp_path);
109 	  if (dwp_fd != -1)
110 	    {
111 	      Dwarf *dwp_dwarf = dwarf_begin (dwp_fd, DWARF_C_READ);
112 	      /* There's no way to know whether we got the correct file until
113 		 we look up the unit, but it should at least be a dwp file.  */
114 	      if (dwp_dwarf != NULL
115 		  && (dwp_dwarf->sectiondata[IDX_debug_cu_index] != NULL
116 		      || dwp_dwarf->sectiondata[IDX_debug_tu_index] != NULL))
117 		{
118 		  cu->dbg->dwp_dwarf = dwp_dwarf;
119 		  cu->dbg->dwp_fd = dwp_fd;
120 		}
121 	      else
122 		close (dwp_fd);
123 	    }
124 	}
125       if (cu->dbg->dwp_dwarf == NULL)
126 	cu->dbg->dwp_dwarf = (Dwarf *) -1;
127     }
128 
129   if (cu->dbg->dwp_dwarf != (Dwarf *) -1)
130     {
131       Dwarf_CU *split = __libdw_dwp_findcu_id (cu->dbg->dwp_dwarf,
132 					       cu->unit_id8);
133       if (split != NULL)
134 	{
135 	  if (tsearch (split->dbg, &cu->dbg->split_tree,
136 		       __libdw_finddbg_cb) == NULL)
137 	    {
138 	      /* Something went wrong.  Don't link.  */
139 	      __libdw_seterrno (DWARF_E_NOMEM);
140 	      return;
141 	    }
142 
143 	  /* Link skeleton and split compile units.  */
144 	  __libdw_link_skel_split (cu, split);
145 	}
146     }
147 }
148 
149 Dwarf_CU *
150 internal_function
__libdw_find_split_unit(Dwarf_CU * cu)151 __libdw_find_split_unit (Dwarf_CU *cu)
152 {
153   /* Only try once.  */
154   if (cu->split != (Dwarf_CU *) -1)
155     return cu->split;
156 
157   /* We need a skeleton unit with a comp_dir and [GNU_]dwo_name attributes.
158      The split unit will be the first in the dwo file and should have the
159      same id as the skeleton.  */
160   if (cu->unit_type == DW_UT_skeleton)
161     {
162       /* First, try the dwp file.  */
163       try_dwp_file (cu);
164 
165       Dwarf_Die cudie = CUDIE (cu);
166       Dwarf_Attribute dwo_name;
167       /* Try a dwo file.  It is fine if dwo_dir doesn't exist, but then
168 	 dwo_name needs to be an absolute path.  */
169       if (cu->split == (Dwarf_CU *) -1
170 	  && (dwarf_attr (&cudie, DW_AT_dwo_name, &dwo_name) != NULL
171 	      || dwarf_attr (&cudie, DW_AT_GNU_dwo_name, &dwo_name) != NULL))
172 	{
173 	  /* Try the dwo file name in the same directory
174 	     as we found the skeleton file.  */
175 	  const char *dwo_file = dwarf_formstring (&dwo_name);
176 	  const char *debugdir = cu->dbg->debugdir;
177 	  char *dwo_path = __libdw_filepath (debugdir, NULL, dwo_file);
178 	  if (dwo_path != NULL)
179 	    {
180 	      try_split_file (cu, dwo_path);
181 	      free (dwo_path);
182 	    }
183 
184 	  if (cu->split == (Dwarf_CU *) -1)
185 	    {
186 	      /* Try compdir plus dwo_name.  */
187 	      Dwarf_Attribute compdir;
188 	      dwarf_attr (&cudie, DW_AT_comp_dir, &compdir);
189 	      const char *dwo_dir = dwarf_formstring (&compdir);
190 	      if (dwo_dir != NULL)
191 		{
192 		  dwo_path = __libdw_filepath (debugdir, dwo_dir, dwo_file);
193 		  if (dwo_path != NULL)
194 		    {
195 		      try_split_file (cu, dwo_path);
196 		      free (dwo_path);
197 		    }
198 		}
199 	    }
200 	  /* XXX If still not found we could try stripping dirs from the
201 	     comp_dir and adding them from the comp_dir, assuming
202 	     someone moved a whole build tree around.  */
203 	}
204     }
205 
206   /* If we found nothing, make sure we don't try again.  */
207   if (cu->split == (Dwarf_CU *) -1)
208     cu->split = NULL;
209 
210   return cu->split;
211 }
212