2 * Copyright (c) 2013 Luca Clementi <luca.clementi@gmail.com>
3 * Copyright (c) 2013-2018 The strace developers.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #ifdef _LARGEFILE64_SOURCE
35 # define fopen_for_input fopen64
37 # define fopen_for_input fopen
40 # define fopen_for_input fopen
43 static unsigned int mmap_cache_generation;
44 static bool use_mmap_cache;
46 extern void mmap_cache_enable(void)
48 use_mmap_cache = true;
51 extern bool mmap_cache_is_enabled(void)
53 return use_mmap_cache;
57 * caching of /proc/ID/maps for each process to speed up stack tracing
59 * The cache must be refreshed after syscalls that affect memory mappings,
60 * e.g. mmap, mprotect, munmap, execve.
63 build_mmap_cache(struct tcb *tcp)
66 struct mmap_cache_t *cache_head = NULL;
67 /* start with a small dynamically-allocated array and then expand it */
68 size_t cur_array_size = 0;
69 char filename[sizeof("/proc/4294967296/maps")];
70 char buffer[PATH_MAX + 80];
72 xsprintf(filename, "/proc/%u/maps", tcp->pid);
73 fp = fopen_for_input(filename, "r");
75 perror_msg("fopen: %s", filename);
79 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
80 struct mmap_cache_t *entry;
81 unsigned long start_addr, end_addr, mmap_offset;
86 char binary_path[sizeof(buffer)];
88 if (sscanf(buffer, "%lx-%lx %c%c%c%c %lx %*x:%*x %*d %[^\n]",
89 &start_addr, &end_addr,
90 &read_bit, &write_bit, &exec_bit, &shared_bit,
91 &mmap_offset, binary_path) != 8)
94 /* skip mappings that have unknown protection */
95 if (!(read_bit == '-' || read_bit == 'r'))
97 if (!(write_bit == '-' || write_bit == 'w'))
99 if (!(exec_bit == '-' || exec_bit == 'x'))
101 if (!(shared_bit == 'p' || shared_bit == 's'))
104 if (end_addr < start_addr) {
105 error_msg("%s: unrecognized file format", filename);
110 * sanity check to make sure that we're storing
111 * non-overlapping regions in ascending order
113 if (tcp->mmap_cache_size > 0) {
114 entry = &cache_head[tcp->mmap_cache_size - 1];
115 if (entry->start_addr == start_addr &&
116 entry->end_addr == end_addr) {
117 /* duplicate entry, e.g. [vsyscall] */
120 if (start_addr <= entry->start_addr ||
121 start_addr < entry->end_addr) {
122 debug_msg("%s: overlapping memory region: "
123 "\"%s\" [%08lx-%08lx] overlaps with "
124 "\"%s\" [%08lx-%08lx]",
125 filename, binary_path, start_addr,
126 end_addr, entry->binary_filename,
127 entry->start_addr, entry->end_addr);
132 if (tcp->mmap_cache_size >= cur_array_size)
133 cache_head = xgrowarray(cache_head, &cur_array_size,
134 sizeof(*cache_head));
136 entry = &cache_head[tcp->mmap_cache_size];
137 entry->start_addr = start_addr;
138 entry->end_addr = end_addr;
139 entry->mmap_offset = mmap_offset;
140 entry->protections = (
142 | ((read_bit == 'r')? MMAP_CACHE_PROT_READABLE : 0)
143 | ((write_bit == 'w')? MMAP_CACHE_PROT_WRITABLE : 0)
144 | ((exec_bit == 'x')? MMAP_CACHE_PROT_EXECUTABLE: 0)
145 | ((shared_bit == 's')? MMAP_CACHE_PROT_SHARED : 0)
147 entry->binary_filename = xstrdup(binary_path);
148 tcp->mmap_cache_size++;
151 tcp->mmap_cache = cache_head;
152 tcp->mmap_cache_generation = mmap_cache_generation;
154 debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p",
155 tcp->mmap_cache_generation,
156 mmap_cache_generation,
157 tcp, tcp->mmap_cache);
160 /* deleting the cache */
162 mmap_cache_delete(struct tcb *tcp, const char *caller)
166 debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s",
167 tcp->mmap_cache_generation,
168 mmap_cache_generation,
169 tcp, tcp->mmap_cache, caller);
171 for (i = 0; i < tcp->mmap_cache_size; i++) {
172 free(tcp->mmap_cache[i].binary_filename);
173 tcp->mmap_cache[i].binary_filename = NULL;
175 free(tcp->mmap_cache);
176 tcp->mmap_cache = NULL;
177 tcp->mmap_cache_size = 0;
180 extern enum mmap_cache_rebuild_result
181 mmap_cache_rebuild_if_invalid(struct tcb *tcp, const char *caller)
183 enum mmap_cache_rebuild_result r = MMAP_CACHE_REBUILD_READY;
184 if ((tcp->mmap_cache_generation != mmap_cache_generation)
186 mmap_cache_delete(tcp, caller);
188 if (!tcp->mmap_cache) {
189 r = MMAP_CACHE_REBUILD_RENEWED;
190 build_mmap_cache(tcp);
193 if (!(tcp->mmap_cache && tcp->mmap_cache_size))
194 r = MMAP_CACHE_REBUILD_NOCACHE;
200 mmap_cache_invalidate(struct tcb *tcp)
202 #if SUPPORTED_PERSONALITIES > 1
203 if (tcp->currpers != DEFAULT_PERSONALITY) {
204 /* disable stack trace */
208 mmap_cache_generation++;
209 debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p",
210 tcp->mmap_cache_generation,
211 mmap_cache_generation,
212 tcp, tcp->mmap_cache);
215 struct mmap_cache_t *
216 mmap_cache_search(struct tcb *tcp, unsigned long ip)
219 int upper = (int) tcp->mmap_cache_size - 1;
221 while (lower <= upper) {
222 struct mmap_cache_t *cur_mmap_cache;
223 int mid = (upper + lower) / 2;
225 cur_mmap_cache = &tcp->mmap_cache[mid];
227 if (ip >= cur_mmap_cache->start_addr &&
228 ip < cur_mmap_cache->end_addr)
229 return cur_mmap_cache;
230 else if (ip < cur_mmap_cache->start_addr)