]>
Commit | Line | Data |
---|---|---|
76bdc726 | 1 | /* Copyright (C) 2021-2023 Free Software Foundation, Inc. |
bb368aad VM |
2 | Contributed by Oracle. |
3 | ||
4 | This file is part of GNU Binutils. | |
5 | ||
6 | This program is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 3, or (at your option) | |
9 | any later version. | |
10 | ||
11 | This program is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with this program; if not, write to the Free Software | |
18 | Foundation, 51 Franklin Street - Fifth Floor, Boston, | |
19 | MA 02110-1301, USA. */ | |
20 | ||
21 | /* | |
22 | * memory map tracking | |
23 | * incorporating former "loadobjects" into more general "map" | |
24 | * (including code and data segments and dynamic functions) | |
25 | */ | |
26 | ||
27 | #include "config.h" | |
28 | #include <alloca.h> | |
29 | #include <dlfcn.h> | |
30 | #include <errno.h> | |
31 | #include <fcntl.h> | |
32 | #include <elf.h> | |
33 | #include <sys/mman.h> | |
34 | #include <sys/param.h> | |
35 | #include <stdint.h> | |
36 | ||
37 | #include "gp-defs.h" | |
38 | #include "collector.h" | |
39 | #include "gp-experiment.h" | |
40 | #include "memmgr.h" | |
41 | ||
42 | /* | |
43 | * These are obsolete and unreliable. | |
44 | * They are included here only for historical compatibility. | |
45 | */ | |
46 | #define MA_SHARED 0x08 /* changes are shared by mapped object */ | |
47 | #define MA_ANON 0x40 /* anonymous memory (e.g. /dev/zero) */ | |
48 | #define MA_ISM 0x80 /* intimate shared mem (shared MMU resources) */ | |
49 | #define MA_BREAK 0x10 /* grown by brk(2) */ | |
50 | #define MA_STACK 0x20 /* grown automatically on stack faults */ | |
51 | ||
52 | typedef struct prmap_t | |
53 | { | |
54 | unsigned long pr_vaddr; /* virtual address of mapping */ | |
55 | unsigned long pr_size; /* size of mapping in bytes */ | |
56 | char *pr_mapname; /* name in /proc/<pid>/object */ | |
57 | int pr_mflags; /* protection and attribute flags (see below) */ | |
58 | unsigned long pr_offset; /* offset into mapped object, if any */ | |
59 | unsigned long pr_dev; | |
60 | unsigned long pr_ino; | |
61 | int pr_pagesize; /* pagesize (bytes) for this mapping */ | |
62 | } prmap_t; | |
63 | ||
64 | /* TprintfT(<level>,...) definitions. Adjust per module as needed */ | |
65 | #define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings | |
66 | #define DBG_LT1 1 // for configuration details, warnings | |
67 | #define DBG_LT2 2 | |
68 | #define DBG_LT3 3 | |
69 | #define DBG_LT4 4 | |
70 | ||
71 | #define SYS_MMAP_NAME "mmap" | |
72 | #define SYS_MMAP64_NAME "mmap64" | |
73 | #define SYS_MUNMAP_NAME "munmap" | |
74 | #define SYS_DLOPEN_NAME "dlopen" | |
75 | #define SYS_DLCLOSE_NAME "dlclose" | |
76 | ||
77 | typedef struct MapInfo | |
78 | { | |
79 | struct MapInfo *next; | |
80 | unsigned long vaddr; | |
81 | unsigned long size; | |
82 | char *mapname; /* name in /proc/<pid>/object */ | |
83 | char *filename; | |
84 | unsigned long offset; | |
85 | int mflags; | |
86 | int pagesize; | |
87 | } MapInfo; | |
88 | ||
89 | typedef struct NameInfo | |
90 | { | |
91 | struct NameInfo *next; | |
92 | char *mapname; | |
93 | char filename[1]; /* dynamic length file name */ | |
94 | } NameInfo; | |
95 | ||
96 | static NameInfo *namemaps = NULL; | |
97 | static MapInfo mmaps; /* current memory maps */ | |
98 | static struct DataHandle *map_hndl = NULL; | |
99 | static char dyntext_fname[MAXPATHLEN]; | |
100 | static void *mapcache = NULL; | |
101 | static char *maptext = NULL; | |
102 | static size_t maptext_sz = 4096; /* initial buffer size */ | |
103 | static int mmap_mode = 0; | |
104 | static int mmap_initted = 0; | |
105 | static collector_mutex_t map_lock = COLLECTOR_MUTEX_INITIALIZER; | |
106 | static collector_mutex_t dyntext_lock = COLLECTOR_MUTEX_INITIALIZER; | |
107 | ||
108 | /* a reentrance guard for the interposition functions ensures that updates to | |
109 | the map cache/file are sequential, with the first doing the final update */ | |
110 | static int reentrance = 0; | |
111 | #define CHCK_REENTRANCE (reentrance || mmap_mode <= 0) | |
112 | #define CURR_REENTRANCE reentrance | |
113 | #define PUSH_REENTRANCE reentrance++ | |
114 | #define POP_REENTRANCE reentrance-- | |
115 | ||
116 | #define CALL_REAL(x) (__real_##x) | |
117 | #define NULL_PTR(x) (__real_##x == NULL) | |
118 | ||
119 | /* interposition function handles */ | |
120 | static void *(*__real_mmap)(void* start, size_t length, int prot, int flags, | |
121 | int fd, off_t offset) = NULL; | |
122 | static void *(*__real_mmap64)(void* start, size_t length, int prot, int flags, | |
123 | int fd, off64_t offset) = NULL; | |
124 | static int (*__real_munmap)(void* start, size_t length) = NULL; | |
125 | static void *(*__real_dlopen)(const char* pathname, int mode) = NULL; | |
126 | #if (ARCH(Intel) && WSIZE(32)) || ARCH(SPARC) | |
127 | static void *(*__real_dlopen_2_1)(const char* pathname, int mode) = NULL; | |
128 | static void *(*__real_dlopen_2_0)(const char* pathname, int mode) = NULL; | |
129 | #endif | |
130 | static int (*__real_dlclose)(void* handle) = NULL; | |
131 | static void (*collector_heap_record)(int, size_t, void*) = NULL; | |
132 | ||
133 | /* internal function prototypes */ | |
134 | static int init_mmap_intf (); | |
135 | static int init_mmap_files (); | |
136 | static void append_segment_record (char *format, ...); | |
137 | static void update_map_segments (hrtime_t hrt, int resolve); | |
138 | static void resolve_mapname (MapInfo *map, char *name); | |
139 | static void record_segment_map (hrtime_t timestamp, uint64_t loadaddr, | |
140 | unsigned long msize, int pagesize, int modeflags, | |
141 | long long offset, unsigned check, char *name); | |
142 | static void record_segment_unmap (hrtime_t timestamp, uint64_t loadaddr); | |
143 | ||
144 | /* Linux needs handling of the vsyscall page to get its data into the map.xml file */ | |
145 | static void process_vsyscall_page (); | |
146 | ||
147 | #define MAXVSYSFUNCS 10 | |
148 | static int nvsysfuncs = 0; | |
149 | static char *sysfuncname[MAXVSYSFUNCS]; | |
150 | static uint64_t sysfuncvaddr[MAXVSYSFUNCS]; | |
151 | static unsigned long sysfuncsize[MAXVSYSFUNCS]; | |
152 | ||
153 | #define MAXDYN 20 | |
154 | static int ndyn = 0; | |
155 | static char *dynname [MAXDYN]; | |
156 | static void *dynvaddr [MAXDYN]; | |
157 | static unsigned dynsize [MAXDYN]; | |
158 | static char *dynfuncname[MAXDYN]; | |
159 | ||
160 | /*===================================================================*/ | |
161 | ||
162 | /* | |
163 | * void __collector_mmap_init_mutex_locks() | |
164 | * Iinitialize mmap mutex locks. | |
165 | */ | |
166 | void | |
167 | __collector_mmap_init_mutex_locks () | |
168 | { | |
169 | __collector_mutex_init (&map_lock); | |
170 | __collector_mutex_init (&dyntext_lock); | |
171 | } | |
172 | ||
173 | /* __collector_ext_update_map_segments called by the audit agent | |
174 | * Is is also called by dbx/collector when a (possible) map update | |
175 | * is intimated, such as after dlopen/dlclose. | |
176 | * Required when libcollector.so is not preloaded and interpositions inactive. | |
177 | */ | |
178 | int | |
179 | __collector_ext_update_map_segments (void) | |
180 | { | |
181 | if (!mmap_initted) | |
182 | return 0; | |
183 | TprintfT (0, "__collector_ext_update_map_segments(%d)\n", CURR_REENTRANCE); | |
184 | if (CHCK_REENTRANCE) | |
185 | return 0; | |
186 | PUSH_REENTRANCE; | |
187 | update_map_segments (GETRELTIME (), 1); | |
188 | POP_REENTRANCE; | |
189 | return 0; | |
190 | } | |
191 | /* | |
192 | * int __collector_ext_mmap_install() | |
193 | * Install and initialise mmap tracing. | |
194 | */ | |
195 | int | |
196 | __collector_ext_mmap_install (int record) | |
197 | { | |
198 | TprintfT (0, "__collector_ext_mmap_install(mmap_mode=%d)\n", mmap_mode); | |
199 | if (NULL_PTR (mmap)) | |
200 | { | |
201 | if (init_mmap_intf ()) | |
202 | { | |
203 | TprintfT (0, "ERROR: collector mmap tracing initialization failed.\n"); | |
204 | return COL_ERROR_EXPOPEN; | |
205 | } | |
206 | } | |
207 | else | |
208 | TprintfT (DBG_LT2, "collector mmap tracing: mmap pointer not null\n"); | |
209 | ||
210 | /* Initialize side door interface with the heap tracing module */ | |
211 | collector_heap_record = (void(*)(int, size_t, void*))dlsym (RTLD_DEFAULT, "__collector_heap_record"); | |
212 | if (record) | |
213 | { | |
214 | map_hndl = __collector_create_handle (SP_MAP_FILE); | |
215 | if (map_hndl == NULL) | |
216 | return COL_ERROR_MAPOPEN; | |
217 | if (init_mmap_files ()) | |
218 | { | |
219 | TprintfT (0, "ERROR: collector init_mmap_files() failed.\n"); | |
220 | return COL_ERROR_EXPOPEN; | |
221 | } | |
222 | } | |
223 | mmaps.next = NULL; | |
224 | mapcache = NULL; | |
225 | PUSH_REENTRANCE; | |
226 | update_map_segments (GETRELTIME (), 1); // initial map | |
227 | POP_REENTRANCE; | |
228 | mmap_mode = 1; | |
229 | mmap_initted = 1; | |
230 | process_vsyscall_page (); | |
231 | return COL_ERROR_NONE; | |
232 | } | |
233 | ||
234 | /* | |
235 | * int __collector_ext_mmap_deinstall() | |
236 | * Optionally update final map and stop tracing mmap events. | |
237 | */ | |
238 | int | |
239 | __collector_ext_mmap_deinstall (int update) | |
240 | { | |
241 | if (!mmap_initted) | |
242 | return COL_ERROR_NONE; | |
243 | mmap_mode = 0; | |
244 | if (update) | |
245 | { | |
246 | /* Final map */ | |
247 | PUSH_REENTRANCE; | |
248 | update_map_segments (GETRELTIME (), 1); | |
249 | POP_REENTRANCE; | |
250 | } | |
251 | TprintfT (0, "__collector_ext_mmap_deinstall(%d)\n", update); | |
252 | if (map_hndl != NULL) | |
253 | { | |
254 | __collector_delete_handle (map_hndl); | |
255 | map_hndl = NULL; | |
256 | } | |
257 | __collector_mutex_lock (&map_lock); // get lock before resetting | |
258 | ||
259 | /* Free all memory maps */ | |
260 | MapInfo *mp; | |
261 | for (mp = mmaps.next; mp;) | |
262 | { | |
263 | MapInfo *next = mp->next; | |
264 | __collector_freeCSize (__collector_heap, mp, sizeof (*mp)); | |
265 | mp = next; | |
266 | } | |
267 | mmaps.next = NULL; | |
268 | ||
269 | /* Free all name maps */ | |
270 | NameInfo *np; | |
271 | for (np = namemaps; np;) | |
272 | { | |
273 | NameInfo *next = np->next; | |
274 | __collector_freeCSize (__collector_heap, np, sizeof (*np) + __collector_strlen (np->filename)); | |
275 | np = next; | |
276 | } | |
277 | namemaps = NULL; | |
278 | mapcache = __collector_reallocVSize (__collector_heap, mapcache, 0); | |
279 | mmaps.next = NULL; | |
280 | mapcache = NULL; | |
281 | __collector_mutex_unlock (&map_lock); | |
282 | TprintfT (0, "__collector_ext_mmap_deinstall done\n"); | |
283 | return 0; | |
284 | } | |
285 | ||
286 | /* | |
287 | * void __collector_mmap_fork_child_cleanup() | |
288 | * Perform all necessary cleanup steps in child process after fork(). | |
289 | */ | |
290 | void | |
291 | __collector_mmap_fork_child_cleanup () | |
292 | { | |
293 | /* Initialize all mmap "mutex" locks */ | |
294 | __collector_mmap_init_mutex_locks (); | |
295 | if (!mmap_initted) | |
296 | return; | |
297 | mmap_mode = 0; | |
298 | __collector_delete_handle (map_hndl); | |
299 | __collector_mutex_lock (&map_lock); // get lock before resetting | |
300 | ||
301 | /* Free all memory maps */ | |
302 | MapInfo *mp; | |
303 | for (mp = mmaps.next; mp;) | |
304 | { | |
305 | MapInfo *next = mp->next; | |
306 | __collector_freeCSize (__collector_heap, mp, sizeof (*mp)); | |
307 | mp = next; | |
308 | } | |
309 | mmaps.next = NULL; | |
310 | ||
311 | /* Free all name maps */ | |
312 | NameInfo *np; | |
313 | for (np = namemaps; np;) | |
314 | { | |
315 | NameInfo *next = np->next; | |
316 | __collector_freeCSize (__collector_heap, np, sizeof (*np) + __collector_strlen (np->filename)); | |
317 | np = next; | |
318 | } | |
319 | namemaps = NULL; | |
320 | mapcache = __collector_reallocVSize (__collector_heap, mapcache, 0); | |
321 | mmap_initted = 0; | |
322 | reentrance = 0; | |
323 | __collector_mutex_unlock (&map_lock); | |
324 | } | |
325 | ||
326 | static int | |
327 | init_mmap_files () | |
328 | { | |
329 | TprintfT (DBG_LT2, "init_mmap_files\n"); | |
330 | /* also create the headerless dyntext file (if required) */ | |
331 | CALL_UTIL (snprintf)(dyntext_fname, sizeof (dyntext_fname), "%s/%s", | |
332 | __collector_exp_dir_name, SP_DYNTEXT_FILE); | |
333 | if (CALL_UTIL (access)(dyntext_fname, F_OK) != 0) | |
334 | { | |
335 | int fd = CALL_UTIL (open)(dyntext_fname, O_RDWR | O_CREAT | O_TRUNC, | |
336 | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | |
337 | if (fd == -1) | |
338 | { | |
339 | char errmsg[256]; | |
340 | TprintfT (0, "ERROR: init_mmap_files: open(%s) failed\n", | |
341 | dyntext_fname); | |
342 | __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s: %s</event>\n", | |
343 | SP_JCMD_CERROR, COL_ERROR_DYNOPEN, errno, | |
344 | dyntext_fname, errmsg); | |
345 | return COL_ERROR_DYNOPEN; | |
346 | } | |
347 | else | |
348 | CALL_UTIL (close)(fd); | |
349 | } | |
350 | return COL_ERROR_NONE; | |
351 | } | |
352 | ||
353 | static void | |
354 | append_segment_record (char *format, ...) | |
355 | { | |
356 | char buf[1024]; | |
357 | char *bufptr = buf; | |
358 | va_list va; | |
359 | va_start (va, format); | |
360 | int sz = __collector_xml_vsnprintf (bufptr, sizeof (buf), format, va); | |
361 | va_end (va); | |
362 | ||
363 | if (__collector_expstate != EXP_OPEN && __collector_expstate != EXP_PAUSED) | |
364 | { | |
365 | TprintfT (0, "append_segment_record: expt neither open nor paused (%d); " | |
366 | "not writing to map.xml\n\t%s", __collector_expstate, buf); | |
367 | return; | |
368 | } | |
369 | if (sz >= sizeof (buf)) | |
370 | { | |
371 | /* Allocate a new buffer */ | |
372 | sz += 1; /* add the terminating null byte */ | |
373 | bufptr = (char*) alloca (sz); | |
374 | va_start (va, format); | |
375 | sz = __collector_xml_vsnprintf (bufptr, sz, format, va); | |
376 | va_end (va); | |
377 | } | |
378 | int rc = __collector_write_string (map_hndl, bufptr, sz); | |
379 | if (rc != 0) | |
380 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\"></event>\n", | |
381 | SP_JCMD_CERROR, COL_ERROR_MAPWRITE); | |
382 | } | |
383 | ||
384 | static void | |
385 | record_segment_map (hrtime_t timestamp, uint64_t loadaddr, unsigned long msize, | |
386 | int pagesize, int modeflags, long long offset, | |
387 | unsigned check, char *name) | |
388 | { | |
389 | ||
390 | TprintfT (DBG_LT2, "record_segment_map(%s @ 0x%llx)\n", name, (long long) loadaddr); | |
391 | append_segment_record ("<event kind=\"map\" object=\"segment\" tstamp=\"%u.%09u\" " | |
392 | "vaddr=\"0x%016llX\" size=\"%lu\" pagesz=\"%d\" foffset=\"%c0x%08llX\" " | |
393 | "modes=\"0x%03X\" chksum=\"0x%0X\" name=\"%s\"/>\n", | |
394 | (unsigned) (timestamp / NANOSEC), | |
395 | (unsigned) (timestamp % NANOSEC), | |
396 | loadaddr, msize, pagesize, | |
397 | offset < 0 ? '-' : '+', offset < 0 ? -offset : offset, | |
398 | modeflags, check, name); | |
399 | } | |
400 | ||
401 | static void | |
402 | record_segment_unmap (hrtime_t timestamp, uint64_t loadaddr) | |
403 | { | |
404 | TprintfT (DBG_LT2, "record_segment_unmap(@ 0x%llx)\n", (long long) loadaddr); | |
405 | append_segment_record ("<event kind=\"unmap\" tstamp=\"%u.%09u\" vaddr=\"0x%016llX\"/>\n", | |
406 | (unsigned) (timestamp / NANOSEC), | |
407 | (unsigned) (timestamp % NANOSEC), loadaddr); | |
408 | } | |
409 | ||
410 | #if WSIZE(64) | |
411 | #define ELF_EHDR Elf64_Ehdr | |
412 | #define ELF_PHDR Elf64_Phdr | |
413 | #define ELF_SHDR Elf64_Shdr | |
414 | #define ELF_DYN Elf64_Dyn | |
415 | #define ELF_AUX Elf64_auxv_t | |
416 | #define ELF_SYM Elf64_Sym | |
417 | #define ELF_ST_BIND ELF64_ST_BIND | |
418 | #define ELF_ST_TYPE ELF64_ST_TYPE | |
419 | #elif WSIZE(32) | |
420 | #define ELF_EHDR Elf32_Ehdr | |
421 | #define ELF_PHDR Elf32_Phdr | |
422 | #define ELF_SHDR Elf32_Shdr | |
423 | #define ELF_DYN Elf32_Dyn | |
424 | #define ELF_AUX Elf32_auxv_t | |
425 | #define ELF_SYM Elf32_Sym | |
426 | #define ELF_ST_BIND ELF32_ST_BIND | |
427 | #define ELF_ST_TYPE ELF32_ST_TYPE | |
428 | #endif | |
429 | ||
430 | static unsigned | |
431 | checksum_mapname (MapInfo* map) | |
432 | { | |
433 | unsigned checksum = 0; | |
434 | /* only checksum code segments */ | |
435 | if ((map->mflags & (PROT_EXEC | PROT_READ)) == 0 || | |
436 | (map->mflags & PROT_WRITE) != 0) | |
437 | return 0; | |
438 | checksum = (unsigned) - 1; | |
439 | TprintfT (DBG_LT2, "checksum_mapname checksum = 0x%0X\n", checksum); | |
440 | return checksum; | |
441 | } | |
442 | ||
443 | ||
bb368aad | 444 | static void* |
fe39ffdc VM |
445 | dlopen_searchpath (void*(real_dlopen) (const char *, int), |
446 | void *caller_addr, const char *basename, int mode) | |
bb368aad VM |
447 | { |
448 | TprintfT (DBG_LT2, "dlopen_searchpath(%p, %s, %d)\n", caller_addr, basename, mode); | |
449 | Dl_info dl_info; | |
450 | if (dladdr (caller_addr, &dl_info) == 0) | |
451 | { | |
452 | TprintfT (0, "ERROR: dladdr(%p): %s\n", caller_addr, dlerror ()); | |
453 | return 0; | |
454 | } | |
455 | TprintfT (DBG_LT2, "dladdr(%p): %p fname=%s\n", | |
456 | caller_addr, dl_info.dli_fbase, dl_info.dli_fname); | |
fe39ffdc VM |
457 | int noload = RTLD_LAZY | RTLD_NOW | RTLD_NOLOAD; |
458 | void *caller_hndl = NULL; | |
bb368aad VM |
459 | #define WORKAROUND_RTLD_BUG 1 |
460 | #ifdef WORKAROUND_RTLD_BUG | |
461 | // A dynamic linker dlopen bug can result in corruption/closure of open streams | |
462 | // XXXX workaround should be removed once linker patches are all available | |
463 | #if WSIZE(64) | |
464 | #define MAINBASE 0x400000 | |
465 | #elif WSIZE(32) | |
466 | #define MAINBASE 0x08048000 | |
467 | #endif | |
468 | const char* tmp_path = | |
469 | (dl_info.dli_fbase == (void*) MAINBASE) ? NULL : dl_info.dli_fname; | |
fe39ffdc | 470 | caller_hndl = real_dlopen (tmp_path, noload); |
bb368aad VM |
471 | |
472 | #else //XXXX workaround should be removed once linker patches are all available | |
473 | ||
fe39ffdc | 474 | caller_hndl = real_dlopen (dl_info.dli_fname, noload); |
bb368aad VM |
475 | |
476 | #endif //XXXX workaround should be removed once linker patches are all available | |
477 | ||
478 | if (!caller_hndl) | |
479 | { | |
480 | TprintfT (0, "ERROR: dlopen(%s,NOLOAD): %s\n", dl_info.dli_fname, dlerror ()); | |
fe39ffdc | 481 | return NULL; |
bb368aad | 482 | } |
fe39ffdc | 483 | #if !defined(__MUSL_LIBC) |
bb368aad VM |
484 | Dl_serinfo _info, *info = &_info; |
485 | Dl_serpath *path; | |
486 | ||
487 | /* determine search path count and required buffer size */ | |
488 | dlinfo (caller_hndl, RTLD_DI_SERINFOSIZE, (void *) info); | |
489 | ||
490 | /* allocate new buffer and initialize */ | |
491 | /* | |
492 | CR# 7191331 | |
493 | There is a bug in Linux that causes the first call | |
494 | to dlinfo() to return a small value for the dls_size. | |
495 | ||
496 | The first call to dlinfo() determines the search path | |
497 | count and the required buffer size. The second call to | |
498 | dlinfo() tries to obtain the search path information. | |
499 | ||
500 | However, the size of the buffer that is returned by | |
501 | the first call to the dlinfo() is incorrect (too small). | |
502 | The second call to dlinfo() uses the incorrect size to | |
503 | allocate memory on the stack and internally uses the memcpy() | |
504 | function to copy the search paths to the allocated memory space. | |
505 | The length of the search path is much larger than the buffer | |
506 | that is allocated on the stack. The memcpy() overwrites some | |
507 | of the information that are saved on the stack, specifically, | |
508 | it overwrites the "basename" parameter. | |
509 | ||
510 | collect crashes right after the second call to dlinfo(). | |
511 | ||
512 | The search paths are used to locate the shared libraries. | |
513 | dlinfo() creates the search paths based on the paths | |
514 | that are assigned to LD_LIBRARY_PATH environment variable | |
515 | and the standard library paths. The standard library paths | |
516 | consists of the /lib and the /usr/lib paths. The | |
517 | standard library paths are always included to the search | |
518 | paths by dlinfo() even if the LD_LIBRARY_PATH environment | |
519 | variable is not defined. Therefore, at the very least the | |
520 | dls_cnt is assigned to 2 (/lib and /usr/lib) and dlinfo() | |
521 | will never assign dls_cnt to zero. The dls_cnt is the count | |
522 | of the potential paths for searching the shared libraries. | |
523 | ||
524 | So we need to increase the buffer size before the second | |
525 | call to dlinfo(). There are number of ways to increase | |
526 | the buffer size. However, none of them can calculate the | |
527 | buffer size precisely. Some users on the web have suggested | |
528 | to multiply the MAXPATHLEN by dls_cnt for the buffer size. | |
529 | The MAXPATHLEN is assigned to 1024 bytes. In my opinion | |
530 | this is too much. So I have decided to multiply dls_size | |
531 | by dls_cnt for the buffer size since the dls_size is much | |
532 | smaller than 1024 bytes. | |
533 | ||
534 | I have already confirmed with our user that the workaround | |
535 | is working with his real application. Additionally, | |
536 | the dlopen_searchpath() function is called only by the | |
fe39ffdc | 537 | libcollector init() function when the experiment is started. |
bb368aad VM |
538 | Therefore, allocating some extra bytes on the stack which |
539 | is local to this routine is harmless. | |
540 | */ | |
541 | ||
542 | info = alloca (_info.dls_size * _info.dls_cnt); | |
543 | info->dls_size = _info.dls_size; | |
544 | info->dls_cnt = _info.dls_cnt; | |
545 | ||
546 | /* obtain search path information */ | |
547 | dlinfo (caller_hndl, RTLD_DI_SERINFO, (void *) info); | |
548 | path = &info->dls_serpath[0]; | |
549 | ||
550 | char pathname[MAXPATHLEN]; | |
551 | for (unsigned int cnt = 1; cnt <= info->dls_cnt; cnt++, path++) | |
552 | { | |
553 | __collector_strlcpy (pathname, path->dls_name, sizeof (pathname)); | |
554 | __collector_strlcat (pathname, "/", sizeof (pathname)); | |
555 | __collector_strlcat (pathname, basename, sizeof (pathname)); | |
556 | void* ret = NULL; | |
557 | #if (ARCH(Intel) && WSIZE(32)) || ARCH(SPARC) | |
558 | ret = (real_dlopen) (pathname, mode); | |
559 | #else | |
560 | ret = CALL_REAL (dlopen)(pathname, mode); | |
561 | #endif | |
562 | TprintfT (DBG_LT2, "try %d/%d: %s = %p\n", cnt, info->dls_cnt, pathname, ret); | |
563 | if (ret) | |
564 | return ret; // success! | |
565 | } | |
fe39ffdc VM |
566 | #endif |
567 | return NULL; | |
bb368aad VM |
568 | } |
569 | ||
570 | static void | |
571 | resolve_mapname (MapInfo *map, char *name) | |
572 | { | |
573 | map->filename = ""; | |
574 | map->mapname = ""; | |
575 | if (name == NULL || *name == '\0') | |
576 | { | |
577 | if (map->mflags & MA_STACK) | |
578 | map->filename = "<" SP_MAP_STACK ">"; | |
579 | else if (map->mflags & MA_BREAK) | |
580 | map->filename = "<" SP_MAP_HEAP ">"; | |
581 | else if (map->mflags & MA_ISM) | |
582 | map->filename = "<" SP_MAP_SHMEM ">"; | |
583 | return; | |
584 | } | |
585 | NameInfo *np; | |
586 | for (np = namemaps; np; np = np->next) | |
587 | if (__collector_strcmp (np->mapname, name) == 0) | |
588 | break; | |
589 | ||
590 | if (np == NULL) | |
591 | { | |
592 | const char *fname; | |
593 | fname = name; | |
594 | /* Create and link a new name map */ | |
595 | size_t fnamelen = __collector_strlen (fname) + 1; | |
596 | np = (NameInfo*) __collector_allocCSize (__collector_heap, sizeof (NameInfo) + fnamelen, 1); | |
597 | if (np == NULL) // We could not get memory | |
598 | return; | |
599 | np->mapname = np->filename; | |
600 | __collector_strlcpy (np->filename, fname, fnamelen); | |
601 | np->next = namemaps; | |
602 | namemaps = np; | |
603 | } | |
604 | map->mapname = np->mapname; | |
605 | map->filename = np->filename; | |
606 | if (map->filename[0] == (char) 0) | |
607 | map->filename = map->mapname; | |
608 | TprintfT (DBG_LT2, "resolve_mapname: %s resolved to %s\n", map->mapname, map->filename); | |
609 | } | |
610 | ||
611 | static unsigned long | |
612 | str2ulong (char **ss) | |
613 | { | |
614 | char *s = *ss; | |
615 | unsigned long val = 0UL; | |
616 | const int base = 16; | |
617 | for (;;) | |
618 | { | |
619 | char c = *s++; | |
620 | if (c >= '0' && c <= '9') | |
621 | val = val * base + (c - '0'); | |
622 | else if (c >= 'a' && c <= 'f') | |
623 | val = val * base + (c - 'a') + 10; | |
624 | else if (c >= 'A' && c <= 'F') | |
625 | val = val * base + (c - 'A') + 10; | |
626 | else | |
627 | break; | |
628 | } | |
629 | *ss = s - 1; | |
630 | return val; | |
631 | } | |
632 | ||
633 | static void | |
634 | update_map_segments (hrtime_t hrt, int resolve) | |
635 | { | |
636 | size_t filesz; | |
637 | if (__collector_mutex_trylock (&map_lock)) | |
638 | { | |
639 | TprintfT (0, "WARNING: update_map_segments(resolve=%d) BUSY\n", resolve); | |
640 | return; | |
641 | } | |
642 | TprintfT (DBG_LT2, "\n"); | |
643 | TprintfT (DBG_LT2, "begin update_map_segments(hrt, %d)\n", resolve); | |
644 | ||
645 | // Note: there is similar code to read /proc/$PID/map[s] in | |
646 | // perfan/er_kernel/src/KSubExp.cc KSubExp::write_subexpt_map() | |
647 | const char* proc_map = "/proc/self/maps"; | |
648 | size_t bufsz = maptext_sz; | |
649 | int done = 0; | |
650 | filesz = 0; | |
651 | int map_fd = CALL_UTIL (open)(proc_map, O_RDONLY); | |
652 | while (!done) | |
653 | { | |
654 | bufsz *= 2; | |
655 | maptext = __collector_reallocVSize (__collector_heap, maptext, bufsz); | |
656 | TprintfT (DBG_LT2, " update_map_segments: Loop for bufsize=%ld\n", | |
657 | (long) bufsz); | |
658 | for (;;) | |
659 | { | |
660 | int n = CALL_UTIL (read)(map_fd, maptext + filesz, bufsz - filesz); | |
661 | TprintfT (DBG_LT2, " update_map_segments: __collector_read(bufp=%p nbyte=%ld)=%d\n", | |
662 | maptext + filesz, (long) ( bufsz - filesz), n); | |
663 | if (n < 0) | |
664 | { | |
665 | TprintfT (0, "ERROR: update_map_segments: read(maps): errno=%d\n", errno); | |
666 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", | |
667 | SP_JCMD_CERROR, COL_ERROR_MAPREAD, errno, proc_map); | |
668 | CALL_UTIL (close)(map_fd); | |
669 | __collector_mutex_unlock (&map_lock); | |
670 | return; | |
671 | } | |
672 | else if (n == 0) | |
673 | { | |
674 | done = 1; | |
675 | break; | |
676 | } | |
677 | filesz += n; | |
678 | if (filesz >= bufsz) /* Buffer too small */ | |
679 | break; | |
680 | } | |
681 | } | |
682 | CALL_UTIL (close)(map_fd); | |
683 | maptext_sz = filesz; | |
684 | ||
685 | int mapcache_entries = 0; | |
686 | char *str, *str1; | |
687 | for (str = maptext;; str = str1) | |
688 | { | |
689 | for (str1 = str; str1 - maptext < filesz; str1++) | |
690 | { | |
691 | if (*str1 == '\n') | |
692 | { | |
693 | *str1 = (char) 0; | |
694 | break; | |
695 | } | |
696 | } | |
697 | if (str1 - maptext >= filesz) | |
698 | break; | |
699 | str1++; | |
700 | mapcache_entries++; | |
701 | mapcache = __collector_reallocVSize (__collector_heap, mapcache, | |
702 | sizeof (prmap_t) * mapcache_entries); | |
703 | prmap_t *map = ((prmap_t *) mapcache) + (mapcache_entries - 1); | |
704 | map->pr_vaddr = str2ulong (&str); | |
705 | str++; | |
706 | unsigned long eaddr = str2ulong (&str); | |
707 | str++; | |
708 | map->pr_size = eaddr - map->pr_vaddr; | |
709 | map->pr_mflags = 0; | |
710 | map->pr_mflags += (*str++ == 'r' ? PROT_READ : 0); | |
711 | map->pr_mflags += (*str++ == 'w' ? PROT_WRITE : 0); | |
712 | map->pr_mflags += (*str++ == 'x' ? PROT_EXEC : 0); | |
713 | map->pr_mflags += (*str++ == 's' ? MA_SHARED : 0); | |
714 | str++; | |
715 | map->pr_offset = str2ulong (&str); | |
716 | str++; | |
717 | map->pr_dev = str2ulong (&str) * 0x100; | |
718 | str++; | |
719 | map->pr_dev += str2ulong (&str); | |
720 | str++; | |
721 | map->pr_ino = str2ulong (&str); | |
722 | if (map->pr_dev == 0) | |
723 | map->pr_mflags |= MA_ANON; | |
724 | while (*str == ' ') | |
725 | str++; | |
726 | map->pr_mapname = str; | |
727 | map->pr_pagesize = 4096; | |
728 | } | |
729 | ||
730 | /* Compare two maps and record all differences */ | |
731 | unsigned nidx = 0; | |
732 | MapInfo *prev = &mmaps; | |
733 | MapInfo *oldp = mmaps.next; | |
734 | for (;;) | |
735 | { | |
736 | prmap_t *newp = nidx < mapcache_entries ? | |
737 | (prmap_t*) mapcache + nidx : NULL; | |
738 | if (oldp == NULL && newp == NULL) | |
739 | break; | |
740 | ||
741 | /* If two maps are equal proceed to the next pair */ | |
742 | if (oldp && newp && | |
743 | oldp->vaddr == newp->pr_vaddr && | |
744 | oldp->size == newp->pr_size && | |
745 | __collector_strcmp (oldp->mapname, newp->pr_mapname) == 0) | |
746 | { | |
747 | prev = oldp; | |
748 | oldp = oldp->next; | |
749 | nidx++; | |
750 | continue; | |
751 | } | |
752 | /* Check if we need to unload the old map first */ | |
753 | if (newp == NULL || (oldp && oldp->vaddr <= newp->pr_vaddr)) | |
754 | { | |
755 | if (oldp != NULL) | |
756 | { | |
757 | /* Don't record MA_ANON maps except MA_STACK and MA_BREAK */ | |
758 | if ((!(oldp->mflags & MA_ANON) || (oldp->mflags & (MA_STACK | MA_BREAK)))) | |
759 | record_segment_unmap (hrt, oldp->vaddr); | |
760 | /* Remove and free map */ | |
761 | prev->next = oldp->next; | |
762 | MapInfo *tmp = oldp; | |
763 | oldp = oldp->next; | |
764 | __collector_freeCSize (__collector_heap, tmp, sizeof (*tmp)); | |
765 | } | |
766 | } | |
767 | else | |
768 | { | |
769 | MapInfo *map = (MapInfo*) __collector_allocCSize (__collector_heap, sizeof (MapInfo), 1); | |
770 | if (map == NULL) | |
771 | { | |
772 | __collector_mutex_unlock (&map_lock); | |
773 | return; | |
774 | } | |
775 | map->vaddr = newp->pr_vaddr; | |
776 | map->size = newp->pr_size; | |
777 | map->offset = newp->pr_offset; | |
778 | map->mflags = newp->pr_mflags; | |
779 | map->pagesize = newp->pr_pagesize; | |
780 | resolve_mapname (map, newp->pr_mapname); | |
781 | ||
782 | /* Insert new map */ | |
783 | map->next = prev->next; | |
784 | prev->next = map; | |
785 | prev = map; | |
786 | ||
787 | /* Don't record MA_ANON maps except MA_STACK and MA_BREAK */ | |
788 | if (!(newp->pr_mflags & MA_ANON) || (newp->pr_mflags & (MA_STACK | MA_BREAK))) | |
789 | { | |
790 | unsigned checksum = checksum_mapname (map); | |
791 | record_segment_map (hrt, map->vaddr, map->size, | |
792 | map->pagesize, map->mflags, | |
793 | map->offset, checksum, map->filename); | |
794 | } | |
795 | nidx++; | |
796 | } | |
797 | } | |
798 | TprintfT (DBG_LT2, "update_map_segments: done\n\n"); | |
799 | __collector_mutex_unlock (&map_lock); | |
800 | } /* update_map_segments */ | |
801 | ||
802 | /* | |
803 | * Map addr to a segment. Cope with split segments. | |
804 | */ | |
805 | int | |
806 | __collector_check_segment_internal (unsigned long addr, unsigned long *base, | |
807 | unsigned long *end, int maxnretries, int MA_FLAGS) | |
808 | { | |
809 | int number_of_tries = 0; | |
810 | retry: | |
811 | ; | |
812 | ||
813 | unsigned long curbase = 0; | |
814 | unsigned long curfoff = 0; | |
815 | unsigned long cursize = 0; | |
816 | ||
817 | MapInfo *mp; | |
818 | for (mp = mmaps.next; mp; mp = mp->next) | |
819 | { | |
820 | ||
821 | if (curbase + cursize == mp->vaddr && | |
822 | curfoff + cursize == mp->offset && | |
823 | ((mp->mflags & MA_FLAGS) == MA_FLAGS | |
824 | || __collector_strncmp (mp->mapname, "[vdso]", 6) == 0 | |
825 | || __collector_strncmp (mp->mapname, "[vsyscall]", 10) == 0 | |
826 | )) | |
827 | cursize = mp->vaddr + mp->size - curbase; | |
828 | else if (addr < mp->vaddr) | |
829 | break; | |
830 | else if ((mp->mflags & MA_FLAGS) != MA_FLAGS | |
831 | && __collector_strncmp (mp->mapname, "[vdso]", 6) | |
832 | && __collector_strncmp (mp->mapname, "[vsyscall]", 10)) | |
833 | { | |
834 | curbase = 0; | |
835 | curfoff = 0; | |
836 | cursize = 0; | |
837 | } | |
838 | else | |
839 | { | |
840 | curbase = mp->vaddr; | |
841 | curfoff = mp->offset; | |
842 | cursize = mp->size; | |
843 | } | |
844 | } | |
845 | ||
846 | if (addr >= curbase && addr < curbase + cursize) | |
847 | { | |
848 | *base = curbase; | |
849 | *end = curbase + cursize; | |
850 | return 1; | |
851 | } | |
852 | ||
853 | /* | |
854 | * 21275311 Unwind failure in native stack for java application running on jdk8 on x86 | |
855 | * | |
856 | * On JDK8, we've observed cases where Java-compiled methods end up | |
857 | * in virtual address segments that were "dead zones" (mflags&PROT_READ==0) at | |
858 | * the time of the last update_map_segments() but are now "live". So if we | |
859 | * fail to find a segment, let's call update_map_segments and then retry | |
860 | * before giving up. | |
861 | */ | |
862 | if (number_of_tries < maxnretries) | |
863 | { | |
864 | number_of_tries++; | |
865 | __collector_ext_update_map_segments (); | |
866 | goto retry; | |
867 | } | |
868 | *base = 0; | |
869 | *end = 0; | |
870 | return 0; | |
871 | } | |
872 | ||
873 | /** | |
874 | * Check if address belongs to a readable and executable segment | |
875 | * @param addr | |
876 | * @param base | |
877 | * @param end | |
878 | * @param maxnretries | |
879 | * @return 1 - yes, 0 - no | |
880 | */ | |
881 | int | |
882 | __collector_check_segment (unsigned long addr, unsigned long *base, | |
883 | unsigned long *end, int maxnretries) | |
884 | { | |
885 | int MA_FLAGS = PROT_READ | PROT_EXEC; | |
886 | int res = __collector_check_segment_internal (addr, base, end, maxnretries, MA_FLAGS); | |
887 | return res; | |
888 | } | |
889 | ||
890 | /** | |
891 | * Check if address belongs to a readable segment | |
892 | * @param addr | |
893 | * @param base | |
894 | * @param end | |
895 | * @param maxnretries | |
896 | * @return 1 - yes, 0 - no | |
897 | */ | |
898 | int | |
899 | __collector_check_readable_segment( unsigned long addr, unsigned long *base, unsigned long *end, int maxnretries ) | |
900 | { | |
901 | int MA_FLAGS = PROT_READ; | |
902 | int res = __collector_check_segment_internal(addr, base, end, maxnretries, MA_FLAGS); | |
903 | return res; | |
904 | } | |
905 | ||
906 | static ELF_AUX *auxv = NULL; | |
907 | ||
908 | static void | |
909 | process_vsyscall_page () | |
910 | { | |
911 | TprintfT (DBG_LT2, "process_vsyscall_page()\n"); | |
912 | if (ndyn != 0) | |
913 | { | |
914 | /* We've done this one in this process, and cached the results */ | |
915 | /* use the cached results */ | |
916 | for (int i = 0; i < ndyn; i++) | |
917 | { | |
918 | append_segment_record ("<event kind=\"map\" object=\"dynfunc\" name=\"%s\" " | |
919 | "vaddr=\"0x%016lX\" size=\"%u\" funcname=\"%s\" />\n", | |
920 | dynname[i], dynvaddr[i], dynsize[i], dynfuncname[i]); | |
921 | TprintfT (DBG_LT2, "process_vsyscall_page: append_segment_record map dynfunc='%s' vaddr=0x%016lX size=%ld funcname='%s' -- from cache\n", | |
922 | dynname[i], (unsigned long) dynvaddr[i], | |
923 | (long) dynsize[i], dynfuncname[i]); | |
924 | } | |
925 | } | |
926 | if (nvsysfuncs != 0) | |
927 | { | |
928 | /* We've done this one in this process, and cached the results */ | |
929 | /* use the cached results */ | |
930 | hrtime_t hrt = GETRELTIME (); | |
931 | for (int i = 0; i < nvsysfuncs; i++) | |
932 | { | |
933 | append_segment_record ("<event kind=\"map\" object=\"function\" tstamp=\"%u.%09u\" " | |
934 | "vaddr=\"0x%016lX\" size=\"%u\" name=\"%s\" />\n", | |
935 | (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), | |
936 | (unsigned long) sysfuncvaddr[i], (unsigned) sysfuncsize[i], sysfuncname[i]); | |
937 | TprintfT (DBG_LT2, "process_vsyscall_page: append_segment_record map function='%s' vaddr=0x%016lX size=%ld -- from cache\n", | |
938 | sysfuncname[i], (unsigned long) sysfuncvaddr[i], (long) sysfuncsize[i]); | |
939 | } | |
940 | } | |
941 | if (ndyn + nvsysfuncs != 0) | |
942 | return; | |
943 | ||
944 | /* After fork we can't rely on environ as it might have | |
945 | * been moved by putenv(). Use the pointer saved by the parent. | |
946 | */ | |
947 | if (auxv == NULL) | |
948 | { | |
949 | char **envp = (char**) environ; | |
950 | if (envp == NULL) | |
951 | return; | |
952 | while (*envp++ != NULL); | |
953 | auxv = (ELF_AUX*) envp; | |
954 | } | |
955 | TprintfT (DBG_LT2, "process_vsyscall_page, auxv = ox%p\n", auxv); | |
956 | ||
957 | ELF_AUX *ap; | |
958 | #ifdef DEBUG | |
959 | for (ap = auxv; ap->a_type != AT_NULL; ap++) | |
960 | TprintfT (DBG_LT2, "process_vsyscall_page: ELF_AUX: " | |
961 | " a_type = 0x%016llx %10lld " | |
962 | " a_un.a_val = 0x%016llx %10lld\n", | |
963 | (long long) ap->a_type, (long long) ap->a_type, | |
964 | (long long) ap->a_un.a_val, (long long) ap->a_un.a_val); | |
965 | #endif | |
966 | ||
967 | // find the first ELF_AUX of type AT_SYSINFO_EHDR | |
968 | ELF_EHDR *ehdr = NULL; | |
969 | for (ap = auxv; ap->a_type != AT_NULL; ap++) | |
970 | { | |
971 | if (ap->a_type == AT_SYSINFO_EHDR) | |
972 | { | |
973 | // newer Linuxes do not have a_ptr field, they just have a_val | |
2a2cb7cf | 974 | ehdr = (ELF_EHDR*)(intptr_t) ap->a_un.a_val; |
bb368aad VM |
975 | if (ehdr != NULL) |
976 | break; | |
977 | } | |
978 | } | |
979 | ||
980 | // If one is found | |
981 | if (ehdr != NULL) | |
982 | { | |
983 | char *mapName = "SYSINFO_EHDR"; | |
984 | MapInfo *mp; | |
985 | for (mp = mmaps.next; mp; mp = mp->next) | |
986 | { | |
987 | if ((unsigned long) ehdr == mp->vaddr) | |
988 | { | |
989 | mp->mflags |= PROT_EXEC; | |
990 | if (mp->mapname && mp->mapname[0]) | |
991 | mapName = mp->mapname; | |
992 | break; | |
993 | } | |
994 | } | |
995 | ||
996 | // Find the dynsym section and record all symbols | |
997 | char *base = (char*) ehdr; | |
998 | ELF_SHDR *shdr = (ELF_SHDR*) (base + ehdr->e_shoff); | |
999 | int i; | |
1000 | ||
1001 | #if 0 | |
1002 | TprintfT (DBG_LT2, "process_vsyscall_page: ehdr: EI_CLASS=%lld EI_DATA=%lld EI_OSABI=%lld e_type=%lld e_machine=%lld e_version=%lld\n" | |
1003 | " e_entry =0x%016llx %10lld e_phoff =0x%016llx %10lld\n" | |
1004 | " e_shoff =0x%016llx %10lld e_flags =0x%016llx %10lld\n" | |
1005 | " e_ehsize =0x%016llx %10lld e_phentsize =0x%016llx %10lld\n" | |
1006 | " e_phnum =0x%016llx %10lld e_shentsize =0x%016llx %10lld\n" | |
1007 | " e_shnum =0x%016llx %10lld e_shstrndx =0x%016llx %10lld\n", | |
1008 | (long long) ehdr->e_ident[EI_CLASS], (long long) ehdr->e_ident[EI_DATA], (long long) ehdr->e_ident[EI_OSABI], | |
1009 | (long long) ehdr->e_type, (long long) ehdr->e_machine, (long long) ehdr->e_version, | |
1010 | (long long) ehdr->e_entry, (long long) ehdr->e_entry, | |
1011 | (long long) ehdr->e_phoff, (long long) ehdr->e_phoff, | |
1012 | (long long) ehdr->e_shoff, (long long) ehdr->e_shoff, | |
1013 | (long long) ehdr->e_flags, (long long) ehdr->e_flags, | |
1014 | (long long) ehdr->e_ehsize, (long long) ehdr->e_ehsize, | |
1015 | (long long) ehdr->e_phentsize, (long long) ehdr->e_phentsize, | |
1016 | (long long) ehdr->e_phnum, (long long) ehdr->e_phnum, | |
1017 | (long long) ehdr->e_shentsize, (long long) ehdr->e_shentsize, | |
1018 | (long long) ehdr->e_shnum, (long long) ehdr->e_shnum, | |
1019 | (long long) ehdr->e_shstrndx, (long long) ehdr->e_shstrndx); | |
1020 | for (i = 1; i < ehdr->e_shnum; i++) | |
1021 | { | |
1022 | TprintfT (DBG_LT2, "process_vsyscall_page: SECTION=%d sh_name=%lld '%s'\n" | |
1023 | " sh_type =0x%016llx %10lld\n" | |
1024 | " sh_flags =0x%016llx %10lld\n" | |
1025 | " sh_addr =0x%016llx %10lld\n" | |
1026 | " sh_offset =0x%016llx %10lld\n" | |
1027 | " sh_size =0x%016llx %10lld\n" | |
1028 | " sh_link =0x%016llx %10lld\n" | |
1029 | " sh_info =0x%016llx %10lld\n" | |
1030 | " sh_addralign =0x%016llx %10lld\n" | |
1031 | " sh_entsize =0x%016llx %10lld\n", | |
1032 | i, (long long) shdr[i].sh_name, base + shdr[ehdr->e_shstrndx].sh_offset + shdr[i].sh_name, | |
1033 | (long long) shdr[i].sh_type, (long long) shdr[i].sh_type, | |
1034 | (long long) shdr[i].sh_flags, (long long) shdr[i].sh_flags, | |
1035 | (long long) shdr[i].sh_addr, (long long) shdr[i].sh_addr, | |
1036 | (long long) shdr[i].sh_offset, (long long) shdr[i].sh_offset, | |
1037 | (long long) shdr[i].sh_size, (long long) shdr[i].sh_size, | |
1038 | (long long) shdr[i].sh_link, (long long) shdr[i].sh_link, | |
1039 | (long long) shdr[i].sh_info, (long long) shdr[i].sh_info, | |
1040 | (long long) shdr[i].sh_addralign, (long long) shdr[i].sh_addralign, | |
1041 | (long long) shdr[i].sh_entsize, (long long) shdr[i].sh_entsize); | |
1042 | } | |
1043 | #endif | |
1044 | ||
1045 | int dynSec = -1; | |
1046 | for (i = 1; i < ehdr->e_shnum; i++) | |
1047 | if (shdr[i].sh_type == SHT_DYNSYM) | |
1048 | { | |
1049 | dynSec = i; | |
1050 | break; | |
1051 | } | |
1052 | if (dynSec != -1) | |
1053 | { | |
1054 | char *symbase = base + shdr[shdr[dynSec].sh_link].sh_offset; | |
1055 | ELF_SYM *symbols = (ELF_SYM*) (base + shdr[dynSec].sh_offset); | |
1056 | int nextSec = 0; | |
1057 | int n = shdr[dynSec].sh_size / shdr[dynSec].sh_entsize; | |
1058 | for (i = 0; i < n; i++) | |
1059 | { | |
1060 | ELF_SYM *sym = symbols + i; | |
1061 | TprintfT (DBG_LT2, "process_vsyscall_page: symbol=%d st_name=%lld '%s'\n" | |
1062 | " st_size = 0x%016llx %10lld\n" | |
1063 | " st_value = 0x%016llx %10lld\n" | |
1064 | " st_shndx = 0x%016llx %10lld\n" | |
1065 | " st_info = 0x%016llx %10lld\n", | |
1066 | i, (long long) sym->st_name, symbase + sym->st_name, | |
1067 | (long long) sym->st_size, (long long) sym->st_size, | |
1068 | (long long) sym->st_value, (long long) sym->st_value, | |
1069 | (long long) sym->st_shndx, (long long) sym->st_shndx, | |
1070 | (long long) sym->st_info, (long long) sym->st_info); | |
1071 | if (sym->st_shndx <= 0 || sym->st_size <= 0 || | |
1072 | ELF_ST_BIND (sym->st_info) != STB_GLOBAL || ELF_ST_TYPE (sym->st_info) != STT_FUNC) | |
1073 | continue; | |
1074 | if (nextSec == 0) | |
1075 | nextSec = sym->st_shndx; | |
1076 | else if (nextSec > sym->st_shndx) | |
1077 | nextSec = sym->st_shndx; | |
1078 | } | |
1079 | if (nextSec == 0) | |
1080 | ehdr = NULL; | |
1081 | ||
1082 | while (nextSec != 0) | |
1083 | { | |
1084 | int curSec = nextSec; | |
1085 | char *bgn = base + shdr[curSec].sh_offset; | |
1086 | char *end = bgn + shdr[curSec].sh_size; | |
1087 | for (i = 0; i < n; i++) | |
1088 | { | |
1089 | ELF_SYM *sym = symbols + i; | |
1090 | if (sym->st_shndx <= 0 || sym->st_size <= 0 || | |
1091 | ELF_ST_BIND (sym->st_info) != STB_GLOBAL || ELF_ST_TYPE (sym->st_info) != STT_FUNC) | |
1092 | continue; | |
1093 | if (sym->st_shndx > curSec) | |
1094 | { | |
1095 | if (nextSec == curSec) | |
1096 | nextSec = sym->st_shndx; | |
1097 | else if (nextSec > sym->st_shndx) | |
1098 | nextSec = sym->st_shndx; | |
1099 | nextSec = sym->st_shndx; | |
1100 | continue; | |
1101 | } | |
1102 | if (sym->st_shndx != curSec) | |
1103 | continue; | |
1104 | long long st_delta = (sym->st_value >= shdr[sym->st_shndx].sh_addr) ? | |
1105 | (sym->st_value - shdr[sym->st_shndx].sh_addr) : -1; | |
1106 | char *st_value = bgn + st_delta; | |
1107 | if (st_delta >= 0 && st_value + sym->st_size <= end) | |
1108 | { | |
1109 | append_segment_record ("<event kind=\"map\" object=\"dynfunc\" name=\"%s\" " | |
1110 | "vaddr=\"0x%016lX\" size=\"%u\" funcname=\"%s\" />\n", | |
1111 | mapName, (void*) st_value, sym->st_size, symbase + sym->st_name); | |
1112 | ||
1113 | TprintfT (DBG_LT2, "process_vsyscall_page: append_segment_record map dynfunc='%s' vaddr=%016lX size=%ld funcname='%s'\n", | |
1114 | mapName, (unsigned long) st_value, | |
1115 | (long) sym->st_size, symbase + sym->st_name); | |
1116 | ||
1117 | /* now cache this for a subsequent experiment */ | |
1118 | if (ndyn >= MAXDYN) | |
1119 | __collector_log_write ("<event kind=\"%s\" id=\"%d\">MAXDYN=%d</event>\n", | |
1120 | SP_JCMD_CERROR, COL_ERROR_MAPCACHE, MAXDYN); | |
1121 | else | |
1122 | { | |
1123 | dynname [ndyn] = CALL_UTIL (libc_strdup)(mapName); | |
1124 | dynvaddr [ndyn] = (void *) st_value; | |
1125 | dynsize [ndyn] = (unsigned) sym->st_size; | |
1126 | dynfuncname[ndyn] = CALL_UTIL (libc_strdup)(symbase + sym->st_name); | |
1127 | TprintfT (DBG_LT2, "process_vsyscall_page: cached entry %d map function='%s' vaddr=0x%016lX size=%ld '%s'\n", | |
1128 | ndyn, dynname[ndyn], (unsigned long) dynvaddr[ndyn], | |
1129 | (long) dynsize[ndyn], dynfuncname[ndyn]); | |
1130 | ndyn++; | |
1131 | } | |
1132 | } | |
1133 | } | |
1134 | __collector_int_func_load (DFUNC_KERNEL, mapName, NULL, | |
1135 | (void*) (base + shdr[curSec].sh_offset), shdr[curSec].sh_size, 0, NULL); | |
1136 | ||
1137 | /* now cache this function for a subsequent experiment */ | |
1138 | if (nvsysfuncs >= MAXVSYSFUNCS) | |
1139 | __collector_log_write ("<event kind=\"%s\" id=\"%d\">MAXVSYSFUNCS=%d</event>\n", | |
1140 | SP_JCMD_CERROR, COL_ERROR_MAPCACHE, MAXVSYSFUNCS); | |
1141 | else | |
1142 | { | |
1143 | sysfuncname[nvsysfuncs] = CALL_UTIL (libc_strdup)(mapName); | |
1144 | sysfuncvaddr[nvsysfuncs] = (unsigned long) (base + shdr[curSec].sh_offset); | |
1145 | sysfuncsize[nvsysfuncs] = (unsigned long) (shdr[curSec].sh_size); | |
1146 | TprintfT (DBG_LT2, "process_vsyscall_page: cached entry %d map function='%s' vaddr=0x%016lX size=%ld\n", | |
1147 | nvsysfuncs, sysfuncname[nvsysfuncs], | |
1148 | (unsigned long) sysfuncvaddr[nvsysfuncs], | |
1149 | (long) sysfuncsize[nvsysfuncs]); | |
1150 | nvsysfuncs++; | |
1151 | } | |
1152 | TprintfT (DBG_LT2, "process_vsyscall_page: collector_int_func_load='%s' vaddr=0x%016lX size=%ld\n", | |
1153 | mapName, (unsigned long) (base + shdr[curSec].sh_offset), | |
1154 | (long) shdr[curSec].sh_size); | |
1155 | if (curSec == nextSec) | |
1156 | break; | |
1157 | } | |
1158 | } | |
1159 | } | |
1160 | ||
1161 | #if WSIZE(32) | |
1162 | unsigned long vsysaddr = (unsigned long) 0xffffe000; | |
1163 | #elif WSIZE(64) | |
1164 | unsigned long vsysaddr = (unsigned long) 0xffffffffff600000; | |
1165 | #endif | |
1166 | // Make sure the vsyscall map has PROT_EXEC | |
1167 | MapInfo *mp; | |
1168 | for (mp = mmaps.next; mp; mp = mp->next) | |
1169 | { | |
1170 | TprintfT (DBG_LT2, "MapInfo: vaddr=0x%016llx [size=%lld] mflags=0x%llx offset=%lld pagesize=%lld\n" | |
1171 | " mapname='%s' filename='%s'\n", | |
1172 | (unsigned long long) mp->vaddr, (long long) mp->size, | |
1173 | (long long) mp->mflags, (long long) mp->offset, (long long) mp->pagesize, | |
1174 | mp->mapname ? mp->mapname : "NULL", | |
1175 | mp->filename ? mp->filename : "NULL"); | |
1176 | if (vsysaddr == mp->vaddr) | |
1177 | mp->mflags |= PROT_EXEC; | |
1178 | if ((unsigned long) ehdr == (unsigned long) mp->vaddr) | |
1179 | continue; | |
1180 | if (__collector_strncmp (mp->mapname, "[vdso]", 6) == 0 | |
1181 | || __collector_strncmp (mp->mapname, "[vsyscall]", 10) == 0) | |
1182 | { | |
1183 | /* | |
1184 | * On rubbia ( 2.6.9-5.ELsmp #1 SMP 32-bit ) access to ehdr causes SEGV. | |
1185 | * There doesn't seem to be a way to reliably determine the actual presence | |
1186 | * of the page: even when /proc reports it's there it can't be accessed. | |
1187 | * We will have to put up with <Unknown> on some Linuxes until this is resolved. | |
1188 | __collector_int_func_load(DFUNC_KERNEL, mp->mapname, NULL, (void*) mp->vaddr, mp->size, 0, NULL); | |
1189 | */ | |
1190 | hrtime_t hrt = GETRELTIME (); | |
1191 | append_segment_record ( | |
1192 | "<event kind=\"map\" object=\"function\" tstamp=\"%u.%09u\" " | |
1193 | "vaddr=\"0x%016lX\" size=\"%u\" name=\"%s\" />\n", | |
1194 | (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), | |
1195 | (unsigned long) mp->vaddr, (unsigned) mp->size, mp->mapname); | |
1196 | TprintfT (DBG_LT2, "process_vsyscall_page: append_segment_record map function = %s, vaddr = 0x%016lX, size = %u\n", | |
1197 | mp->mapname, (unsigned long) mp->vaddr, (unsigned) mp->size); | |
1198 | ||
1199 | /* now cache this function for a subsequent experiment */ | |
1200 | if (nvsysfuncs >= MAXVSYSFUNCS) | |
1201 | __collector_log_write ("<event kind=\"%s\" id=\"%d\">MAXVSYSFUNCS=%d</event>\n", | |
1202 | SP_JCMD_CERROR, COL_ERROR_MAPCACHE, MAXVSYSFUNCS); | |
1203 | else | |
1204 | { | |
1205 | sysfuncname[nvsysfuncs] = CALL_UTIL (libc_strdup)(mp->mapname); | |
1206 | sysfuncvaddr[nvsysfuncs] = mp->vaddr; | |
1207 | sysfuncsize[nvsysfuncs] = (unsigned long) mp->size; | |
1208 | TprintfT (DBG_LT2, "process_vsyscall_page: cached entry %d map function='%s' vaddr=0x%016lX size=%ld\n", | |
1209 | nvsysfuncs, sysfuncname[nvsysfuncs], | |
1210 | (unsigned long) sysfuncvaddr[nvsysfuncs], | |
1211 | (long) sysfuncsize[nvsysfuncs]); | |
1212 | nvsysfuncs++; | |
1213 | ||
1214 | } | |
1215 | } | |
1216 | } | |
1217 | } | |
1218 | ||
1219 | /* | |
1220 | * collector API for dynamic functions | |
1221 | */ | |
1222 | void collector_func_load () __attribute__ ((weak, alias ("__collector_func_load"))); | |
1223 | void | |
1224 | __collector_func_load (char *name, char *alias, char *sourcename, | |
1225 | void *vaddr, int size, int lntsize, DT_lineno *lntable) | |
1226 | { | |
1227 | __collector_int_func_load (DFUNC_API, name, sourcename, | |
1228 | vaddr, size, lntsize, lntable); | |
1229 | } | |
1230 | ||
1231 | void collector_func_unload () __attribute__ ((weak, alias ("__collector_func_unload"))); | |
1232 | void | |
1233 | __collector_func_unload (void *vaddr) | |
1234 | { | |
1235 | __collector_int_func_unload (DFUNC_API, vaddr); | |
1236 | } | |
1237 | ||
1238 | /* routines for handling dynamic functions */ | |
1239 | static void | |
1240 | rwrite (int fd, void *buf, size_t nbyte) | |
1241 | { | |
1242 | size_t left = nbyte; | |
1243 | size_t res; | |
1244 | char *ptr = (char*) buf; | |
1245 | while (left > 0) | |
1246 | { | |
1247 | res = CALL_UTIL (write)(fd, ptr, left); | |
1248 | if (res == -1) | |
1249 | { | |
1250 | TprintfT (0, "ERROR: rwrite(%s) failed: errno=%d\n", dyntext_fname, errno); | |
1251 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", | |
1252 | SP_JCMD_CERROR, COL_ERROR_DYNWRITE, errno, dyntext_fname); | |
1253 | return; | |
1254 | } | |
1255 | left -= res; | |
1256 | ptr += res; | |
1257 | } | |
1258 | } | |
1259 | ||
1260 | void | |
1261 | __collector_int_func_load (dfunc_mode_t mode, char *name, char *sourcename, | |
1262 | void *vaddr, int size, int lntsize, DT_lineno *lntable) | |
1263 | { | |
1264 | char name_buf[32]; | |
1265 | int slen; | |
1266 | static char pad[16]; | |
1267 | int padn; | |
1268 | if (!mmap_initted) | |
1269 | return; | |
1270 | hrtime_t hrt = GETRELTIME (); | |
1271 | ||
1272 | if (name == NULL) | |
1273 | { | |
1274 | /* generate a name based on vaddr */ | |
1275 | CALL_UTIL (snprintf)(name_buf, sizeof (name_buf), "0x%lx", (unsigned long) vaddr); | |
1276 | name = name_buf; | |
1277 | } | |
1278 | ||
1279 | switch (mode) | |
1280 | { | |
1281 | case DFUNC_API: | |
1282 | case DFUNC_KERNEL: | |
1283 | append_segment_record ("<event kind=\"map\" object=\"function\" tstamp=\"%u.%09u\" " | |
1284 | "vaddr=\"0x%016lX\" size=\"%u\" name=\"%s\" />\n", | |
1285 | (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), | |
1286 | (unsigned long) vaddr, (unsigned) size, name); | |
1287 | break; | |
1288 | case DFUNC_JAVA: | |
1289 | append_segment_record ("<event kind=\"map\" object=\"jcm\" tstamp=\"%u.%09u\" " | |
1290 | "vaddr=\"0x%016lX\" size=\"%u\" methodId=\"%s\" />\n", | |
1291 | (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), | |
1292 | (unsigned long) vaddr, (unsigned) size, name); | |
1293 | break; | |
1294 | default: | |
1295 | return; | |
1296 | } | |
1297 | ||
1298 | /* 21275311 Unwind failure in native stack for java application running on jdk8 on x86 | |
1299 | * Check: | |
1300 | * - function starts in a known segment (base1 != 0) | |
1301 | * - function ends in the same segment (base1==base2 && end1==end2) | |
1302 | * If not, then call update_map_segments(). | |
1303 | */ | |
1304 | unsigned long base1, end1, base2, end2; | |
1305 | __collector_check_segment ((unsigned long) vaddr, &base1, &end1, 0); | |
1306 | if (base1) | |
1307 | __collector_check_segment (((unsigned long) vaddr)+((unsigned long) size), &base2, &end2, 0); | |
1308 | if (base1 == 0 || base1 != base2 || end1 != end2) | |
1309 | __collector_ext_update_map_segments (); | |
1310 | ||
1311 | /* Write a copy of actual code to the "dyntext" file */ | |
1312 | DT_header dt_hdr; | |
1313 | dt_hdr.type = DT_HEADER; | |
1314 | dt_hdr.size = sizeof (dt_hdr); | |
1315 | dt_hdr.time = hrt; | |
1316 | unsigned long t = (unsigned long) vaddr; /* to suppress a warning from gcc */ | |
1317 | dt_hdr.vaddr = (uint64_t) t; | |
1318 | ||
1319 | DT_code dt_code; | |
1320 | dt_code.type = DT_CODE; | |
1321 | void *code = vaddr; | |
1322 | if (vaddr != NULL && size > 0) | |
1323 | { | |
1324 | dt_code.size = sizeof (dt_code) + ((size + 0xf) & ~0xf); | |
1325 | if (mode == DFUNC_KERNEL) | |
1326 | { | |
1327 | /* Some Linuxes don't accept vaddrs from the vsyscall | |
1328 | * page in write(). Make a copy. | |
1329 | */ | |
1330 | code = alloca (size); | |
1331 | __collector_memcpy (code, vaddr, size); | |
1332 | } | |
1333 | } | |
1334 | else | |
1335 | dt_code.size = 0; | |
1336 | ||
1337 | DT_srcfile dt_src; | |
1338 | dt_src.type = DT_SRCFILE; | |
1339 | if (sourcename) | |
1340 | { | |
1341 | slen = CALL_UTIL (strlen)(sourcename) + 1; | |
1342 | dt_src.size = slen ? sizeof (dt_src) + ((slen + 0xf) & ~0xf) : 0; | |
1343 | } | |
1344 | else | |
1345 | { | |
1346 | slen = 0; | |
1347 | dt_src.size = 0; | |
1348 | } | |
1349 | ||
1350 | DT_ltable dt_ltbl; | |
1351 | dt_ltbl.type = DT_LTABLE; | |
1352 | if (lntable != NULL && lntsize > 0) | |
1353 | dt_ltbl.size = sizeof (dt_ltbl) + lntsize * sizeof (DT_lineno); | |
1354 | else | |
1355 | dt_ltbl.size = 0; | |
1356 | ||
1357 | int fd = CALL_UTIL (open)(dyntext_fname, O_RDWR | O_APPEND); | |
1358 | if (fd == -1) | |
1359 | { | |
1360 | TprintfT (0, "ERROR: __collector_int_func_load: open(%s) failed: errno=%d\n", | |
1361 | dyntext_fname, errno); | |
1362 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", | |
1363 | SP_JCMD_CERROR, COL_ERROR_DYNOPEN, errno, dyntext_fname); | |
1364 | return; | |
1365 | } | |
1366 | ||
1367 | /* Lock the whole file */ | |
1368 | __collector_mutex_lock (&dyntext_lock); | |
1369 | rwrite (fd, &dt_hdr, sizeof (dt_hdr)); | |
1370 | if (dt_code.size) | |
1371 | { | |
1372 | padn = dt_code.size - sizeof (dt_code) - size; | |
1373 | rwrite (fd, &dt_code, sizeof (dt_code)); | |
1374 | rwrite (fd, code, size); | |
1375 | rwrite (fd, &pad, padn); | |
1376 | } | |
1377 | if (dt_src.size) | |
1378 | { | |
1379 | padn = dt_src.size - sizeof (dt_src) - slen; | |
1380 | rwrite (fd, &dt_src, sizeof (dt_src)); | |
1381 | rwrite (fd, sourcename, slen); | |
1382 | rwrite (fd, &pad, padn); | |
1383 | } | |
1384 | if (dt_ltbl.size) | |
1385 | { | |
1386 | rwrite (fd, &dt_ltbl, sizeof (dt_ltbl)); | |
1387 | rwrite (fd, lntable, dt_ltbl.size - sizeof (dt_ltbl)); | |
1388 | } | |
1389 | ||
1390 | /* Unlock the file */ | |
1391 | __collector_mutex_unlock( &dyntext_lock ); | |
1392 | CALL_UTIL(close( fd ) ); | |
1393 | } | |
1394 | ||
1395 | void | |
1396 | __collector_int_func_unload (dfunc_mode_t mode, void *vaddr) | |
1397 | { | |
1398 | if (!mmap_initted) | |
1399 | return; | |
1400 | hrtime_t hrt = GETRELTIME (); | |
1401 | if (mode == DFUNC_API) | |
1402 | append_segment_record ("<event kind=\"unmap\" tstamp=\"%u.%09u\" vaddr=\"0x%016lX\"/>\n", | |
1403 | (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), (unsigned long) vaddr); | |
1404 | else if (mode == DFUNC_JAVA) | |
1405 | /* note that the "vaddr" is really a method id, not an address */ | |
1406 | append_segment_record ("<event kind=\"unmap\" tstamp=\"%u.%09u\" methodId=\"0x%016lX\"/>\n", | |
1407 | (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), (unsigned long) vaddr); | |
1408 | else | |
1409 | return; | |
1410 | } | |
1411 | ||
1412 | /* | |
1413 | * int init_mmap_intf() | |
1414 | * Set up interposition (if not already done). | |
1415 | */ | |
1416 | static int | |
1417 | init_mmap_intf () | |
1418 | { | |
1419 | if (__collector_dlsym_guard) | |
1420 | return 1; | |
1421 | void *dlflag; | |
1422 | __real_mmap = (void*(*)(void* addr, size_t len, int prot, int flags, | |
1423 | int fildes, off_t off))dlsym (RTLD_NEXT, SYS_MMAP_NAME); | |
1424 | if (__real_mmap == NULL) | |
1425 | { | |
1426 | ||
1427 | /* We are probably dlopened after libthread/libc, | |
1428 | * try to search in the previously loaded objects | |
1429 | */ | |
1430 | __real_mmap = (void*(*)(void* addr, size_t len, int prot, int flags, | |
1431 | int fildes, off_t off))dlsym (RTLD_DEFAULT, SYS_MMAP_NAME); | |
1432 | if (__real_mmap == NULL) | |
1433 | { | |
1434 | TprintfT (0, "ERROR: collector real mmap not found\n"); | |
1435 | return 1; | |
1436 | } | |
1437 | TprintfT (DBG_LT2, "collector real mmap found with RTLD_DEFAULT\n"); | |
1438 | dlflag = RTLD_DEFAULT; | |
1439 | } | |
1440 | else | |
1441 | { | |
1442 | TprintfT (DBG_LT2, "collector real mmap found with RTLD_NEXT\n"); | |
1443 | dlflag = RTLD_NEXT; | |
1444 | } | |
1445 | ||
1446 | TprintfT (DBG_LT2, "init_mmap_intf() @%p __real_mmap\n", __real_mmap); | |
1447 | __real_mmap64 = (void*(*)(void *, size_t, int, int, int, off64_t)) | |
1448 | dlsym (dlflag, SYS_MMAP64_NAME); | |
1449 | TprintfT (DBG_LT2, "init_mmap_intf() @%p __real_mmap64\n", __real_mmap64); | |
1450 | __real_munmap = (int(*)(void *, size_t)) dlsym (dlflag, SYS_MUNMAP_NAME); | |
1451 | TprintfT (DBG_LT2, "init_mmap_intf() @%p __real_munmap\n", __real_munmap); | |
1452 | ||
1453 | // dlopen/dlmopen/dlclose are in libdl.so | |
1454 | __real_dlopen = (void*(*)(const char *, int)) | |
1455 | dlvsym (dlflag, SYS_DLOPEN_NAME, SYS_DLOPEN_VERSION); | |
1456 | TprintfT (DBG_LT2, "init_mmap_intf() [%s] @%p __real_dlopen\n", | |
1457 | SYS_DLOPEN_VERSION, __real_dlopen); | |
1458 | #if (ARCH(Intel) && WSIZE(32)) || ARCH(SPARC) | |
1459 | __real_dlopen_2_1 = __real_dlopen; | |
1460 | __real_dlopen_2_0 = (void*(*)(const char *, int)) | |
1461 | dlvsym (dlflag, SYS_DLOPEN_NAME, "GLIBC_2.0"); | |
1462 | #endif | |
1463 | ||
1464 | __real_dlclose = (int(*)(void* handle))dlsym (dlflag, SYS_DLCLOSE_NAME); | |
1465 | TprintfT (DBG_LT2, "init_mmap_intf() @%p __real_dlclose\n", __real_dlclose); | |
1466 | TprintfT (DBG_LT2, "init_mmap_intf() done\n"); | |
1467 | ||
1468 | return 0; | |
1469 | } | |
1470 | ||
1471 | /*------------------------------------------------------------- mmap */ | |
1472 | void * | |
1473 | mmap (void *start, size_t length, int prot, int flags, int fd, off_t offset) | |
1474 | { | |
1475 | int err = 0; | |
1476 | if (NULL_PTR (mmap)) | |
1477 | err = init_mmap_intf (); | |
1478 | if (err) | |
1479 | return MAP_FAILED; | |
1480 | ||
1481 | /* hrtime_t hrt = GETRELTIME(); */ | |
1482 | void *ret = CALL_REAL (mmap)(start, length, prot, flags, fd, offset); | |
1483 | ||
1484 | if (!CHCK_REENTRANCE && (ret != MAP_FAILED) && collector_heap_record != NULL) | |
1485 | { | |
1486 | PUSH_REENTRANCE; | |
1487 | /* write a separate record for mmap tracing */ | |
1488 | collector_heap_record (MMAP_TRACE, length, ret); | |
1489 | POP_REENTRANCE; | |
1490 | } | |
1491 | TprintfT (DBG_LT2, "libcollector.mmap(%p, %ld, %d, %d, %d, 0x%lld) = %p\n", | |
1492 | start, (long) length, prot, flags, fd, (long long) offset, ret); | |
1493 | return ret; | |
1494 | } | |
1495 | ||
1496 | /*------------------------------------------------------------- mmap64 */ | |
1497 | #if WSIZE(32) /* mmap64 only defined for non-64-bit */ | |
1498 | ||
1499 | void * | |
1500 | mmap64 (void *start, size_t length, int prot, int flags, int fd, off64_t offset) | |
1501 | { | |
1502 | if (NULL_PTR (mmap64)) | |
1503 | init_mmap_intf (); | |
1504 | ||
1505 | /* hrtime_t hrt = GETRELTIME(); */ | |
1506 | void *ret = CALL_REAL (mmap64)(start, length, prot, flags, fd, offset); | |
1507 | if (!CHCK_REENTRANCE && (ret != MAP_FAILED) && collector_heap_record != NULL) | |
1508 | { | |
1509 | PUSH_REENTRANCE; | |
1510 | /* write a separate record for mmap tracing */ | |
1511 | collector_heap_record (MMAP_TRACE, length, ret); | |
1512 | POP_REENTRANCE; | |
1513 | } | |
1514 | TprintfT (DBG_LT2, "libcollector.mmap64(%p, %ld, %d, %d, %d, 0x%lld) = %p\n", | |
1515 | start, (long) length, prot, flags, fd, (long long) offset, ret); | |
1516 | return ret; | |
1517 | } | |
1518 | #endif /* WSIZE(32) */ | |
1519 | ||
1520 | /*------------------------------------------------------------- munmap */ | |
1521 | int | |
1522 | munmap (void *start, size_t length) | |
1523 | { | |
1524 | if (NULL_PTR (munmap)) | |
1525 | init_mmap_intf (); | |
1526 | ||
1527 | /* hrtime_t hrt = GETRELTIME(); */ | |
1528 | int rc = CALL_REAL (munmap)(start, length); | |
1529 | if (!CHCK_REENTRANCE && (rc == 0) && collector_heap_record != NULL) | |
1530 | { | |
1531 | PUSH_REENTRANCE; | |
1532 | /* write a separate record for mmap tracing */ | |
1533 | collector_heap_record (MUNMAP_TRACE, length, start); | |
1534 | POP_REENTRANCE; | |
1535 | } | |
1536 | TprintfT (DBG_LT2, "libcollector.munmap(%p, %ld) = %d\n", start, (long) length, rc); | |
1537 | return rc; | |
1538 | } | |
1539 | ||
1540 | ||
1541 | /*------------------------------------------------------------- dlopen */ | |
bb368aad | 1542 | static void * |
fe39ffdc VM |
1543 | __collector_dlopen_symver (void*(real_dlopen) (const char *, int), |
1544 | void *caller, const char *pathname, int mode) | |
bb368aad VM |
1545 | { |
1546 | const char * real_pathname = pathname; | |
1547 | char new_pathname[MAXPATHLEN]; | |
1548 | int origin_offset = 0; | |
1549 | TprintfT (DBG_LT2, "dlopen: pathname=%s, mode=%d\n", pathname ? pathname : "NULL", mode); | |
1550 | if (pathname && __collector_strStartWith (pathname, "$ORIGIN/") == 0) | |
1551 | origin_offset = 8; | |
1552 | else if (pathname && __collector_strStartWith (pathname, "${ORIGIN}/") == 0) | |
1553 | origin_offset = 10; | |
1554 | if (origin_offset) | |
1555 | { | |
bb368aad VM |
1556 | Dl_info dl_info; |
1557 | if (caller && dladdr (caller, &dl_info) != 0) | |
1558 | { | |
1559 | TprintfT (DBG_LT2, "dladdr(%p): %p fname=%s\n", | |
1560 | caller, dl_info.dli_fbase, dl_info.dli_fname); | |
1561 | new_pathname[0] = '\0'; | |
1562 | const char *p = __collector_strrchr (dl_info.dli_fname, '/'); | |
1563 | if (p) | |
1564 | __collector_strlcpy (new_pathname, dl_info.dli_fname, | |
1565 | (p - dl_info.dli_fname + 2) < MAXPATHLEN ? (p - dl_info.dli_fname + 2) : MAXPATHLEN); | |
1566 | __collector_strlcat (new_pathname, pathname + origin_offset, MAXPATHLEN - CALL_UTIL (strlen)(new_pathname)); | |
1567 | real_pathname = new_pathname; | |
1568 | } | |
1569 | else | |
1570 | TprintfT (0, "ERROR: dladdr(%p): %s\n", caller, dlerror ()); | |
1571 | } | |
1572 | if (NULL_PTR (dlopen)) | |
1573 | init_mmap_intf (); | |
1574 | TprintfT (DBG_LT2, "libcollector.dlopen(%s,%d) interposing\n", | |
1575 | pathname ? pathname : "", mode); | |
fe39ffdc | 1576 | void *ret = NULL; |
bb368aad VM |
1577 | |
1578 | // set guard for duration of handling dlopen, since want to ensure | |
1579 | // new mappings are resolved after the actual dlopen has occurred | |
1580 | PUSH_REENTRANCE; | |
1581 | hrtime_t hrt = GETRELTIME (); | |
1582 | ||
fe39ffdc VM |
1583 | if (caller && real_pathname && !__collector_strchr (real_pathname, '/')) |
1584 | ret = dlopen_searchpath (real_dlopen, caller, real_pathname, mode); | |
bb368aad VM |
1585 | |
1586 | if (!ret) | |
fe39ffdc | 1587 | ret = real_dlopen (real_pathname, mode); |
bb368aad VM |
1588 | TprintfT (DBG_LT2, "libcollector -- dlopen(%s) returning %p\n", pathname, ret); |
1589 | ||
1590 | /* Don't call update if dlopen failed: preserve dlerror() */ | |
1591 | if (ret && (mmap_mode > 0) && !(mode & RTLD_NOLOAD)) | |
1592 | update_map_segments (hrt, 1); | |
1593 | TprintfT (DBG_LT2, "libcollector -- dlopen(%s) returning %p\n", pathname, ret); | |
1594 | POP_REENTRANCE; | |
1595 | return ret; | |
1596 | } | |
1597 | ||
fe39ffdc VM |
1598 | void * |
1599 | dlopen (const char *pathname, int mode) | |
1600 | { | |
1601 | if (NULL_PTR (dlopen)) | |
1602 | init_mmap_intf (); | |
9c48ba47 | 1603 | void* caller = __builtin_return_address (0); // must be called inside dlopen first layer interposition |
fe39ffdc VM |
1604 | return __collector_dlopen_symver (CALL_REAL (dlopen), caller, pathname, mode); |
1605 | } | |
1606 | ||
1607 | #if !defined(__MUSL_LIBC) && ((ARCH(Intel) && WSIZE(32)) || ARCH(SPARC)) | |
1608 | // map interposed symbol versions | |
1609 | ||
9c48ba47 | 1610 | SYMVER_ATTRIBUTE (__collector_dlopen_2_1, dlopen@GLIBC_2.1) |
fe39ffdc VM |
1611 | void * |
1612 | __collector_dlopen_2_1 (const char *pathname, int mode) | |
1613 | { | |
1614 | if (NULL_PTR (dlopen_2_1)) | |
1615 | init_mmap_intf (); | |
9c48ba47 | 1616 | void *caller = __builtin_return_address (0); // must be called inside dlopen first layer interposition |
fe39ffdc VM |
1617 | return __collector_dlopen_symver (CALL_REAL (dlopen_2_1), caller, pathname, mode); |
1618 | } | |
1619 | ||
1620 | SYMVER_ATTRIBUTE (__collector_dlopen_2_0, dlopen@GLIBC_2.0) | |
1621 | void * | |
1622 | __collector_dlopen_2_0 (const char *pathname, int mode) | |
1623 | { | |
1624 | if (NULL_PTR (dlopen_2_0)) | |
1625 | init_mmap_intf (); | |
9c48ba47 | 1626 | void* caller = __builtin_return_address (0); // must be called inside dlopen first layer interposition |
fe39ffdc VM |
1627 | return __collector_dlopen_symver (CALL_REAL (dlopen_2_0), caller, pathname, mode); |
1628 | } | |
1629 | #endif | |
1630 | ||
bb368aad VM |
1631 | /*------------------------------------------------------------- dlclose */ |
1632 | int | |
1633 | dlclose (void *handle) | |
1634 | { | |
1635 | if (NULL_PTR (dlclose)) | |
1636 | init_mmap_intf (); | |
1637 | TprintfT (DBG_LT2, "__collector_dlclose(%p) entered\n", handle); | |
1638 | hrtime_t hrt = GETRELTIME (); | |
1639 | if (!CHCK_REENTRANCE) | |
1640 | { | |
1641 | PUSH_REENTRANCE; | |
1642 | update_map_segments (hrt, 1); | |
1643 | POP_REENTRANCE; | |
1644 | hrt = GETRELTIME (); | |
1645 | } | |
1646 | int ret = CALL_REAL (dlclose)(handle); | |
1647 | ||
1648 | /* Don't call update if dlclose failed: preserve dlerror() */ | |
1649 | if (!ret && !CHCK_REENTRANCE) | |
1650 | { | |
1651 | PUSH_REENTRANCE; | |
1652 | update_map_segments (hrt, 1); | |
1653 | POP_REENTRANCE; | |
1654 | } | |
1655 | TprintfT (DBG_LT2, "__collector_dlclose(%p) returning %d\n", handle, ret); | |
1656 | return ret; | |
1657 | } |