/src/elfutils/libdw/libdw_find_split_unit.c
Line | Count | Source (jump to first uncovered line) |
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 | | #include "eu-search.h" |
36 | | |
37 | | #include <limits.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 |
45 | | try_split_file (Dwarf_CU *cu, const char *dwo_path) |
46 | 0 | { |
47 | 0 | int split_fd = open (dwo_path, O_RDONLY); |
48 | 0 | if (split_fd != -1) |
49 | 0 | { |
50 | 0 | Dwarf *split_dwarf = dwarf_begin (split_fd, DWARF_C_READ); |
51 | 0 | if (split_dwarf != NULL) |
52 | 0 | { |
53 | 0 | Dwarf_CU *split = NULL; |
54 | 0 | while (INTUSE(dwarf_get_units) (split_dwarf, split, &split, |
55 | 0 | NULL, NULL, NULL, NULL) == 0) |
56 | 0 | { |
57 | 0 | if (split->unit_type == DW_UT_split_compile |
58 | 0 | && cu->unit_id8 == split->unit_id8) |
59 | 0 | { |
60 | 0 | if (eu_tsearch (split->dbg, &cu->dbg->split_tree, |
61 | 0 | __libdw_finddbg_cb) == NULL) |
62 | 0 | { |
63 | | /* Something went wrong. Don't link. */ |
64 | 0 | __libdw_seterrno (DWARF_E_NOMEM); |
65 | 0 | break; |
66 | 0 | } |
67 | | |
68 | | /* Link skeleton and split compile units. */ |
69 | 0 | __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 | 0 | elf_cntl (split_dwarf->elf, ELF_C_FDDONE); |
75 | 0 | break; |
76 | 0 | } |
77 | 0 | } |
78 | 0 | if (cu->split == (Dwarf_CU *) -1) |
79 | 0 | dwarf_end (split_dwarf); |
80 | 0 | } |
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 | 0 | close (split_fd); |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | | static void |
89 | | try_dwp_file (Dwarf_CU *cu) |
90 | 0 | { |
91 | 0 | if (cu->dbg->dwp_dwarf == NULL) |
92 | 0 | { |
93 | 0 | if (cu->dbg->elfpath != NULL) |
94 | 0 | { |
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 | 0 | size_t elfpath_len = strlen (cu->dbg->elfpath); |
99 | 0 | char *dwp_path = malloc (elfpath_len + 5); |
100 | 0 | if (dwp_path == NULL) |
101 | 0 | { |
102 | 0 | __libdw_seterrno (DWARF_E_NOMEM); |
103 | 0 | return; |
104 | 0 | } |
105 | 0 | memcpy (dwp_path, cu->dbg->elfpath, elfpath_len); |
106 | 0 | strcpy (dwp_path + elfpath_len, ".dwp"); |
107 | 0 | int dwp_fd = open (dwp_path, O_RDONLY); |
108 | 0 | free (dwp_path); |
109 | 0 | if (dwp_fd != -1) |
110 | 0 | { |
111 | 0 | 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 | 0 | if (dwp_dwarf != NULL |
115 | 0 | && (dwp_dwarf->sectiondata[IDX_debug_cu_index] != NULL |
116 | 0 | || dwp_dwarf->sectiondata[IDX_debug_tu_index] != NULL)) |
117 | 0 | { |
118 | 0 | cu->dbg->dwp_dwarf = dwp_dwarf; |
119 | 0 | cu->dbg->dwp_fd = dwp_fd; |
120 | 0 | } |
121 | 0 | else |
122 | 0 | close (dwp_fd); |
123 | 0 | } |
124 | 0 | } |
125 | 0 | if (cu->dbg->dwp_dwarf == NULL) |
126 | 0 | cu->dbg->dwp_dwarf = (Dwarf *) -1; |
127 | 0 | } |
128 | | |
129 | 0 | if (cu->dbg->dwp_dwarf != (Dwarf *) -1) |
130 | 0 | { |
131 | 0 | Dwarf_CU *split = __libdw_dwp_findcu_id (cu->dbg->dwp_dwarf, |
132 | 0 | cu->unit_id8); |
133 | 0 | if (split != NULL) |
134 | 0 | { |
135 | 0 | if (eu_tsearch (split->dbg, &cu->dbg->split_tree, |
136 | 0 | __libdw_finddbg_cb) == NULL) |
137 | 0 | { |
138 | | /* Something went wrong. Don't link. */ |
139 | 0 | __libdw_seterrno (DWARF_E_NOMEM); |
140 | 0 | return; |
141 | 0 | } |
142 | | |
143 | | /* Link skeleton and split compile units. */ |
144 | 0 | __libdw_link_skel_split (cu, split); |
145 | 0 | } |
146 | 0 | } |
147 | 0 | } |
148 | | |
149 | | Dwarf_CU * |
150 | | internal_function |
151 | | __libdw_find_split_unit (Dwarf_CU *cu) |
152 | 0 | { |
153 | | /* Set result before releasing the lock. */ |
154 | 0 | Dwarf_CU *result; |
155 | |
|
156 | 0 | rwlock_wrlock(cu->split_lock); |
157 | | |
158 | | /* Only try once. */ |
159 | 0 | if (cu->split != (Dwarf_CU *) -1) |
160 | 0 | { |
161 | 0 | result = cu->split; |
162 | 0 | rwlock_unlock(cu->split_lock); |
163 | 0 | return result; |
164 | 0 | } |
165 | | |
166 | | /* We need a skeleton unit with a comp_dir and [GNU_]dwo_name attributes. |
167 | | The split unit will be the first in the dwo file and should have the |
168 | | same id as the skeleton. */ |
169 | 0 | if (cu->unit_type == DW_UT_skeleton) |
170 | 0 | { |
171 | | /* First, try the dwp file. */ |
172 | 0 | try_dwp_file (cu); |
173 | |
|
174 | 0 | Dwarf_Die cudie = CUDIE (cu); |
175 | 0 | Dwarf_Attribute dwo_name; |
176 | | /* Try a dwo file. It is fine if dwo_dir doesn't exist, but then |
177 | | dwo_name needs to be an absolute path. */ |
178 | 0 | if (cu->split == (Dwarf_CU *) -1 |
179 | 0 | && (dwarf_attr (&cudie, DW_AT_dwo_name, &dwo_name) != NULL |
180 | 0 | || dwarf_attr (&cudie, DW_AT_GNU_dwo_name, &dwo_name) != NULL)) |
181 | 0 | { |
182 | | /* Try the dwo file name in the same directory |
183 | | as we found the skeleton file. */ |
184 | 0 | const char *dwo_file = dwarf_formstring (&dwo_name); |
185 | 0 | const char *debugdir = cu->dbg->debugdir; |
186 | 0 | char *dwo_path = __libdw_filepath (debugdir, NULL, dwo_file); |
187 | 0 | if (dwo_path != NULL) |
188 | 0 | { |
189 | 0 | try_split_file (cu, dwo_path); |
190 | 0 | free (dwo_path); |
191 | 0 | } |
192 | |
|
193 | 0 | if (cu->split == (Dwarf_CU *) -1) |
194 | 0 | { |
195 | | /* Try compdir plus dwo_name. */ |
196 | 0 | Dwarf_Attribute compdir; |
197 | 0 | dwarf_attr (&cudie, DW_AT_comp_dir, &compdir); |
198 | 0 | const char *dwo_dir = dwarf_formstring (&compdir); |
199 | 0 | if (dwo_dir != NULL) |
200 | 0 | { |
201 | 0 | dwo_path = __libdw_filepath (debugdir, dwo_dir, dwo_file); |
202 | 0 | if (dwo_path != NULL) |
203 | 0 | { |
204 | 0 | try_split_file (cu, dwo_path); |
205 | 0 | free (dwo_path); |
206 | 0 | } |
207 | 0 | } |
208 | 0 | } |
209 | | /* XXX If still not found we could try stripping dirs from the |
210 | | comp_dir and adding them from the comp_dir, assuming |
211 | | someone moved a whole build tree around. */ |
212 | 0 | } |
213 | 0 | } |
214 | | |
215 | | /* If we found nothing, make sure we don't try again. */ |
216 | 0 | if (cu->split == (Dwarf_CU *) -1) |
217 | 0 | cu->split = NULL; |
218 | |
|
219 | 0 | result = cu->split; |
220 | 0 | rwlock_unlock(cu->split_lock); |
221 | 0 | return result; |
222 | 0 | } |