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