/src/elfutils/libdwfl_stacktrace/dwflst_tracker_find_elf.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Find Elf file and cache via Dwflst_Process_Tracker. |
2 | | Copyright (C) 2025, 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 | | |
30 | | #ifdef HAVE_CONFIG_H |
31 | | # include <config.h> |
32 | | #endif |
33 | | |
34 | | #include <sys/stat.h> |
35 | | #include "../libelf/libelfP.h" |
36 | | /* XXX: Private header needed for Elf * ref_count field. */ |
37 | | /* TODO: Consider dup_elf() rather than direct ref_count access. */ |
38 | | |
39 | | #include "libdwfl_stacktraceP.h" |
40 | | |
41 | | unsigned long int |
42 | | __libdwfl_stacktrace_elftab_hash_st (const char *module_name, |
43 | | dev_t st_dev, |
44 | | ino_t st_ino) |
45 | 0 | { |
46 | 0 | unsigned long int hval = elf_hash(module_name); |
47 | 0 | hval ^= (unsigned long int)st_dev; |
48 | 0 | hval ^= (unsigned long int)st_ino; |
49 | 0 | return hval; |
50 | 0 | } |
51 | | |
52 | | unsigned long int |
53 | | __libdwfl_stacktrace_elftab_hash (const char *module_name, |
54 | | const char *module_path, |
55 | | int fd) |
56 | 0 | { |
57 | 0 | struct stat sb; |
58 | 0 | int rc = -1; |
59 | 0 | if (fd >= 0) |
60 | 0 | rc = fstat(fd, &sb); |
61 | 0 | else if (module_path != NULL) |
62 | 0 | rc = stat(module_path, &sb); |
63 | 0 | if (rc < 0) |
64 | 0 | return elf_hash(module_name); |
65 | 0 | return __libdwfl_stacktrace_elftab_hash_st |
66 | 0 | (module_name, sb.st_dev, sb.st_ino); |
67 | 0 | } |
68 | | |
69 | | int |
70 | | dwflst_tracker_find_cached_elf (Dwflst_Process_Tracker *tracker, |
71 | | const char *module_name, |
72 | | const char *module_path, |
73 | | char **file_name, Elf **elfp) |
74 | 0 | { |
75 | 0 | dwflst_tracker_elf_info *ent = NULL; |
76 | 0 | int rc = -1; |
77 | 0 | struct stat sb; |
78 | |
|
79 | 0 | if (module_path == NULL) |
80 | 0 | module_path = module_name; |
81 | 0 | unsigned long int hval = |
82 | 0 | __libdwfl_stacktrace_elftab_hash (module_name, module_path, -1/* no fd */); |
83 | |
|
84 | 0 | rwlock_rdlock(tracker->elftab_lock); |
85 | 0 | ent = dwflst_tracker_elftab_find(&tracker->elftab, hval); |
86 | 0 | rwlock_unlock(tracker->elftab_lock); |
87 | | |
88 | | /* Guard against collisions. |
89 | | TODO: Need proper chaining, dynamicsizehash_concurrent isn't really |
90 | | equipped for it. */ |
91 | 0 | if (ent != NULL) |
92 | 0 | rc = fstat(ent->fd, &sb); |
93 | 0 | if (rc < 0 || strcmp (module_name, ent->module_name) != 0 |
94 | 0 | || ent->dev != sb.st_dev || ent->ino != sb.st_ino) |
95 | 0 | return -1; |
96 | | |
97 | | /* Verify that ent->fd has not been updated: */ |
98 | 0 | if (rc < 0 || ent->dev != sb.st_dev || ent->ino != sb.st_ino |
99 | 0 | || ent->last_mtime != sb.st_mtime) |
100 | 0 | return -1; |
101 | | |
102 | 0 | if (ent->elf != NULL) |
103 | 0 | ent->elf->ref_count++; |
104 | 0 | *elfp = ent->elf; |
105 | 0 | *file_name = strdup(ent->module_name); |
106 | 0 | return ent->fd; |
107 | 0 | } |
108 | | INTDEF(dwflst_tracker_find_cached_elf) |
109 | | |
110 | | bool |
111 | | dwflst_tracker_cache_elf (Dwflst_Process_Tracker *tracker, |
112 | | const char *module_name, |
113 | | const char *file_name __attribute__((unused)), |
114 | | Elf *elf, int fd) |
115 | 0 | { |
116 | 0 | dwflst_tracker_elf_info *ent = NULL; |
117 | 0 | int rc = -1; |
118 | 0 | struct stat sb; |
119 | |
|
120 | 0 | if (fd >= 0) |
121 | 0 | rc = fstat(fd, &sb); |
122 | 0 | if (rc < 0) |
123 | 0 | return false; |
124 | 0 | unsigned long int hval = |
125 | 0 | __libdwfl_stacktrace_elftab_hash_st (module_name, sb.st_dev, sb.st_ino); |
126 | |
|
127 | 0 | rwlock_wrlock(tracker->elftab_lock); |
128 | 0 | ent = dwflst_tracker_elftab_find(&tracker->elftab, hval); |
129 | | /* Guard against collisions. |
130 | | TODO: Need proper chaining, dynamicsizehash_concurrent isn't really |
131 | | equipped for it. */ |
132 | 0 | if (ent != NULL && (strcmp (module_name, ent->module_name) != 0 |
133 | 0 | || ent->dev != sb.st_dev || ent->ino != sb.st_ino)) |
134 | 0 | { |
135 | 0 | rwlock_unlock(tracker->elftab_lock); |
136 | 0 | return false; |
137 | 0 | } |
138 | 0 | if (ent == NULL) |
139 | 0 | { |
140 | 0 | ent = calloc (1, sizeof (dwflst_tracker_elf_info)); |
141 | 0 | if (ent == NULL) |
142 | 0 | { |
143 | 0 | rwlock_unlock(tracker->elftab_lock); |
144 | 0 | __libdwfl_seterrno (DWFL_E_NOMEM); |
145 | 0 | return false; |
146 | 0 | } |
147 | 0 | ent->module_name = strdup(module_name); |
148 | |
|
149 | 0 | if (dwflst_tracker_elftab_insert(&tracker->elftab, hval, ent) != 0) |
150 | 0 | { |
151 | 0 | free(ent->module_name); |
152 | 0 | free(ent); |
153 | 0 | rwlock_unlock(tracker->elftab_lock); |
154 | 0 | assert(false); /* Should not occur due to the wrlock on elftab. */ |
155 | 0 | } |
156 | 0 | } |
157 | 0 | else |
158 | 0 | { |
159 | | /* TODO: The following assertions are still triggered on certain |
160 | | code paths that acquire fds or create Elf structs without |
161 | | checking the caching mechanism first. This is not a serious |
162 | | problem, and can be fixed incrementally. */ |
163 | | |
164 | | /* assert(ent->elf == NULL || ent->elf == elf); */ /* Guard against redundant/leaked Elf *. */ |
165 | | /* assert(ent->fd == fd); */ /* Guard against redundant open. */ |
166 | | |
167 | | /* For now, correct behaviour (from dwfl_module_getdwarf.c open_elf) |
168 | | is to replace the existing elf, keep module_name. */ |
169 | 0 | if (ent->elf != NULL && ent->elf != elf) |
170 | 0 | elf_end(ent->elf); |
171 | 0 | } |
172 | 0 | if (elf != NULL && ent->elf != elf) |
173 | 0 | elf->ref_count++; |
174 | 0 | ent->elf = elf; |
175 | 0 | ent->fd = fd; |
176 | 0 | if (rc == 0) |
177 | 0 | { |
178 | 0 | ent->dev = sb.st_dev; |
179 | 0 | ent->ino = sb.st_ino; |
180 | 0 | ent->last_mtime = sb.st_mtime; |
181 | 0 | } |
182 | | /* else create a cache entry with 0 values for dev/ino/mtime; |
183 | | since dev/ino are hashed, this will not conflict with entries |
184 | | for which fstat was successful */ |
185 | 0 | rwlock_unlock(tracker->elftab_lock); |
186 | 0 | return true; |
187 | 0 | } |
188 | | INTDEF(dwflst_tracker_cache_elf) |
189 | | |
190 | | Dwflst_Process_Tracker * |
191 | | dwflst_module_gettracker (Dwfl_Module *mod) |
192 | 0 | { |
193 | 0 | if (mod == NULL) |
194 | 0 | return NULL; |
195 | 0 | if (mod->dwfl == NULL) |
196 | 0 | return NULL; |
197 | 0 | return mod->dwfl->tracker; |
198 | 0 | } |
199 | | INTDEF(dwflst_module_gettracker) |
200 | | |
201 | | int |
202 | | dwflst_tracker_linux_proc_find_elf (Dwfl_Module *mod, |
203 | | void **userdata __attribute__ ((unused)), |
204 | | const char *module_name, Dwarf_Addr base, |
205 | | char **file_name, Elf **elfp) |
206 | 0 | { |
207 | 0 | Dwflst_Process_Tracker *tracker = INTUSE(dwflst_module_gettracker) (mod); |
208 | 0 | int fd; |
209 | |
|
210 | 0 | if (tracker != NULL) |
211 | 0 | { |
212 | 0 | fd = INTUSE(dwflst_tracker_find_cached_elf) |
213 | 0 | (tracker, module_name, module_name, file_name, elfp); |
214 | 0 | if (fd >= 0) |
215 | 0 | return fd; |
216 | 0 | } |
217 | | |
218 | 0 | fd = INTUSE(dwfl_linux_proc_find_elf) (mod, userdata, module_name, |
219 | 0 | base, file_name, elfp); |
220 | |
|
221 | 0 | if (tracker != NULL && fd >= 0 && *file_name != NULL) |
222 | 0 | { |
223 | 0 | INTUSE(dwflst_tracker_cache_elf) |
224 | 0 | (tracker, module_name, *file_name, *elfp, fd); |
225 | 0 | } |
226 | 0 | return fd; |
227 | 0 | } |