2 * Copyright (c) 2013 Luca Clementi <luca.clementi@gmail.com>
3 * Copyright (c) 2013-2018 The strace developers.
5 * SPDX-License-Identifier: LGPL-2.1-or-later
11 #include "largefile_wrappers.h"
12 #include "mmap_cache.h"
13 #include "mmap_notify.h"
16 static unsigned int mmap_cache_generation;
19 mmap_cache_invalidate(struct tcb *tcp, void *unused)
21 #if SUPPORTED_PERSONALITIES > 1
22 if (tcp->currpers != DEFAULT_PERSONALITY) {
23 /* disable stack trace */
27 mmap_cache_generation++;
28 debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p",
29 tcp->mmap_cache ? tcp->mmap_cache->generation : 0,
30 mmap_cache_generation, tcp,
31 tcp->mmap_cache ? tcp->mmap_cache->entry : 0);
35 mmap_cache_enable(void)
37 static bool use_mmap_cache;
39 if (!use_mmap_cache) {
40 mmap_notify_register_client(mmap_cache_invalidate, NULL);
41 use_mmap_cache = true;
45 /* deleting the cache */
47 delete_mmap_cache(struct tcb *tcp, const char *caller)
49 debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s",
50 tcp->mmap_cache ? tcp->mmap_cache->generation : 0,
51 mmap_cache_generation, tcp,
52 tcp->mmap_cache ? tcp->mmap_cache->entry : 0, caller);
57 while (tcp->mmap_cache->size) {
58 unsigned int i = --tcp->mmap_cache->size;
59 free(tcp->mmap_cache->entry[i].binary_filename);
60 tcp->mmap_cache->entry[i].binary_filename = NULL;
63 free(tcp->mmap_cache->entry);
64 tcp->mmap_cache->entry = NULL;
66 free(tcp->mmap_cache);
67 tcp->mmap_cache = NULL;
71 * caching of /proc/ID/maps for each process to speed up stack tracing
73 * The cache must be refreshed after syscalls that affect memory mappings,
74 * e.g. mmap, mprotect, munmap, execve.
76 extern enum mmap_cache_rebuild_result
77 mmap_cache_rebuild_if_invalid(struct tcb *tcp, const char *caller)
80 && tcp->mmap_cache->generation != mmap_cache_generation)
81 delete_mmap_cache(tcp, caller);
84 return MMAP_CACHE_REBUILD_READY;
86 char filename[sizeof("/proc/4294967296/maps")];
87 xsprintf(filename, "/proc/%u/maps", tcp->pid);
89 FILE *fp = fopen_stream(filename, "r");
91 perror_msg("fopen: %s", filename);
92 return MMAP_CACHE_REBUILD_NOCACHE;
95 struct mmap_cache_t cache = {
96 .free_fn = delete_mmap_cache,
97 .generation = mmap_cache_generation
100 /* start with a small dynamically-allocated array and then expand it */
101 size_t allocated = 0;
102 char buffer[PATH_MAX + 80];
104 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
105 unsigned long start_addr, end_addr, mmap_offset;
110 unsigned long major, minor;
111 char binary_path[sizeof(buffer)];
113 if (sscanf(buffer, "%lx-%lx %c%c%c%c %lx %lx:%lx %*d %[^\n]",
114 &start_addr, &end_addr,
115 &read_bit, &write_bit, &exec_bit, &shared_bit,
121 /* skip mappings that have unknown protection */
122 if (!(read_bit == '-' || read_bit == 'r'))
124 if (!(write_bit == '-' || write_bit == 'w'))
126 if (!(exec_bit == '-' || exec_bit == 'x'))
128 if (!(shared_bit == 'p' || shared_bit == 's'))
131 if (end_addr < start_addr) {
132 error_msg("%s: unrecognized file format", filename);
136 struct mmap_cache_entry_t *entry;
138 * sanity check to make sure that we're storing
139 * non-overlapping regions in ascending order
141 if (cache.size > 0) {
142 entry = &cache.entry[cache.size - 1];
143 if (entry->start_addr == start_addr &&
144 entry->end_addr == end_addr) {
145 /* duplicate entry, e.g. [vsyscall] */
148 if (start_addr <= entry->start_addr ||
149 start_addr < entry->end_addr) {
150 debug_msg("%s: overlapping memory region: "
151 "\"%s\" [%08lx-%08lx] overlaps with "
152 "\"%s\" [%08lx-%08lx]",
153 filename, binary_path, start_addr,
154 end_addr, entry->binary_filename,
155 entry->start_addr, entry->end_addr);
160 if (cache.size >= allocated)
161 cache.entry = xgrowarray(cache.entry, &allocated,
162 sizeof(*cache.entry));
164 entry = &cache.entry[cache.size];
165 entry->start_addr = start_addr;
166 entry->end_addr = end_addr;
167 entry->mmap_offset = mmap_offset;
168 entry->protections = (
170 | ((read_bit == 'r')? MMAP_CACHE_PROT_READABLE : 0)
171 | ((write_bit == 'w')? MMAP_CACHE_PROT_WRITABLE : 0)
172 | ((exec_bit == 'x')? MMAP_CACHE_PROT_EXECUTABLE: 0)
173 | ((shared_bit == 's')? MMAP_CACHE_PROT_SHARED : 0)
175 entry->major = major;
176 entry->minor = minor;
177 entry->binary_filename = xstrdup(binary_path);
183 return MMAP_CACHE_REBUILD_NOCACHE;
185 tcp->mmap_cache = xmalloc(sizeof(*tcp->mmap_cache));
186 memcpy(tcp->mmap_cache, &cache, sizeof(cache));
188 debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s",
189 tcp->mmap_cache->generation, mmap_cache_generation,
190 tcp, tcp->mmap_cache->entry, caller);
192 return MMAP_CACHE_REBUILD_RENEWED;
195 struct mmap_cache_entry_t *
196 mmap_cache_search(struct tcb *tcp, unsigned long ip)
198 if (!tcp->mmap_cache)
202 int upper = (int) tcp->mmap_cache->size - 1;
204 while (lower <= upper) {
205 int mid = (upper + lower) / 2;
206 struct mmap_cache_entry_t *entry = &tcp->mmap_cache->entry[mid];
208 if (ip >= entry->start_addr &&
209 ip < entry->end_addr)
211 else if (ip < entry->start_addr)
219 struct mmap_cache_entry_t *
220 mmap_cache_search_custom(struct tcb *tcp, mmap_cache_search_fn fn, void *data)
222 for (unsigned int i = 0; i < tcp->mmap_cache->size; i++) {
223 if (fn(tcp->mmap_cache->entry + i, data))
224 return tcp->mmap_cache->entry + i;