florian | 95af4ce | 2015-01-31 00:29:50 +0000 | [diff] [blame] | 1 | /* -*- mode: C; c-basic-offset: 3; -*- */ |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 2 | |
| 3 | /*--------------------------------------------------------------------*/ |
sewardj | 55f9d1a | 2005-04-25 11:11:44 +0000 | [diff] [blame] | 4 | /*--- The address space manager: segment initialisation and ---*/ |
| 5 | /*--- tracking, stack operations ---*/ |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 6 | /*--- ---*/ |
florian | da9420c | 2015-06-25 21:12:44 +0000 | [diff] [blame] | 7 | /*--- Implementation for Linux (and Darwin!) aspacemgr-linux.c ---*/ |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 8 | /*--------------------------------------------------------------------*/ |
| 9 | |
| 10 | /* |
njn | b9c427c | 2004-12-01 14:14:42 +0000 | [diff] [blame] | 11 | This file is part of Valgrind, a dynamic binary instrumentation |
| 12 | framework. |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 13 | |
sewardj | b3a1e4b | 2015-08-21 11:32:26 +0000 | [diff] [blame] | 14 | Copyright (C) 2000-2015 Julian Seward |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 15 | jseward@acm.org |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 16 | |
| 17 | This program is free software; you can redistribute it and/or |
| 18 | modify it under the terms of the GNU General Public License as |
| 19 | published by the Free Software Foundation; either version 2 of the |
| 20 | License, or (at your option) any later version. |
| 21 | |
| 22 | This program is distributed in the hope that it will be useful, but |
| 23 | WITHOUT ANY WARRANTY; without even the implied warranty of |
| 24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 25 | General Public License for more details. |
| 26 | |
| 27 | You should have received a copy of the GNU General Public License |
| 28 | along with this program; if not, write to the Free Software |
| 29 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 30 | 02111-1307, USA. |
| 31 | |
njn25 | e49d8e7 | 2002-09-23 09:36:25 +0000 | [diff] [blame] | 32 | The GNU General Public License is contained in the file COPYING. |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 33 | */ |
| 34 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 35 | #if defined(VGO_linux) || defined(VGO_darwin) || defined(VGO_solaris) |
njn | 8b68b64 | 2009-06-24 00:37:09 +0000 | [diff] [blame] | 36 | |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 37 | /* ************************************************************* |
| 38 | DO NOT INCLUDE ANY OTHER FILES HERE. |
| 39 | ADD NEW INCLUDES ONLY TO priv_aspacemgr.h |
| 40 | AND THEN ONLY AFTER READING DIRE WARNINGS THERE TOO. |
| 41 | ************************************************************* */ |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 42 | |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 43 | #include "priv_aspacemgr.h" |
bart | 870f745 | 2009-12-29 16:56:18 +0000 | [diff] [blame] | 44 | #include "config.h" |
sewardj | 55f9d1a | 2005-04-25 11:11:44 +0000 | [diff] [blame] | 45 | |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 46 | |
sewardj | aa34929 | 2005-10-11 22:06:29 +0000 | [diff] [blame] | 47 | /* Note: many of the exported functions implemented below are |
| 48 | described more fully in comments in pub_core_aspacemgr.h. |
| 49 | */ |
| 50 | |
| 51 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 52 | /*-----------------------------------------------------------------*/ |
| 53 | /*--- ---*/ |
sewardj | a07dce4 | 2005-10-14 19:05:45 +0000 | [diff] [blame] | 54 | /*--- Overview. ---*/ |
| 55 | /*--- ---*/ |
| 56 | /*-----------------------------------------------------------------*/ |
| 57 | |
| 58 | /* Purpose |
| 59 | ~~~~~~~ |
| 60 | The purpose of the address space manager (aspacem) is: |
| 61 | |
| 62 | (1) to record the disposition of all parts of the process' address |
| 63 | space at all times. |
| 64 | |
| 65 | (2) to the extent that it can, influence layout in ways favourable |
| 66 | to our purposes. |
| 67 | |
| 68 | It is important to appreciate that whilst it can and does attempt |
| 69 | to influence layout, and usually succeeds, it isn't possible to |
| 70 | impose absolute control: in the end, the kernel is the final |
| 71 | arbiter, and can always bounce our requests. |
| 72 | |
| 73 | Strategy |
| 74 | ~~~~~~~~ |
| 75 | The strategy is therefore as follows: |
| 76 | |
| 77 | * Track ownership of mappings. Each one can belong either to |
| 78 | Valgrind or to the client. |
| 79 | |
| 80 | * Try to place the client's fixed and hinted mappings at the |
| 81 | requested addresses. Fixed mappings are allowed anywhere except |
| 82 | in areas reserved by Valgrind; the client can trash its own |
| 83 | mappings if it wants. Hinted mappings are allowed providing they |
| 84 | fall entirely in free areas; if not, they will be placed by |
| 85 | aspacem in a free area. |
| 86 | |
| 87 | * Anonymous mappings are allocated so as to keep Valgrind and |
| 88 | client areas widely separated when possible. If address space |
| 89 | runs low, then they may become intermingled: aspacem will attempt |
| 90 | to use all possible space. But under most circumstances lack of |
| 91 | address space is not a problem and so the areas will remain far |
| 92 | apart. |
| 93 | |
| 94 | Searches for client space start at aspacem_cStart and will wrap |
| 95 | around the end of the available space if needed. Searches for |
| 96 | Valgrind space start at aspacem_vStart and will also wrap around. |
| 97 | Because aspacem_cStart is approximately at the start of the |
| 98 | available space and aspacem_vStart is approximately in the |
| 99 | middle, for the most part the client anonymous mappings will be |
| 100 | clustered towards the start of available space, and Valgrind ones |
| 101 | in the middle. |
| 102 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 103 | On Solaris, searches for client space start at (aspacem_vStart - 1) |
| 104 | and for Valgrind space start at (aspacem_maxAddr - 1) and go backwards. |
| 105 | This simulates what kernel does - brk limit grows from bottom and mmap'ed |
| 106 | objects from top. It is in contrary with Linux where data segment |
| 107 | and mmap'ed objects grow from bottom (leading to early data segment |
| 108 | exhaustion for tools which do not use m_replacemalloc). While Linux glibc |
| 109 | can cope with this problem by employing mmap, Solaris libc treats inability |
| 110 | to grow brk limit as a hard failure. |
| 111 | |
sewardj | a07dce4 | 2005-10-14 19:05:45 +0000 | [diff] [blame] | 112 | The available space is delimited by aspacem_minAddr and |
| 113 | aspacem_maxAddr. aspacem is flexible and can operate with these |
| 114 | at any (sane) setting. For 32-bit Linux, aspacem_minAddr is set |
| 115 | to some low-ish value at startup (64M) and aspacem_maxAddr is |
| 116 | derived from the stack pointer at system startup. This seems a |
| 117 | reliable way to establish the initial boundaries. |
philippe | e4d7812 | 2014-04-20 14:20:37 +0000 | [diff] [blame] | 118 | A command line option allows to change the value of aspacem_minAddr, |
| 119 | so as to allow memory hungry applications to use the lowest |
| 120 | part of the memory. |
sewardj | a07dce4 | 2005-10-14 19:05:45 +0000 | [diff] [blame] | 121 | |
| 122 | 64-bit Linux is similar except for the important detail that the |
sewardj | e06727f | 2013-09-19 09:27:05 +0000 | [diff] [blame] | 123 | upper boundary is set to 64G. The reason is so that all |
sewardj | a07dce4 | 2005-10-14 19:05:45 +0000 | [diff] [blame] | 124 | anonymous mappings (basically all client data areas) are kept |
sewardj | e06727f | 2013-09-19 09:27:05 +0000 | [diff] [blame] | 125 | below 64G, since that is the maximum range that memcheck can |
sewardj | a07dce4 | 2005-10-14 19:05:45 +0000 | [diff] [blame] | 126 | track shadow memory using a fast 2-level sparse array. It can go |
sewardj | e06727f | 2013-09-19 09:27:05 +0000 | [diff] [blame] | 127 | beyond that but runs much more slowly. The 64G limit is |
sewardj | a07dce4 | 2005-10-14 19:05:45 +0000 | [diff] [blame] | 128 | arbitrary and is trivially changed. So, with the current |
| 129 | settings, programs on 64-bit Linux will appear to run out of |
sewardj | e06727f | 2013-09-19 09:27:05 +0000 | [diff] [blame] | 130 | address space and presumably fail at the 64G limit. Given the |
| 131 | considerable space overhead of Memcheck, that means you should be |
| 132 | able to memcheckify programs that use up to about 32G natively. |
sewardj | a07dce4 | 2005-10-14 19:05:45 +0000 | [diff] [blame] | 133 | |
| 134 | Note that the aspacem_minAddr/aspacem_maxAddr limits apply only to |
| 135 | anonymous mappings. The client can still do fixed and hinted maps |
| 136 | at any addresses provided they do not overlap Valgrind's segments. |
| 137 | This makes Valgrind able to load prelinked .so's at their requested |
| 138 | addresses on 64-bit platforms, even if they are very high (eg, |
| 139 | 112TB). |
| 140 | |
| 141 | At startup, aspacem establishes the usable limits, and advises |
| 142 | m_main to place the client stack at the top of the range, which on |
| 143 | a 32-bit machine will be just below the real initial stack. One |
| 144 | effect of this is that self-hosting sort-of works, because an inner |
| 145 | valgrind will then place its client's stack just below its own |
| 146 | initial stack. |
| 147 | |
| 148 | The segment array and segment kinds |
| 149 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 150 | The central data structure is the segment array (segments[0 |
| 151 | .. nsegments_used-1]). This covers the entire address space in |
| 152 | order, giving account of every byte of it. Free spaces are |
| 153 | represented explicitly as this makes many operations simpler. |
| 154 | Mergeable adjacent segments are aggressively merged so as to create |
| 155 | a "normalised" representation (preen_nsegments). |
| 156 | |
| 157 | There are 7 (mutually-exclusive) segment kinds, the meaning of |
| 158 | which is important: |
| 159 | |
| 160 | SkFree: a free space, which may be allocated either to Valgrind (V) |
| 161 | or the client (C). |
| 162 | |
| 163 | SkAnonC: an anonymous mapping belonging to C. For these, aspacem |
| 164 | tracks a boolean indicating whether or not is is part of the |
| 165 | client's heap area (can't remember why). |
| 166 | |
| 167 | SkFileC: a file mapping belonging to C. |
| 168 | |
| 169 | SkShmC: a shared memory segment belonging to C. |
| 170 | |
| 171 | SkAnonV: an anonymous mapping belonging to V. These cover all V's |
| 172 | dynamic memory needs, including non-client malloc/free areas, |
| 173 | shadow memory, and the translation cache. |
| 174 | |
| 175 | SkFileV: a file mapping belonging to V. As far as I know these are |
| 176 | only created transiently for the purposes of reading debug info. |
| 177 | |
| 178 | SkResvn: a reservation segment. |
| 179 | |
| 180 | These are mostly straightforward. Reservation segments have some |
| 181 | subtlety, however. |
| 182 | |
| 183 | A reservation segment is unmapped from the kernel's point of view, |
| 184 | but is an area in which aspacem will not create anonymous maps |
| 185 | (either Vs or Cs). The idea is that we will try to keep it clear |
| 186 | when the choice to do so is ours. Reservation segments are |
| 187 | 'invisible' from the client's point of view: it may choose to park |
| 188 | a fixed mapping in the middle of one, and that's just tough -- we |
| 189 | can't do anything about that. From the client's perspective |
| 190 | reservations are semantically equivalent to (although |
| 191 | distinguishable from, if it makes enquiries) free areas. |
| 192 | |
| 193 | Reservations are a primitive mechanism provided for whatever |
| 194 | purposes the rest of the system wants. Currently they are used to |
| 195 | reserve the expansion space into which a growdown stack is |
| 196 | expanded, and into which the data segment is extended. Note, |
| 197 | though, those uses are entirely external to this module, which only |
| 198 | supplies the primitives. |
| 199 | |
| 200 | Reservations may be shrunk in order that an adjoining anonymous |
| 201 | mapping may be extended. This makes dataseg/stack expansion work. |
| 202 | A reservation may not be shrunk below one page. |
| 203 | |
| 204 | The advise/notify concept |
| 205 | ~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 206 | All mmap-related calls must be routed via aspacem. Calling |
| 207 | sys_mmap directly from the rest of the system is very dangerous |
| 208 | because aspacem's data structures will become out of date. |
| 209 | |
| 210 | The fundamental mode of operation of aspacem is to support client |
| 211 | mmaps. Here's what happens (in ML_(generic_PRE_sys_mmap)): |
| 212 | |
| 213 | * m_syswrap intercepts the mmap call. It examines the parameters |
| 214 | and identifies the requested placement constraints. There are |
| 215 | three possibilities: no constraint (MAny), hinted (MHint, "I |
| 216 | prefer X but will accept anything"), and fixed (MFixed, "X or |
| 217 | nothing"). |
| 218 | |
| 219 | * This request is passed to VG_(am_get_advisory). This decides on |
| 220 | a placement as described in detail in Strategy above. It may |
| 221 | also indicate that the map should fail, because it would trash |
| 222 | one of Valgrind's areas, which would probably kill the system. |
| 223 | |
| 224 | * Control returns to the wrapper. If VG_(am_get_advisory) has |
| 225 | declared that the map should fail, then it must be made to do so. |
| 226 | Usually, though, the request is considered acceptable, in which |
| 227 | case an "advised" address is supplied. The advised address |
| 228 | replaces the original address supplied by the client, and |
| 229 | MAP_FIXED is set. |
| 230 | |
| 231 | Note at this point that although aspacem has been asked for |
| 232 | advice on where to place the mapping, no commitment has yet been |
| 233 | made by either it or the kernel. |
| 234 | |
| 235 | * The adjusted request is handed off to the kernel. |
| 236 | |
| 237 | * The kernel's result is examined. If the map succeeded, aspacem |
| 238 | is told of the outcome (VG_(am_notify_client_mmap)), so it can |
| 239 | update its records accordingly. |
| 240 | |
| 241 | This then is the central advise-notify idiom for handling client |
| 242 | mmap/munmap/mprotect/shmat: |
| 243 | |
| 244 | * ask aspacem for an advised placement (or a veto) |
| 245 | |
| 246 | * if not vetoed, hand request to kernel, using the advised placement |
| 247 | |
| 248 | * examine result, and if successful, notify aspacem of the result. |
| 249 | |
| 250 | There are also many convenience functions, eg |
| 251 | VG_(am_mmap_anon_fixed_client), which do both phases entirely within |
| 252 | aspacem. |
| 253 | |
| 254 | To debug all this, a sync-checker is provided. It reads |
| 255 | /proc/self/maps, compares what it sees with aspacem's records, and |
| 256 | complains if there is a difference. --sanity-level=3 runs it before |
| 257 | and after each syscall, which is a powerful, if slow way of finding |
| 258 | buggy syscall wrappers. |
sewardj | 4f3ef1f | 2005-10-15 00:41:50 +0000 | [diff] [blame] | 259 | |
| 260 | Loss of pointercheck |
| 261 | ~~~~~~~~~~~~~~~~~~~~ |
| 262 | Up to and including Valgrind 2.4.1, x86 segmentation was used to |
| 263 | enforce seperation of V and C, so that wild writes by C could not |
| 264 | trash V. This got called "pointercheck". Unfortunately, the new |
| 265 | more flexible memory layout, plus the need to be portable across |
| 266 | different architectures, means doing this in hardware is no longer |
| 267 | viable, and doing it in software is expensive. So at the moment we |
| 268 | don't do it at all. |
sewardj | a07dce4 | 2005-10-14 19:05:45 +0000 | [diff] [blame] | 269 | */ |
| 270 | |
| 271 | |
| 272 | /*-----------------------------------------------------------------*/ |
| 273 | /*--- ---*/ |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 274 | /*--- The Address Space Manager's state. ---*/ |
| 275 | /*--- ---*/ |
| 276 | /*-----------------------------------------------------------------*/ |
sewardj | a449568 | 2002-10-21 07:29:59 +0000 | [diff] [blame] | 277 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 278 | /* ------ start of STATE for the address-space manager ------ */ |
sewardj | 548be6d | 2005-02-16 01:31:37 +0000 | [diff] [blame] | 279 | |
sewardj | f049d1d | 2013-09-19 10:04:59 +0000 | [diff] [blame] | 280 | /* Max number of segments we can track. On Android, virtual address |
| 281 | space is limited, so keep a low limit -- 5000 x sizef(NSegment) is |
| 282 | 360KB. */ |
sewardj | 26ed419 | 2014-11-04 17:44:21 +0000 | [diff] [blame] | 283 | #if defined(VGPV_arm_linux_android) \ |
| 284 | || defined(VGPV_x86_linux_android) \ |
| 285 | || defined(VGPV_mips32_linux_android) \ |
| 286 | || defined(VGPV_arm64_linux_android) |
sewardj | f049d1d | 2013-09-19 10:04:59 +0000 | [diff] [blame] | 287 | # define VG_N_SEGMENTS 5000 |
| 288 | #else |
| 289 | # define VG_N_SEGMENTS 30000 |
| 290 | #endif |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 291 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 292 | /* Array [0 .. nsegments_used-1] of all mappings. */ |
| 293 | /* Sorted by .addr field. */ |
| 294 | /* I: len may not be zero. */ |
| 295 | /* I: overlapping segments are not allowed. */ |
| 296 | /* I: the segments cover the entire address space precisely. */ |
| 297 | /* Each segment can optionally hold an index into the filename table. */ |
| 298 | |
| 299 | static NSegment nsegments[VG_N_SEGMENTS]; |
| 300 | static Int nsegments_used = 0; |
| 301 | |
| 302 | #define Addr_MIN ((Addr)0) |
| 303 | #define Addr_MAX ((Addr)(-1ULL)) |
| 304 | |
| 305 | /* Limits etc */ |
| 306 | |
philippe | e4d7812 | 2014-04-20 14:20:37 +0000 | [diff] [blame] | 307 | |
| 308 | Addr VG_(clo_aspacem_minAddr) |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 309 | #if defined(VGO_linux) |
| 310 | = (Addr) 0x04000000; // 64M |
| 311 | #elif defined(VGO_darwin) |
philippe | e4d7812 | 2014-04-20 14:20:37 +0000 | [diff] [blame] | 312 | # if VG_WORDSIZE == 4 |
| 313 | = (Addr) 0x00001000; |
| 314 | # else |
| 315 | = (Addr) 0x100000000; // 4GB page zero |
| 316 | # endif |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 317 | #elif defined(VGO_solaris) |
| 318 | = (Addr) 0x00100000; // 1MB |
philippe | e4d7812 | 2014-04-20 14:20:37 +0000 | [diff] [blame] | 319 | #else |
philippe | e4d7812 | 2014-04-20 14:20:37 +0000 | [diff] [blame] | 320 | #endif |
| 321 | |
| 322 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 323 | // The smallest address that aspacem will try to allocate |
| 324 | static Addr aspacem_minAddr = 0; |
| 325 | |
| 326 | // The largest address that aspacem will try to allocate |
| 327 | static Addr aspacem_maxAddr = 0; |
| 328 | |
| 329 | // Where aspacem will start looking for client space |
| 330 | static Addr aspacem_cStart = 0; |
| 331 | |
| 332 | // Where aspacem will start looking for Valgrind space |
| 333 | static Addr aspacem_vStart = 0; |
| 334 | |
| 335 | |
| 336 | #define AM_SANITY_CHECK \ |
| 337 | do { \ |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame^] | 338 | if (VG_(clo_sanity_level) >= 3) \ |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 339 | aspacem_assert(VG_(am_do_sync_check) \ |
| 340 | (__PRETTY_FUNCTION__,__FILE__,__LINE__)); \ |
| 341 | } while (0) |
| 342 | |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 343 | /* ------ end of STATE for the address-space manager ------ */ |
| 344 | |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 345 | /* ------ Forwards decls ------ */ |
sewardj | a364d11 | 2008-10-27 01:25:14 +0000 | [diff] [blame] | 346 | inline |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 347 | static Int find_nsegment_idx ( Addr a ); |
| 348 | |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 349 | static void parse_procselfmaps ( |
| 350 | void (*record_mapping)( Addr addr, SizeT len, UInt prot, |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 351 | ULong dev, ULong ino, Off64T offset, |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 352 | const HChar* filename ), |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 353 | void (*record_gap)( Addr addr, SizeT len ) |
| 354 | ); |
| 355 | |
sewardj | 38a21ac | 2010-01-03 10:14:03 +0000 | [diff] [blame] | 356 | /* ----- Hacks to do with the "commpage" on arm-linux ----- */ |
| 357 | /* Not that I have anything against the commpage per se. It's just |
| 358 | that it's not listed in /proc/self/maps, which is a royal PITA -- |
sewardj | f90dcbc | 2010-10-20 15:43:09 +0000 | [diff] [blame] | 359 | we have to fake it up, in parse_procselfmaps. |
| 360 | |
| 361 | But note also bug 254556 comment #2: this is now fixed in newer |
| 362 | kernels -- it is listed as a "[vectors]" entry. Presumably the |
| 363 | fake entry made here duplicates the [vectors] entry, and so, if at |
| 364 | some point in the future, we can stop supporting buggy kernels, |
| 365 | then this kludge can be removed entirely, since the procmap parser |
| 366 | below will read that entry in the normal way. */ |
sewardj | 38a21ac | 2010-01-03 10:14:03 +0000 | [diff] [blame] | 367 | #if defined(VGP_arm_linux) |
| 368 | # define ARM_LINUX_FAKE_COMMPAGE_START 0xFFFF0000 |
sewardj | f90dcbc | 2010-10-20 15:43:09 +0000 | [diff] [blame] | 369 | # define ARM_LINUX_FAKE_COMMPAGE_END1 0xFFFF1000 |
sewardj | 38a21ac | 2010-01-03 10:14:03 +0000 | [diff] [blame] | 370 | #endif |
| 371 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 372 | |
sewardj | 548be6d | 2005-02-16 01:31:37 +0000 | [diff] [blame] | 373 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 374 | /*-----------------------------------------------------------------*/ |
| 375 | /*--- ---*/ |
| 376 | /*--- Displaying the segment array. ---*/ |
| 377 | /*--- ---*/ |
| 378 | /*-----------------------------------------------------------------*/ |
| 379 | |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 380 | static const HChar* show_SegKind ( SegKind sk ) |
sewardj | 548be6d | 2005-02-16 01:31:37 +0000 | [diff] [blame] | 381 | { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 382 | switch (sk) { |
| 383 | case SkFree: return " "; |
| 384 | case SkAnonC: return "anon"; |
| 385 | case SkAnonV: return "ANON"; |
| 386 | case SkFileC: return "file"; |
| 387 | case SkFileV: return "FILE"; |
tom | 1340c35 | 2005-10-04 15:59:54 +0000 | [diff] [blame] | 388 | case SkShmC: return "shm "; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 389 | case SkResvn: return "RSVN"; |
| 390 | default: return "????"; |
| 391 | } |
| 392 | } |
| 393 | |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 394 | static const HChar* show_ShrinkMode ( ShrinkMode sm ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 395 | { |
| 396 | switch (sm) { |
| 397 | case SmLower: return "SmLower"; |
| 398 | case SmUpper: return "SmUpper"; |
| 399 | case SmFixed: return "SmFixed"; |
| 400 | default: return "Sm?????"; |
| 401 | } |
| 402 | } |
| 403 | |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 404 | static void show_len_concisely ( /*OUT*/HChar* buf, Addr start, Addr end ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 405 | { |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 406 | const HChar* fmt; |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 407 | ULong len = ((ULong)end) - ((ULong)start) + 1; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 408 | |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 409 | if (len < 10*1000*1000ULL) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 410 | fmt = "%7llu"; |
| 411 | } |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 412 | else if (len < 999999ULL * (1ULL<<20)) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 413 | fmt = "%6llum"; |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 414 | len >>= 20; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 415 | } |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 416 | else if (len < 999999ULL * (1ULL<<30)) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 417 | fmt = "%6llug"; |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 418 | len >>= 30; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 419 | } |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 420 | else if (len < 999999ULL * (1ULL<<40)) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 421 | fmt = "%6llut"; |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 422 | len >>= 40; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 423 | } |
| 424 | else { |
| 425 | fmt = "%6llue"; |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 426 | len >>= 50; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 427 | } |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 428 | ML_(am_sprintf)(buf, fmt, len); |
sewardj | 548be6d | 2005-02-16 01:31:37 +0000 | [diff] [blame] | 429 | } |
| 430 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 431 | /* Show full details of an NSegment */ |
| 432 | |
florian | a0d557c | 2015-01-30 22:21:16 +0000 | [diff] [blame] | 433 | static void show_nsegment_full ( Int logLevel, Int segNo, const NSegment* seg ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 434 | { |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 435 | HChar len_buf[20]; |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 436 | const HChar* name = ML_(am_get_segname)( seg->fnIdx ); |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 437 | |
florian | 346ee2f | 2015-04-06 21:34:30 +0000 | [diff] [blame] | 438 | if (name == NULL) |
| 439 | name = "(none)"; |
sewardj | 616cf59 | 2005-10-06 03:04:22 +0000 | [diff] [blame] | 440 | |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 441 | show_len_concisely(len_buf, seg->start, seg->end); |
| 442 | |
| 443 | VG_(debugLog)( |
| 444 | logLevel, "aspacem", |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 445 | "%3d: %s %010lx-%010lx %s %c%c%c%c%c %s " |
| 446 | "d=0x%03llx i=%-7llu o=%-7lld (%d,%d) %s\n", |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 447 | segNo, show_SegKind(seg->kind), |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 448 | seg->start, seg->end, len_buf, |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 449 | seg->hasR ? 'r' : '-', seg->hasW ? 'w' : '-', |
| 450 | seg->hasX ? 'x' : '-', seg->hasT ? 'T' : '-', |
| 451 | seg->isCH ? 'H' : '-', |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 452 | show_ShrinkMode(seg->smode), |
philippe | e6c3b43 | 2015-02-05 22:30:57 +0000 | [diff] [blame] | 453 | seg->dev, seg->ino, seg->offset, |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 454 | ML_(am_segname_get_seqnr)(seg->fnIdx), seg->fnIdx, |
florian | bcf1d7f | 2015-01-27 20:46:19 +0000 | [diff] [blame] | 455 | name |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 456 | ); |
| 457 | } |
| 458 | |
| 459 | |
| 460 | /* Show an NSegment in a user-friendly-ish way. */ |
| 461 | |
florian | 3297124 | 2014-10-23 17:47:15 +0000 | [diff] [blame] | 462 | static void show_nsegment ( Int logLevel, Int segNo, const NSegment* seg ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 463 | { |
| 464 | HChar len_buf[20]; |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 465 | show_len_concisely(len_buf, seg->start, seg->end); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 466 | |
| 467 | switch (seg->kind) { |
| 468 | |
| 469 | case SkFree: |
| 470 | VG_(debugLog)( |
| 471 | logLevel, "aspacem", |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 472 | "%3d: %s %010lx-%010lx %s\n", |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 473 | segNo, show_SegKind(seg->kind), |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 474 | seg->start, seg->end, len_buf |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 475 | ); |
| 476 | break; |
| 477 | |
tom | 1340c35 | 2005-10-04 15:59:54 +0000 | [diff] [blame] | 478 | case SkAnonC: case SkAnonV: case SkShmC: |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 479 | VG_(debugLog)( |
| 480 | logLevel, "aspacem", |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 481 | "%3d: %s %010lx-%010lx %s %c%c%c%c%c\n", |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 482 | segNo, show_SegKind(seg->kind), |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 483 | seg->start, seg->end, len_buf, |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 484 | seg->hasR ? 'r' : '-', seg->hasW ? 'w' : '-', |
| 485 | seg->hasX ? 'x' : '-', seg->hasT ? 'T' : '-', |
| 486 | seg->isCH ? 'H' : '-' |
| 487 | ); |
| 488 | break; |
| 489 | |
| 490 | case SkFileC: case SkFileV: |
| 491 | VG_(debugLog)( |
| 492 | logLevel, "aspacem", |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 493 | "%3d: %s %010lx-%010lx %s %c%c%c%c%c d=0x%03llx " |
| 494 | "i=%-7llu o=%-7lld (%d,%d)\n", |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 495 | segNo, show_SegKind(seg->kind), |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 496 | seg->start, seg->end, len_buf, |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 497 | seg->hasR ? 'r' : '-', seg->hasW ? 'w' : '-', |
| 498 | seg->hasX ? 'x' : '-', seg->hasT ? 'T' : '-', |
| 499 | seg->isCH ? 'H' : '-', |
philippe | e6c3b43 | 2015-02-05 22:30:57 +0000 | [diff] [blame] | 500 | seg->dev, seg->ino, seg->offset, |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 501 | ML_(am_segname_get_seqnr)(seg->fnIdx), seg->fnIdx |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 502 | ); |
| 503 | break; |
| 504 | |
| 505 | case SkResvn: |
| 506 | VG_(debugLog)( |
| 507 | logLevel, "aspacem", |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 508 | "%3d: %s %010lx-%010lx %s %c%c%c%c%c %s\n", |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 509 | segNo, show_SegKind(seg->kind), |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 510 | seg->start, seg->end, len_buf, |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 511 | seg->hasR ? 'r' : '-', seg->hasW ? 'w' : '-', |
| 512 | seg->hasX ? 'x' : '-', seg->hasT ? 'T' : '-', |
| 513 | seg->isCH ? 'H' : '-', |
| 514 | show_ShrinkMode(seg->smode) |
| 515 | ); |
| 516 | break; |
| 517 | |
| 518 | default: |
| 519 | VG_(debugLog)( |
| 520 | logLevel, "aspacem", |
| 521 | "%3d: ???? UNKNOWN SEGMENT KIND\n", |
| 522 | segNo |
| 523 | ); |
| 524 | break; |
| 525 | } |
| 526 | } |
| 527 | |
| 528 | /* Print out the segment array (debugging only!). */ |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 529 | void VG_(am_show_nsegments) ( Int logLevel, const HChar* who ) |
sewardj | 548be6d | 2005-02-16 01:31:37 +0000 | [diff] [blame] | 530 | { |
| 531 | Int i; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 532 | VG_(debugLog)(logLevel, "aspacem", |
florian | 346ee2f | 2015-04-06 21:34:30 +0000 | [diff] [blame] | 533 | "<<< SHOW_SEGMENTS: %s (%d segments)\n", |
| 534 | who, nsegments_used); |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 535 | ML_(am_show_segnames)( logLevel, who); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 536 | for (i = 0; i < nsegments_used; i++) |
| 537 | show_nsegment( logLevel, i, &nsegments[i] ); |
| 538 | VG_(debugLog)(logLevel, "aspacem", |
| 539 | ">>>\n"); |
sewardj | 548be6d | 2005-02-16 01:31:37 +0000 | [diff] [blame] | 540 | } |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 541 | |
sewardj | 548be6d | 2005-02-16 01:31:37 +0000 | [diff] [blame] | 542 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 543 | /* Get the filename corresponding to this segment, if known and if it |
florian | 95af4ce | 2015-01-31 00:29:50 +0000 | [diff] [blame] | 544 | has one. */ |
florian | d3166c4 | 2015-01-24 00:02:19 +0000 | [diff] [blame] | 545 | const HChar* VG_(am_get_filename)( NSegment const * seg ) |
sewardj | 548be6d | 2005-02-16 01:31:37 +0000 | [diff] [blame] | 546 | { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 547 | aspacem_assert(seg); |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 548 | return ML_(am_get_segname)( seg->fnIdx ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 549 | } |
sewardj | 548be6d | 2005-02-16 01:31:37 +0000 | [diff] [blame] | 550 | |
florian | ea8a88c | 2015-02-20 14:00:23 +0000 | [diff] [blame] | 551 | /* Collect up the start addresses of segments whose kind matches one of |
| 552 | the kinds specified in kind_mask. |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 553 | The interface is a bit strange in order to avoid potential |
| 554 | segment-creation races caused by dynamic allocation of the result |
| 555 | buffer *starts. |
| 556 | |
| 557 | The function first computes how many entries in the result |
| 558 | buffer *starts will be needed. If this number <= nStarts, |
| 559 | they are placed in starts[0..], and the number is returned. |
| 560 | If nStarts is not large enough, nothing is written to |
| 561 | starts[0..], and the negation of the size is returned. |
| 562 | |
| 563 | Correct use of this function may mean calling it multiple times in |
| 564 | order to establish a suitably-sized buffer. */ |
| 565 | |
florian | ea8a88c | 2015-02-20 14:00:23 +0000 | [diff] [blame] | 566 | Int VG_(am_get_segment_starts)( UInt kind_mask, Addr* starts, Int nStarts ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 567 | { |
| 568 | Int i, j, nSegs; |
| 569 | |
| 570 | /* don't pass dumbass arguments */ |
florian | 7af930d | 2015-01-20 19:02:05 +0000 | [diff] [blame] | 571 | aspacem_assert(nStarts > 0); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 572 | |
| 573 | nSegs = 0; |
| 574 | for (i = 0; i < nsegments_used; i++) { |
florian | ea8a88c | 2015-02-20 14:00:23 +0000 | [diff] [blame] | 575 | if ((nsegments[i].kind & kind_mask) != 0) |
| 576 | nSegs++; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 577 | } |
| 578 | |
| 579 | if (nSegs > nStarts) { |
| 580 | /* The buffer isn't big enough. Tell the caller how big it needs |
| 581 | to be. */ |
| 582 | return -nSegs; |
| 583 | } |
| 584 | |
| 585 | /* There's enough space. So write into the result buffer. */ |
| 586 | aspacem_assert(nSegs <= nStarts); |
| 587 | |
| 588 | j = 0; |
| 589 | for (i = 0; i < nsegments_used; i++) { |
florian | ea8a88c | 2015-02-20 14:00:23 +0000 | [diff] [blame] | 590 | if ((nsegments[i].kind & kind_mask) != 0) |
| 591 | starts[j++] = nsegments[i].start; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 592 | } |
| 593 | |
| 594 | aspacem_assert(j == nSegs); /* this should not fail */ |
| 595 | return nSegs; |
sewardj | 548be6d | 2005-02-16 01:31:37 +0000 | [diff] [blame] | 596 | } |
| 597 | |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 598 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 599 | /*-----------------------------------------------------------------*/ |
| 600 | /*--- ---*/ |
| 601 | /*--- Sanity checking and preening of the segment array. ---*/ |
| 602 | /*--- ---*/ |
| 603 | /*-----------------------------------------------------------------*/ |
| 604 | |
| 605 | /* Check representational invariants for NSegments. */ |
| 606 | |
florian | 3297124 | 2014-10-23 17:47:15 +0000 | [diff] [blame] | 607 | static Bool sane_NSegment ( const NSegment* s ) |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 608 | { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 609 | if (s == NULL) return False; |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 610 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 611 | /* No zero sized segments and no wraparounds. */ |
florian | fb823ae | 2015-02-24 11:23:51 +0000 | [diff] [blame] | 612 | if (s->start > s->end) return False; |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 613 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 614 | /* require page alignment */ |
| 615 | if (!VG_IS_PAGE_ALIGNED(s->start)) return False; |
| 616 | if (!VG_IS_PAGE_ALIGNED(s->end+1)) return False; |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 617 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 618 | switch (s->kind) { |
| 619 | |
| 620 | case SkFree: |
| 621 | return |
| 622 | s->smode == SmFixed |
| 623 | && s->dev == 0 && s->ino == 0 && s->offset == 0 && s->fnIdx == -1 |
| 624 | && !s->hasR && !s->hasW && !s->hasX && !s->hasT |
| 625 | && !s->isCH; |
| 626 | |
tom | 1340c35 | 2005-10-04 15:59:54 +0000 | [diff] [blame] | 627 | case SkAnonC: case SkAnonV: case SkShmC: |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 628 | return |
| 629 | s->smode == SmFixed |
| 630 | && s->dev == 0 && s->ino == 0 && s->offset == 0 && s->fnIdx == -1 |
| 631 | && (s->kind==SkAnonC ? True : !s->isCH); |
| 632 | |
| 633 | case SkFileC: case SkFileV: |
| 634 | return |
| 635 | s->smode == SmFixed |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 636 | && ML_(am_sane_segname)(s->fnIdx) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 637 | && !s->isCH; |
| 638 | |
| 639 | case SkResvn: |
| 640 | return |
| 641 | s->dev == 0 && s->ino == 0 && s->offset == 0 && s->fnIdx == -1 |
| 642 | && !s->hasR && !s->hasW && !s->hasX && !s->hasT |
| 643 | && !s->isCH; |
| 644 | |
| 645 | default: |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 646 | return False; |
| 647 | } |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 648 | } |
| 649 | |
| 650 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 651 | /* Try merging s2 into s1, if possible. If successful, s1 is |
| 652 | modified, and True is returned. Otherwise s1 is unchanged and |
| 653 | False is returned. */ |
| 654 | |
florian | 3297124 | 2014-10-23 17:47:15 +0000 | [diff] [blame] | 655 | static Bool maybe_merge_nsegments ( NSegment* s1, const NSegment* s2 ) |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 656 | { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 657 | if (s1->kind != s2->kind) |
| 658 | return False; |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 659 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 660 | if (s1->end+1 != s2->start) |
| 661 | return False; |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 662 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 663 | /* reject cases which would cause wraparound */ |
| 664 | if (s1->start > s2->end) |
| 665 | return False; |
| 666 | |
| 667 | switch (s1->kind) { |
| 668 | |
| 669 | case SkFree: |
| 670 | s1->end = s2->end; |
| 671 | return True; |
| 672 | |
| 673 | case SkAnonC: case SkAnonV: |
| 674 | if (s1->hasR == s2->hasR && s1->hasW == s2->hasW |
| 675 | && s1->hasX == s2->hasX && s1->isCH == s2->isCH) { |
| 676 | s1->end = s2->end; |
| 677 | s1->hasT |= s2->hasT; |
| 678 | return True; |
| 679 | } |
| 680 | break; |
| 681 | |
| 682 | case SkFileC: case SkFileV: |
| 683 | if (s1->hasR == s2->hasR |
| 684 | && s1->hasW == s2->hasW && s1->hasX == s2->hasX |
| 685 | && s1->dev == s2->dev && s1->ino == s2->ino |
| 686 | && s2->offset == s1->offset |
| 687 | + ((ULong)s2->start) - ((ULong)s1->start) ) { |
| 688 | s1->end = s2->end; |
| 689 | s1->hasT |= s2->hasT; |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 690 | ML_(am_dec_refcount)(s1->fnIdx); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 691 | return True; |
| 692 | } |
| 693 | break; |
| 694 | |
tom | 1340c35 | 2005-10-04 15:59:54 +0000 | [diff] [blame] | 695 | case SkShmC: |
| 696 | return False; |
| 697 | |
sewardj | 613a9f2 | 2006-08-16 01:48:19 +0000 | [diff] [blame] | 698 | case SkResvn: |
| 699 | if (s1->smode == SmFixed && s2->smode == SmFixed) { |
| 700 | s1->end = s2->end; |
| 701 | return True; |
| 702 | } |
| 703 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 704 | default: |
| 705 | break; |
| 706 | |
| 707 | } |
| 708 | |
| 709 | return False; |
| 710 | } |
| 711 | |
| 712 | |
| 713 | /* Sanity-check and canonicalise the segment array (merge mergable |
| 714 | segments). Returns True if any segments were merged. */ |
| 715 | |
| 716 | static Bool preen_nsegments ( void ) |
| 717 | { |
florian | 95af4ce | 2015-01-31 00:29:50 +0000 | [diff] [blame] | 718 | Int i, r, w, nsegments_used_old = nsegments_used; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 719 | |
| 720 | /* Pass 1: check the segment array covers the entire address space |
| 721 | exactly once, and also that each segment is sane. */ |
| 722 | aspacem_assert(nsegments_used > 0); |
| 723 | aspacem_assert(nsegments[0].start == Addr_MIN); |
| 724 | aspacem_assert(nsegments[nsegments_used-1].end == Addr_MAX); |
| 725 | |
| 726 | aspacem_assert(sane_NSegment(&nsegments[0])); |
| 727 | for (i = 1; i < nsegments_used; i++) { |
| 728 | aspacem_assert(sane_NSegment(&nsegments[i])); |
| 729 | aspacem_assert(nsegments[i-1].end+1 == nsegments[i].start); |
| 730 | } |
| 731 | |
| 732 | /* Pass 2: merge as much as possible, using |
| 733 | maybe_merge_segments. */ |
| 734 | w = 0; |
| 735 | for (r = 1; r < nsegments_used; r++) { |
| 736 | if (maybe_merge_nsegments(&nsegments[w], &nsegments[r])) { |
| 737 | /* nothing */ |
| 738 | } else { |
| 739 | w++; |
| 740 | if (w != r) |
| 741 | nsegments[w] = nsegments[r]; |
| 742 | } |
| 743 | } |
| 744 | w++; |
| 745 | aspacem_assert(w > 0 && w <= nsegments_used); |
| 746 | nsegments_used = w; |
| 747 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 748 | return nsegments_used != nsegments_used_old; |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 749 | } |
| 750 | |
| 751 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 752 | /* Check the segment array corresponds with the kernel's view of |
| 753 | memory layout. sync_check_ok returns True if no anomalies were |
| 754 | found, else False. In the latter case the mismatching segments are |
| 755 | displayed. |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 756 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 757 | The general idea is: we get the kernel to show us all its segments |
| 758 | and also the gaps in between. For each such interval, try and find |
| 759 | a sequence of appropriate intervals in our segment array which |
| 760 | cover or more than cover the kernel's interval, and which all have |
| 761 | suitable kinds/permissions etc. |
| 762 | |
| 763 | Although any specific kernel interval is not matched exactly to a |
| 764 | valgrind interval or sequence thereof, eventually any disagreement |
| 765 | on mapping boundaries will be detected. This is because, if for |
| 766 | example valgrind's intervals cover a greater range than the current |
| 767 | kernel interval, it must be the case that a neighbouring free-space |
| 768 | interval belonging to valgrind cannot cover the neighbouring |
| 769 | free-space interval belonging to the kernel. So the disagreement |
| 770 | is detected. |
| 771 | |
| 772 | In other words, we examine each kernel interval in turn, and check |
| 773 | we do not disagree over the range of that interval. Because all of |
| 774 | the address space is examined, any disagreements must eventually be |
| 775 | detected. |
| 776 | */ |
| 777 | |
| 778 | static Bool sync_check_ok = False; |
| 779 | |
| 780 | static void sync_check_mapping_callback ( Addr addr, SizeT len, UInt prot, |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 781 | ULong dev, ULong ino, Off64T offset, |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 782 | const HChar* filename ) |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 783 | { |
sewardj | aa34929 | 2005-10-11 22:06:29 +0000 | [diff] [blame] | 784 | Int iLo, iHi, i; |
florian | 954d6bb | 2015-04-07 20:43:55 +0000 | [diff] [blame] | 785 | Bool sloppyXcheck, sloppyRcheck; |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 786 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 787 | /* If a problem has already been detected, don't continue comparing |
| 788 | segments, so as to avoid flooding the output with error |
| 789 | messages. */ |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 790 | #if !defined(VGO_darwin) |
| 791 | /* GrP fixme not */ |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 792 | if (!sync_check_ok) |
| 793 | return; |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 794 | #endif |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 795 | if (len == 0) |
| 796 | return; |
| 797 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 798 | /* The kernel should not give us wraparounds. */ |
| 799 | aspacem_assert(addr <= addr + len - 1); |
fitzhardinge | 1a4adf0 | 2003-12-22 10:42:59 +0000 | [diff] [blame] | 800 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 801 | iLo = find_nsegment_idx( addr ); |
| 802 | iHi = find_nsegment_idx( addr + len - 1 ); |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 803 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 804 | /* These 5 should be guaranteed by find_nsegment_idx. */ |
| 805 | aspacem_assert(0 <= iLo && iLo < nsegments_used); |
| 806 | aspacem_assert(0 <= iHi && iHi < nsegments_used); |
| 807 | aspacem_assert(iLo <= iHi); |
| 808 | aspacem_assert(nsegments[iLo].start <= addr ); |
| 809 | aspacem_assert(nsegments[iHi].end >= addr + len - 1 ); |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 810 | |
sewardj | aa34929 | 2005-10-11 22:06:29 +0000 | [diff] [blame] | 811 | /* x86 doesn't differentiate 'x' and 'r' (at least, all except the |
tom | c28e3dd | 2005-11-02 14:42:39 +0000 | [diff] [blame] | 812 | most recent NX-bit enabled CPUs) and so recent kernels attempt |
| 813 | to provide execute protection by placing all executable mappings |
| 814 | low down in the address space and then reducing the size of the |
| 815 | code segment to prevent code at higher addresses being executed. |
sewardj | aa34929 | 2005-10-11 22:06:29 +0000 | [diff] [blame] | 816 | |
tom | c28e3dd | 2005-11-02 14:42:39 +0000 | [diff] [blame] | 817 | These kernels report which mappings are really executable in |
| 818 | the /proc/self/maps output rather than mirroring what was asked |
| 819 | for when each mapping was created. In order to cope with this we |
sewardj | b5b8740 | 2011-03-07 16:05:35 +0000 | [diff] [blame] | 820 | have a sloppyXcheck mode which we enable on x86 and s390 - in this |
| 821 | mode we allow the kernel to report execute permission when we weren't |
tom | c28e3dd | 2005-11-02 14:42:39 +0000 | [diff] [blame] | 822 | expecting it but not vice versa. */ |
sewardj | b5b8740 | 2011-03-07 16:05:35 +0000 | [diff] [blame] | 823 | # if defined(VGA_x86) || defined (VGA_s390x) |
tom | c28e3dd | 2005-11-02 14:42:39 +0000 | [diff] [blame] | 824 | sloppyXcheck = True; |
njn | 4c245e5 | 2009-03-15 23:25:38 +0000 | [diff] [blame] | 825 | # else |
| 826 | sloppyXcheck = False; |
tom | c28e3dd | 2005-11-02 14:42:39 +0000 | [diff] [blame] | 827 | # endif |
sewardj | aa34929 | 2005-10-11 22:06:29 +0000 | [diff] [blame] | 828 | |
florian | 954d6bb | 2015-04-07 20:43:55 +0000 | [diff] [blame] | 829 | /* Some kernels on s390 provide 'r' permission even when it was not |
rhyskidd | a6cece9 | 2015-05-01 06:29:51 +0000 | [diff] [blame] | 830 | explicitly requested. It seems that 'x' permission implies 'r'. |
| 831 | This behaviour also occurs on OS X. */ |
| 832 | # if defined(VGA_s390x) || defined(VGO_darwin) |
florian | 954d6bb | 2015-04-07 20:43:55 +0000 | [diff] [blame] | 833 | sloppyRcheck = True; |
| 834 | # else |
| 835 | sloppyRcheck = False; |
| 836 | # endif |
| 837 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 838 | /* NSegments iLo .. iHi inclusive should agree with the presented |
| 839 | data. */ |
| 840 | for (i = iLo; i <= iHi; i++) { |
sewardj | 47c98a7 | 2005-03-12 20:36:15 +0000 | [diff] [blame] | 841 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 842 | Bool same, cmp_offsets, cmp_devino; |
tom | aa55dca | 2005-10-04 13:02:31 +0000 | [diff] [blame] | 843 | UInt seg_prot; |
njn | 6081fce | 2005-06-25 19:45:34 +0000 | [diff] [blame] | 844 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 845 | /* compare the kernel's offering against ours. */ |
| 846 | same = nsegments[i].kind == SkAnonC |
| 847 | || nsegments[i].kind == SkAnonV |
| 848 | || nsegments[i].kind == SkFileC |
tom | 1340c35 | 2005-10-04 15:59:54 +0000 | [diff] [blame] | 849 | || nsegments[i].kind == SkFileV |
| 850 | || nsegments[i].kind == SkShmC; |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 851 | |
tom | aa55dca | 2005-10-04 13:02:31 +0000 | [diff] [blame] | 852 | seg_prot = 0; |
| 853 | if (nsegments[i].hasR) seg_prot |= VKI_PROT_READ; |
| 854 | if (nsegments[i].hasW) seg_prot |= VKI_PROT_WRITE; |
| 855 | if (nsegments[i].hasX) seg_prot |= VKI_PROT_EXEC; |
| 856 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 857 | cmp_offsets |
| 858 | = nsegments[i].kind == SkFileC || nsegments[i].kind == SkFileV; |
sewardj | 616cf59 | 2005-10-06 03:04:22 +0000 | [diff] [blame] | 859 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 860 | cmp_devino |
| 861 | = nsegments[i].dev != 0 || nsegments[i].ino != 0; |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 862 | |
sewardj | 616cf59 | 2005-10-06 03:04:22 +0000 | [diff] [blame] | 863 | /* Consider other reasons to not compare dev/inode */ |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 864 | #if defined(VGO_linux) |
sewardj | 616cf59 | 2005-10-06 03:04:22 +0000 | [diff] [blame] | 865 | /* bproc does some godawful hack on /dev/zero at process |
| 866 | migration, which changes the name of it, and its dev & ino */ |
| 867 | if (filename && 0==VG_(strcmp)(filename, "/dev/zero (deleted)")) |
| 868 | cmp_devino = False; |
| 869 | |
sewardj | f36e99a | 2005-11-13 02:42:23 +0000 | [diff] [blame] | 870 | /* hack apparently needed on MontaVista Linux */ |
| 871 | if (filename && VG_(strstr)(filename, "/.lib-ro/")) |
| 872 | cmp_devino = False; |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 873 | #endif |
sewardj | f36e99a | 2005-11-13 02:42:23 +0000 | [diff] [blame] | 874 | |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 875 | #if defined(VGO_darwin) |
| 876 | // GrP fixme kernel info doesn't have dev/inode |
| 877 | cmp_devino = False; |
| 878 | |
| 879 | // GrP fixme V and kernel don't agree on offsets |
| 880 | cmp_offsets = False; |
| 881 | #endif |
| 882 | |
tom | c28e3dd | 2005-11-02 14:42:39 +0000 | [diff] [blame] | 883 | /* If we are doing sloppy execute permission checks then we |
| 884 | allow segment to have X permission when we weren't expecting |
| 885 | it (but not vice versa) so if the kernel reported execute |
| 886 | permission then pretend that this segment has it regardless |
| 887 | of what we were expecting. */ |
| 888 | if (sloppyXcheck && (prot & VKI_PROT_EXEC) != 0) { |
| 889 | seg_prot |= VKI_PROT_EXEC; |
sewardj | aa34929 | 2005-10-11 22:06:29 +0000 | [diff] [blame] | 890 | } |
| 891 | |
florian | 954d6bb | 2015-04-07 20:43:55 +0000 | [diff] [blame] | 892 | if (sloppyRcheck && (prot & (VKI_PROT_EXEC | VKI_PROT_READ)) == |
| 893 | (VKI_PROT_EXEC | VKI_PROT_READ)) { |
| 894 | seg_prot |= VKI_PROT_READ; |
| 895 | } |
| 896 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 897 | same = same |
tom | aa55dca | 2005-10-04 13:02:31 +0000 | [diff] [blame] | 898 | && seg_prot == prot |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 899 | && (cmp_devino |
| 900 | ? (nsegments[i].dev == dev && nsegments[i].ino == ino) |
| 901 | : True) |
| 902 | && (cmp_offsets |
| 903 | ? nsegments[i].start-nsegments[i].offset == addr-offset |
| 904 | : True); |
| 905 | if (!same) { |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 906 | Addr start = addr; |
| 907 | Addr end = start + len - 1; |
| 908 | HChar len_buf[20]; |
| 909 | show_len_concisely(len_buf, start, end); |
| 910 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 911 | sync_check_ok = False; |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 912 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 913 | VG_(debugLog)( |
| 914 | 0,"aspacem", |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 915 | "segment mismatch: V's seg 1st, kernel's 2nd:\n"); |
| 916 | show_nsegment_full( 0, i, &nsegments[i] ); |
| 917 | VG_(debugLog)(0,"aspacem", |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 918 | "...: .... %010lx-%010lx %s %c%c%c.. ....... " |
| 919 | "d=0x%03llx i=%-7llu o=%-7lld (.) m=. %s\n", |
| 920 | start, end, len_buf, |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 921 | prot & VKI_PROT_READ ? 'r' : '-', |
| 922 | prot & VKI_PROT_WRITE ? 'w' : '-', |
| 923 | prot & VKI_PROT_EXEC ? 'x' : '-', |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 924 | dev, ino, offset, filename ? filename : "(none)" ); |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 925 | |
| 926 | return; |
njn | f6ec8ec | 2005-07-21 23:26:25 +0000 | [diff] [blame] | 927 | } |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 928 | } |
| 929 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 930 | /* Looks harmless. Keep going. */ |
| 931 | return; |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 932 | } |
| 933 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 934 | static void sync_check_gap_callback ( Addr addr, SizeT len ) |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 935 | { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 936 | Int iLo, iHi, i; |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 937 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 938 | /* If a problem has already been detected, don't continue comparing |
| 939 | segments, so as to avoid flooding the output with error |
| 940 | messages. */ |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 941 | #if !defined(VGO_darwin) |
| 942 | /* GrP fixme not */ |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 943 | if (!sync_check_ok) |
| 944 | return; |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 945 | #endif |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 946 | if (len == 0) |
| 947 | return; |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 948 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 949 | /* The kernel should not give us wraparounds. */ |
| 950 | aspacem_assert(addr <= addr + len - 1); |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 951 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 952 | iLo = find_nsegment_idx( addr ); |
| 953 | iHi = find_nsegment_idx( addr + len - 1 ); |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 954 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 955 | /* These 5 should be guaranteed by find_nsegment_idx. */ |
| 956 | aspacem_assert(0 <= iLo && iLo < nsegments_used); |
| 957 | aspacem_assert(0 <= iHi && iHi < nsegments_used); |
| 958 | aspacem_assert(iLo <= iHi); |
| 959 | aspacem_assert(nsegments[iLo].start <= addr ); |
| 960 | aspacem_assert(nsegments[iHi].end >= addr + len - 1 ); |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 961 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 962 | /* NSegments iLo .. iHi inclusive should agree with the presented |
| 963 | data. */ |
| 964 | for (i = iLo; i <= iHi; i++) { |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 965 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 966 | Bool same; |
thughes | 9aaebc3 | 2004-07-15 23:13:37 +0000 | [diff] [blame] | 967 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 968 | /* compare the kernel's offering against ours. */ |
| 969 | same = nsegments[i].kind == SkFree |
| 970 | || nsegments[i].kind == SkResvn; |
| 971 | |
| 972 | if (!same) { |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 973 | Addr start = addr; |
| 974 | Addr end = start + len - 1; |
| 975 | HChar len_buf[20]; |
| 976 | show_len_concisely(len_buf, start, end); |
| 977 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 978 | sync_check_ok = False; |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 979 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 980 | VG_(debugLog)( |
| 981 | 0,"aspacem", |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 982 | "segment mismatch: V's gap 1st, kernel's 2nd:\n"); |
| 983 | show_nsegment_full( 0, i, &nsegments[i] ); |
| 984 | VG_(debugLog)(0,"aspacem", |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 985 | " : .... %010lx-%010lx %s\n", |
| 986 | start, end, len_buf); |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 987 | return; |
thughes | 9aaebc3 | 2004-07-15 23:13:37 +0000 | [diff] [blame] | 988 | } |
thughes | 9aaebc3 | 2004-07-15 23:13:37 +0000 | [diff] [blame] | 989 | } |
| 990 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 991 | /* Looks harmless. Keep going. */ |
| 992 | return; |
sewardj | 548be6d | 2005-02-16 01:31:37 +0000 | [diff] [blame] | 993 | } |
| 994 | |
| 995 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 996 | /* Sanity check: check that Valgrind and the kernel agree on the |
| 997 | address space layout. Prints offending segments and call point if |
| 998 | a discrepancy is detected, but does not abort the system. Returned |
| 999 | Bool is False if a discrepancy was found. */ |
sewardj | b5f6f51 | 2005-03-10 23:59:00 +0000 | [diff] [blame] | 1000 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1001 | Bool VG_(am_do_sync_check) ( const HChar* fn, |
| 1002 | const HChar* file, Int line ) |
jsgf | 855d93d | 2003-10-13 22:26:55 +0000 | [diff] [blame] | 1003 | { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1004 | sync_check_ok = True; |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 1005 | if (0) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1006 | VG_(debugLog)(0,"aspacem", "do_sync_check %s:%d\n", file,line); |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 1007 | parse_procselfmaps( sync_check_mapping_callback, |
| 1008 | sync_check_gap_callback ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1009 | if (!sync_check_ok) { |
| 1010 | VG_(debugLog)(0,"aspacem", |
| 1011 | "sync check at %s:%d (%s): FAILED\n", |
| 1012 | file, line, fn); |
| 1013 | VG_(debugLog)(0,"aspacem", "\n"); |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 1014 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1015 | # if 0 |
| 1016 | { |
florian | 7b7d594 | 2014-12-19 20:29:22 +0000 | [diff] [blame] | 1017 | HChar buf[100]; // large enough |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1018 | VG_(am_show_nsegments)(0,"post syncheck failure"); |
| 1019 | VG_(sprintf)(buf, "/bin/cat /proc/%d/maps", VG_(getpid)()); |
| 1020 | VG_(system)(buf); |
| 1021 | } |
| 1022 | # endif |
sewardj | 79048ce | 2005-02-18 08:28:32 +0000 | [diff] [blame] | 1023 | |
njn | e3f0635 | 2005-06-01 03:48:33 +0000 | [diff] [blame] | 1024 | } |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1025 | return sync_check_ok; |
njn | e3f0635 | 2005-06-01 03:48:33 +0000 | [diff] [blame] | 1026 | } |
| 1027 | |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 1028 | /* Hook to allow sanity checks to be done from aspacemgr-common.c. */ |
| 1029 | void ML_(am_do_sanity_check)( void ) |
| 1030 | { |
| 1031 | AM_SANITY_CHECK; |
| 1032 | } |
| 1033 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1034 | |
| 1035 | /*-----------------------------------------------------------------*/ |
| 1036 | /*--- ---*/ |
| 1037 | /*--- Low level access / modification of the segment array. ---*/ |
| 1038 | /*--- ---*/ |
| 1039 | /*-----------------------------------------------------------------*/ |
| 1040 | |
| 1041 | /* Binary search the interval array for a given address. Since the |
sewardj | a364d11 | 2008-10-27 01:25:14 +0000 | [diff] [blame] | 1042 | array covers the entire address space the search cannot fail. The |
| 1043 | _WRK function does the real work. Its caller (just below) caches |
| 1044 | the results thereof, to save time. With N_CACHE of 63 we get a hit |
| 1045 | rate exceeding 90% when running OpenOffice. |
| 1046 | |
| 1047 | Re ">> 12", it doesn't matter that the page size of some targets |
| 1048 | might be different from 12. Really "(a >> 12) % N_CACHE" is merely |
| 1049 | a hash function, and the actual cache entry is always validated |
| 1050 | correctly against the selected cache entry before use. |
| 1051 | */ |
| 1052 | /* Don't call find_nsegment_idx_WRK; use find_nsegment_idx instead. */ |
| 1053 | __attribute__((noinline)) |
| 1054 | static Int find_nsegment_idx_WRK ( Addr a ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1055 | { |
| 1056 | Addr a_mid_lo, a_mid_hi; |
| 1057 | Int mid, |
| 1058 | lo = 0, |
| 1059 | hi = nsegments_used-1; |
| 1060 | while (True) { |
| 1061 | /* current unsearched space is from lo to hi, inclusive. */ |
| 1062 | if (lo > hi) { |
| 1063 | /* Not found. This can't happen. */ |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 1064 | ML_(am_barf)("find_nsegment_idx: not found"); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1065 | } |
| 1066 | mid = (lo + hi) / 2; |
| 1067 | a_mid_lo = nsegments[mid].start; |
| 1068 | a_mid_hi = nsegments[mid].end; |
| 1069 | |
| 1070 | if (a < a_mid_lo) { hi = mid-1; continue; } |
| 1071 | if (a > a_mid_hi) { lo = mid+1; continue; } |
| 1072 | aspacem_assert(a >= a_mid_lo && a <= a_mid_hi); |
| 1073 | aspacem_assert(0 <= mid && mid < nsegments_used); |
| 1074 | return mid; |
| 1075 | } |
| 1076 | } |
| 1077 | |
sewardj | a364d11 | 2008-10-27 01:25:14 +0000 | [diff] [blame] | 1078 | inline static Int find_nsegment_idx ( Addr a ) |
| 1079 | { |
sewardj | bbfcb26 | 2011-06-07 21:42:07 +0000 | [diff] [blame] | 1080 | # define N_CACHE 131 /*prime*/ |
sewardj | a364d11 | 2008-10-27 01:25:14 +0000 | [diff] [blame] | 1081 | static Addr cache_pageno[N_CACHE]; |
| 1082 | static Int cache_segidx[N_CACHE]; |
| 1083 | static Bool cache_inited = False; |
| 1084 | |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame^] | 1085 | # ifdef N_Q_M_STATS |
sewardj | a364d11 | 2008-10-27 01:25:14 +0000 | [diff] [blame] | 1086 | static UWord n_q = 0; |
| 1087 | static UWord n_m = 0; |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame^] | 1088 | n_q++; |
| 1089 | if (0 == (n_q & 0xFFFF)) |
| 1090 | VG_(debugLog)(0,"xxx","find_nsegment_idx: %lu %lu\n", n_q, n_m); |
| 1091 | # endif |
sewardj | a364d11 | 2008-10-27 01:25:14 +0000 | [diff] [blame] | 1092 | |
| 1093 | UWord ix; |
| 1094 | |
| 1095 | if (LIKELY(cache_inited)) { |
| 1096 | /* do nothing */ |
| 1097 | } else { |
| 1098 | for (ix = 0; ix < N_CACHE; ix++) { |
| 1099 | cache_pageno[ix] = 0; |
| 1100 | cache_segidx[ix] = -1; |
| 1101 | } |
| 1102 | cache_inited = True; |
| 1103 | } |
| 1104 | |
| 1105 | ix = (a >> 12) % N_CACHE; |
| 1106 | |
sewardj | a364d11 | 2008-10-27 01:25:14 +0000 | [diff] [blame] | 1107 | if ((a >> 12) == cache_pageno[ix] |
| 1108 | && cache_segidx[ix] >= 0 |
| 1109 | && cache_segidx[ix] < nsegments_used |
| 1110 | && nsegments[cache_segidx[ix]].start <= a |
| 1111 | && a <= nsegments[cache_segidx[ix]].end) { |
| 1112 | /* hit */ |
| 1113 | /* aspacem_assert( cache_segidx[ix] == find_nsegment_idx_WRK(a) ); */ |
| 1114 | return cache_segidx[ix]; |
| 1115 | } |
| 1116 | /* miss */ |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame^] | 1117 | # ifdef N_Q_M_STATS |
sewardj | a364d11 | 2008-10-27 01:25:14 +0000 | [diff] [blame] | 1118 | n_m++; |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame^] | 1119 | # endif |
sewardj | a364d11 | 2008-10-27 01:25:14 +0000 | [diff] [blame] | 1120 | cache_segidx[ix] = find_nsegment_idx_WRK(a); |
| 1121 | cache_pageno[ix] = a >> 12; |
| 1122 | return cache_segidx[ix]; |
| 1123 | # undef N_CACHE |
| 1124 | } |
| 1125 | |
| 1126 | |
florian | 7cb1293 | 2015-02-16 23:04:53 +0000 | [diff] [blame] | 1127 | /* Finds the segment containing 'a'. Only returns non-SkFree segments. */ |
sewardj | 716f31a | 2006-10-17 01:23:57 +0000 | [diff] [blame] | 1128 | NSegment const * VG_(am_find_nsegment) ( Addr a ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1129 | { |
| 1130 | Int i = find_nsegment_idx(a); |
| 1131 | aspacem_assert(i >= 0 && i < nsegments_used); |
| 1132 | aspacem_assert(nsegments[i].start <= a); |
| 1133 | aspacem_assert(a <= nsegments[i].end); |
| 1134 | if (nsegments[i].kind == SkFree) |
| 1135 | return NULL; |
| 1136 | else |
| 1137 | return &nsegments[i]; |
| 1138 | } |
| 1139 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 1140 | /* Finds an anonymous segment containing 'a'. Returned pointer is read only. */ |
| 1141 | NSegment const *VG_(am_find_anon_segment) ( Addr a ) |
| 1142 | { |
| 1143 | Int i = find_nsegment_idx(a); |
| 1144 | aspacem_assert(i >= 0 && i < nsegments_used); |
| 1145 | aspacem_assert(nsegments[i].start <= a); |
| 1146 | aspacem_assert(a <= nsegments[i].end); |
| 1147 | if (nsegments[i].kind == SkAnonC || nsegments[i].kind == SkAnonV) |
| 1148 | return &nsegments[i]; |
| 1149 | else |
| 1150 | return NULL; |
| 1151 | } |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1152 | |
florian | 686b8ca | 2015-02-14 21:17:46 +0000 | [diff] [blame] | 1153 | /* Map segment pointer to segment index. */ |
florian | 3e79863 | 2012-11-24 19:41:54 +0000 | [diff] [blame] | 1154 | static Int segAddr_to_index ( const NSegment* seg ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1155 | { |
florian | 686b8ca | 2015-02-14 21:17:46 +0000 | [diff] [blame] | 1156 | aspacem_assert(seg >= &nsegments[0] && seg < &nsegments[nsegments_used]); |
| 1157 | |
| 1158 | return seg - &nsegments[0]; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1159 | } |
| 1160 | |
| 1161 | |
florian | 7cb1293 | 2015-02-16 23:04:53 +0000 | [diff] [blame] | 1162 | /* Find the next segment along from 'here', if it is a non-SkFree segment. */ |
florian | 3e79863 | 2012-11-24 19:41:54 +0000 | [diff] [blame] | 1163 | NSegment const * VG_(am_next_nsegment) ( const NSegment* here, Bool fwds ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1164 | { |
| 1165 | Int i = segAddr_to_index(here); |
florian | 686b8ca | 2015-02-14 21:17:46 +0000 | [diff] [blame] | 1166 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1167 | if (fwds) { |
| 1168 | i++; |
| 1169 | if (i >= nsegments_used) |
| 1170 | return NULL; |
| 1171 | } else { |
| 1172 | i--; |
| 1173 | if (i < 0) |
| 1174 | return NULL; |
| 1175 | } |
florian | 7cb1293 | 2015-02-16 23:04:53 +0000 | [diff] [blame] | 1176 | if (nsegments[i].kind == SkFree) |
| 1177 | return NULL; |
| 1178 | else |
| 1179 | return &nsegments[i]; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1180 | } |
| 1181 | |
| 1182 | |
| 1183 | /* Trivial fn: return the total amount of space in anonymous mappings, |
| 1184 | both for V and the client. Is used for printing stats in |
| 1185 | out-of-memory messages. */ |
| 1186 | ULong VG_(am_get_anonsize_total)( void ) |
| 1187 | { |
| 1188 | Int i; |
| 1189 | ULong total = 0; |
| 1190 | for (i = 0; i < nsegments_used; i++) { |
| 1191 | if (nsegments[i].kind == SkAnonC || nsegments[i].kind == SkAnonV) { |
| 1192 | total += (ULong)nsegments[i].end |
| 1193 | - (ULong)nsegments[i].start + 1ULL; |
| 1194 | } |
| 1195 | } |
| 1196 | return total; |
| 1197 | } |
| 1198 | |
| 1199 | |
philippe | adfff76 | 2014-04-20 22:10:24 +0000 | [diff] [blame] | 1200 | /* Test if a piece of memory is addressable by client or by valgrind with at |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1201 | least the "prot" protection permissions by examining the underlying |
florian | a623345 | 2015-05-10 11:07:06 +0000 | [diff] [blame] | 1202 | segments. The KINDS argument specifies the allowed segments ADDR may |
| 1203 | belong to in order to be considered "valid". |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1204 | */ |
| 1205 | static |
florian | a623345 | 2015-05-10 11:07:06 +0000 | [diff] [blame] | 1206 | Bool is_valid_for( UInt kinds, Addr start, SizeT len, UInt prot ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1207 | { |
| 1208 | Int i, iLo, iHi; |
| 1209 | Bool needR, needW, needX; |
| 1210 | |
| 1211 | if (len == 0) |
| 1212 | return True; /* somewhat dubious case */ |
| 1213 | if (start + len < start) |
| 1214 | return False; /* reject wraparounds */ |
| 1215 | |
| 1216 | needR = toBool(prot & VKI_PROT_READ); |
| 1217 | needW = toBool(prot & VKI_PROT_WRITE); |
| 1218 | needX = toBool(prot & VKI_PROT_EXEC); |
| 1219 | |
| 1220 | iLo = find_nsegment_idx(start); |
| 1221 | aspacem_assert(start >= nsegments[iLo].start); |
| 1222 | |
| 1223 | if (start+len-1 <= nsegments[iLo].end) { |
| 1224 | /* This is a speedup hack which avoids calling find_nsegment_idx |
| 1225 | a second time when possible. It is always correct to just |
| 1226 | use the "else" clause below, but is_valid_for_client is |
| 1227 | called a lot by the leak checker, so avoiding pointless calls |
| 1228 | to find_nsegment_idx, which can be expensive, is helpful. */ |
| 1229 | iHi = iLo; |
| 1230 | } else { |
| 1231 | iHi = find_nsegment_idx(start + len - 1); |
| 1232 | } |
| 1233 | |
florian | a623345 | 2015-05-10 11:07:06 +0000 | [diff] [blame] | 1234 | for (i = iLo; i <= iHi; i++) { |
| 1235 | if ( (nsegments[i].kind & kinds) != 0 |
| 1236 | && (needR ? nsegments[i].hasR : True) |
| 1237 | && (needW ? nsegments[i].hasW : True) |
| 1238 | && (needX ? nsegments[i].hasX : True) ) { |
| 1239 | /* ok */ |
| 1240 | } else { |
| 1241 | return False; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1242 | } |
| 1243 | } |
florian | a623345 | 2015-05-10 11:07:06 +0000 | [diff] [blame] | 1244 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1245 | return True; |
| 1246 | } |
| 1247 | |
| 1248 | /* Test if a piece of memory is addressable by the client with at |
| 1249 | least the "prot" protection permissions by examining the underlying |
| 1250 | segments. */ |
| 1251 | Bool VG_(am_is_valid_for_client)( Addr start, SizeT len, |
| 1252 | UInt prot ) |
| 1253 | { |
florian | a623345 | 2015-05-10 11:07:06 +0000 | [diff] [blame] | 1254 | const UInt kinds = SkFileC | SkAnonC | SkShmC; |
| 1255 | |
| 1256 | return is_valid_for(kinds, start, len, prot); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1257 | } |
| 1258 | |
| 1259 | /* Variant of VG_(am_is_valid_for_client) which allows free areas to |
| 1260 | be consider part of the client's addressable space. It also |
| 1261 | considers reservations to be allowable, since from the client's |
| 1262 | point of view they don't exist. */ |
| 1263 | Bool VG_(am_is_valid_for_client_or_free_or_resvn) |
| 1264 | ( Addr start, SizeT len, UInt prot ) |
| 1265 | { |
florian | a623345 | 2015-05-10 11:07:06 +0000 | [diff] [blame] | 1266 | const UInt kinds = SkFileC | SkAnonC | SkShmC | SkFree | SkResvn; |
| 1267 | |
| 1268 | return is_valid_for(kinds, start, len, prot); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1269 | } |
| 1270 | |
iraisr | e8b9ee3 | 2015-08-31 21:31:09 +0000 | [diff] [blame] | 1271 | /* Checks if a piece of memory consists of either free or reservation |
| 1272 | segments. */ |
| 1273 | Bool VG_(am_is_free_or_resvn)( Addr start, SizeT len ) |
| 1274 | { |
| 1275 | const UInt kinds = SkFree | SkResvn; |
| 1276 | |
| 1277 | return is_valid_for(kinds, start, len, 0); |
| 1278 | } |
| 1279 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1280 | |
philippe | adfff76 | 2014-04-20 22:10:24 +0000 | [diff] [blame] | 1281 | Bool VG_(am_is_valid_for_valgrind) ( Addr start, SizeT len, UInt prot ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1282 | { |
florian | a623345 | 2015-05-10 11:07:06 +0000 | [diff] [blame] | 1283 | const UInt kinds = SkFileV | SkAnonV; |
| 1284 | |
| 1285 | return is_valid_for(kinds, start, len, prot); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1286 | } |
| 1287 | |
| 1288 | |
| 1289 | /* Returns True if any part of the address range is marked as having |
| 1290 | translations made from it. This is used to determine when to |
| 1291 | discard code, so if in doubt return True. */ |
| 1292 | |
| 1293 | static Bool any_Ts_in_range ( Addr start, SizeT len ) |
| 1294 | { |
| 1295 | Int iLo, iHi, i; |
| 1296 | aspacem_assert(len > 0); |
| 1297 | aspacem_assert(start + len > start); |
| 1298 | iLo = find_nsegment_idx(start); |
| 1299 | iHi = find_nsegment_idx(start + len - 1); |
| 1300 | for (i = iLo; i <= iHi; i++) { |
| 1301 | if (nsegments[i].hasT) |
| 1302 | return True; |
| 1303 | } |
| 1304 | return False; |
| 1305 | } |
| 1306 | |
| 1307 | |
florian | 8f3cd17 | 2015-04-22 14:16:11 +0000 | [diff] [blame] | 1308 | /* Check whether ADDR looks like an address or address-to-be located in an |
| 1309 | extensible client stack segment. Return true if |
| 1310 | (1) ADDR is located in an already mapped stack segment, OR |
| 1311 | (2) ADDR is located in a reservation segment into which an abutting SkAnonC |
florian | 017d8f5 | 2015-03-23 17:13:04 +0000 | [diff] [blame] | 1312 | segment can be extended. */ |
florian | 8f3cd17 | 2015-04-22 14:16:11 +0000 | [diff] [blame] | 1313 | Bool VG_(am_addr_is_in_extensible_client_stack)( Addr addr ) |
florian | 017d8f5 | 2015-03-23 17:13:04 +0000 | [diff] [blame] | 1314 | { |
| 1315 | const NSegment *seg = nsegments + find_nsegment_idx(addr); |
| 1316 | |
| 1317 | switch (seg->kind) { |
| 1318 | case SkFree: |
| 1319 | case SkAnonV: |
| 1320 | case SkFileV: |
| 1321 | case SkFileC: |
| 1322 | case SkShmC: |
florian | 8f3cd17 | 2015-04-22 14:16:11 +0000 | [diff] [blame] | 1323 | return False; |
florian | 017d8f5 | 2015-03-23 17:13:04 +0000 | [diff] [blame] | 1324 | |
| 1325 | case SkResvn: { |
florian | 8f3cd17 | 2015-04-22 14:16:11 +0000 | [diff] [blame] | 1326 | if (seg->smode != SmUpper) return False; |
florian | ad4e979 | 2015-07-05 21:53:33 +0000 | [diff] [blame] | 1327 | /* If the abutting segment towards higher addresses is an SkAnonC |
florian | 017d8f5 | 2015-03-23 17:13:04 +0000 | [diff] [blame] | 1328 | segment, then ADDR is a future stack pointer. */ |
| 1329 | const NSegment *next = VG_(am_next_nsegment)(seg, /*forward*/ True); |
florian | 8f3cd17 | 2015-04-22 14:16:11 +0000 | [diff] [blame] | 1330 | if (next == NULL || next->kind != SkAnonC) return False; |
florian | 017d8f5 | 2015-03-23 17:13:04 +0000 | [diff] [blame] | 1331 | |
| 1332 | /* OK; looks like a stack segment */ |
florian | 8f3cd17 | 2015-04-22 14:16:11 +0000 | [diff] [blame] | 1333 | return True; |
florian | 017d8f5 | 2015-03-23 17:13:04 +0000 | [diff] [blame] | 1334 | } |
| 1335 | |
| 1336 | case SkAnonC: { |
| 1337 | /* If the abutting segment towards lower addresses is an SkResvn |
| 1338 | segment, then ADDR is a stack pointer into mapped memory. */ |
| 1339 | const NSegment *next = VG_(am_next_nsegment)(seg, /*forward*/ False); |
florian | d57686f | 2015-04-22 13:50:13 +0000 | [diff] [blame] | 1340 | if (next == NULL || next->kind != SkResvn || next->smode != SmUpper) |
florian | 8f3cd17 | 2015-04-22 14:16:11 +0000 | [diff] [blame] | 1341 | return False; |
florian | 017d8f5 | 2015-03-23 17:13:04 +0000 | [diff] [blame] | 1342 | |
| 1343 | /* OK; looks like a stack segment */ |
florian | 8f3cd17 | 2015-04-22 14:16:11 +0000 | [diff] [blame] | 1344 | return True; |
florian | 017d8f5 | 2015-03-23 17:13:04 +0000 | [diff] [blame] | 1345 | } |
| 1346 | |
| 1347 | default: |
| 1348 | aspacem_assert(0); // should never happen |
| 1349 | } |
| 1350 | } |
| 1351 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1352 | /*-----------------------------------------------------------------*/ |
| 1353 | /*--- ---*/ |
| 1354 | /*--- Modifying the segment array, and constructing segments. ---*/ |
| 1355 | /*--- ---*/ |
| 1356 | /*-----------------------------------------------------------------*/ |
| 1357 | |
| 1358 | /* Split the segment containing 'a' into two, so that 'a' is |
| 1359 | guaranteed to be the start of a new segment. If 'a' is already the |
| 1360 | start of a segment, do nothing. */ |
| 1361 | |
| 1362 | static void split_nsegment_at ( Addr a ) |
| 1363 | { |
| 1364 | Int i, j; |
| 1365 | |
| 1366 | aspacem_assert(a > 0); |
| 1367 | aspacem_assert(VG_IS_PAGE_ALIGNED(a)); |
| 1368 | |
| 1369 | i = find_nsegment_idx(a); |
| 1370 | aspacem_assert(i >= 0 && i < nsegments_used); |
| 1371 | |
| 1372 | if (nsegments[i].start == a) |
| 1373 | /* 'a' is already the start point of a segment, so nothing to be |
| 1374 | done. */ |
| 1375 | return; |
| 1376 | |
| 1377 | /* else we have to slide the segments upwards to make a hole */ |
| 1378 | if (nsegments_used >= VG_N_SEGMENTS) |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 1379 | ML_(am_barf_toolow)("VG_N_SEGMENTS"); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1380 | for (j = nsegments_used-1; j > i; j--) |
| 1381 | nsegments[j+1] = nsegments[j]; |
| 1382 | nsegments_used++; |
| 1383 | |
| 1384 | nsegments[i+1] = nsegments[i]; |
| 1385 | nsegments[i+1].start = a; |
| 1386 | nsegments[i].end = a-1; |
| 1387 | |
| 1388 | if (nsegments[i].kind == SkFileV || nsegments[i].kind == SkFileC) |
| 1389 | nsegments[i+1].offset |
| 1390 | += ((ULong)nsegments[i+1].start) - ((ULong)nsegments[i].start); |
| 1391 | |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 1392 | ML_(am_inc_refcount)(nsegments[i].fnIdx); |
florian | 346ee2f | 2015-04-06 21:34:30 +0000 | [diff] [blame] | 1393 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1394 | aspacem_assert(sane_NSegment(&nsegments[i])); |
| 1395 | aspacem_assert(sane_NSegment(&nsegments[i+1])); |
| 1396 | } |
| 1397 | |
| 1398 | |
| 1399 | /* Do the minimum amount of segment splitting necessary to ensure that |
| 1400 | sLo is the first address denoted by some segment and sHi is the |
| 1401 | highest address denoted by some other segment. Returns the indices |
| 1402 | of the lowest and highest segments in the range. */ |
| 1403 | |
| 1404 | static |
| 1405 | void split_nsegments_lo_and_hi ( Addr sLo, Addr sHi, |
| 1406 | /*OUT*/Int* iLo, |
| 1407 | /*OUT*/Int* iHi ) |
| 1408 | { |
| 1409 | aspacem_assert(sLo < sHi); |
| 1410 | aspacem_assert(VG_IS_PAGE_ALIGNED(sLo)); |
| 1411 | aspacem_assert(VG_IS_PAGE_ALIGNED(sHi+1)); |
| 1412 | |
| 1413 | if (sLo > 0) |
| 1414 | split_nsegment_at(sLo); |
| 1415 | if (sHi < sHi+1) |
| 1416 | split_nsegment_at(sHi+1); |
| 1417 | |
| 1418 | *iLo = find_nsegment_idx(sLo); |
| 1419 | *iHi = find_nsegment_idx(sHi); |
| 1420 | aspacem_assert(0 <= *iLo && *iLo < nsegments_used); |
| 1421 | aspacem_assert(0 <= *iHi && *iHi < nsegments_used); |
| 1422 | aspacem_assert(*iLo <= *iHi); |
| 1423 | aspacem_assert(nsegments[*iLo].start == sLo); |
| 1424 | aspacem_assert(nsegments[*iHi].end == sHi); |
| 1425 | /* Not that I'm overly paranoid or anything, definitely not :-) */ |
| 1426 | } |
| 1427 | |
| 1428 | |
| 1429 | /* Add SEG to the collection, deleting/truncating any it overlaps. |
| 1430 | This deals with all the tricky cases of splitting up segments as |
| 1431 | needed. */ |
| 1432 | |
florian | 3297124 | 2014-10-23 17:47:15 +0000 | [diff] [blame] | 1433 | static void add_segment ( const NSegment* seg ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1434 | { |
| 1435 | Int i, iLo, iHi, delta; |
| 1436 | Bool segment_is_sane; |
| 1437 | |
| 1438 | Addr sStart = seg->start; |
| 1439 | Addr sEnd = seg->end; |
| 1440 | |
| 1441 | aspacem_assert(sStart <= sEnd); |
| 1442 | aspacem_assert(VG_IS_PAGE_ALIGNED(sStart)); |
| 1443 | aspacem_assert(VG_IS_PAGE_ALIGNED(sEnd+1)); |
| 1444 | |
| 1445 | segment_is_sane = sane_NSegment(seg); |
njn | ddaef35 | 2009-07-31 05:06:29 +0000 | [diff] [blame] | 1446 | if (!segment_is_sane) show_nsegment_full(0,-1,seg); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1447 | aspacem_assert(segment_is_sane); |
| 1448 | |
| 1449 | split_nsegments_lo_and_hi( sStart, sEnd, &iLo, &iHi ); |
| 1450 | |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame^] | 1451 | /* Increase the reference count of SEG's name. We need to do this |
| 1452 | *before* decreasing the reference count of the names of the replaced |
| 1453 | segments. Consider the case where the segment name of SEG and one of |
| 1454 | the replaced segments are the same. If the refcount of that name is 1, |
| 1455 | then decrementing first would put the slot for that name on the free |
| 1456 | list. Attempting to increment the refcount later would then fail |
| 1457 | because the slot is no longer allocated. */ |
| 1458 | ML_(am_inc_refcount)(seg->fnIdx); |
| 1459 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1460 | /* Now iLo .. iHi inclusive is the range of segment indices which |
| 1461 | seg will replace. If we're replacing more than one segment, |
florian | 346ee2f | 2015-04-06 21:34:30 +0000 | [diff] [blame] | 1462 | slide those above the range down to fill the hole. Before doing |
| 1463 | that decrement the reference counters for the segments names of |
| 1464 | the replaced segments. */ |
| 1465 | for (i = iLo; i <= iHi; ++i) |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 1466 | ML_(am_dec_refcount)(nsegments[i].fnIdx); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1467 | delta = iHi - iLo; |
| 1468 | aspacem_assert(delta >= 0); |
| 1469 | if (delta > 0) { |
| 1470 | for (i = iLo; i < nsegments_used-delta; i++) |
| 1471 | nsegments[i] = nsegments[i+delta]; |
| 1472 | nsegments_used -= delta; |
| 1473 | } |
| 1474 | |
| 1475 | nsegments[iLo] = *seg; |
| 1476 | |
| 1477 | (void)preen_nsegments(); |
| 1478 | if (0) VG_(am_show_nsegments)(0,"AFTER preen (add_segment)"); |
| 1479 | } |
| 1480 | |
| 1481 | |
| 1482 | /* Clear out an NSegment record. */ |
| 1483 | |
| 1484 | static void init_nsegment ( /*OUT*/NSegment* seg ) |
| 1485 | { |
| 1486 | seg->kind = SkFree; |
| 1487 | seg->start = 0; |
| 1488 | seg->end = 0; |
| 1489 | seg->smode = SmFixed; |
| 1490 | seg->dev = 0; |
| 1491 | seg->ino = 0; |
tom | f4c2310 | 2005-10-31 17:05:21 +0000 | [diff] [blame] | 1492 | seg->mode = 0; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1493 | seg->offset = 0; |
| 1494 | seg->fnIdx = -1; |
| 1495 | seg->hasR = seg->hasW = seg->hasX = seg->hasT = seg->isCH = False; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1496 | } |
| 1497 | |
| 1498 | /* Make an NSegment which holds a reservation. */ |
| 1499 | |
| 1500 | static void init_resvn ( /*OUT*/NSegment* seg, Addr start, Addr end ) |
| 1501 | { |
| 1502 | aspacem_assert(start < end); |
| 1503 | aspacem_assert(VG_IS_PAGE_ALIGNED(start)); |
| 1504 | aspacem_assert(VG_IS_PAGE_ALIGNED(end+1)); |
| 1505 | init_nsegment(seg); |
| 1506 | seg->kind = SkResvn; |
| 1507 | seg->start = start; |
| 1508 | seg->end = end; |
| 1509 | } |
| 1510 | |
| 1511 | |
| 1512 | /*-----------------------------------------------------------------*/ |
| 1513 | /*--- ---*/ |
| 1514 | /*--- Startup, including reading /proc/self/maps. ---*/ |
| 1515 | /*--- ---*/ |
| 1516 | /*-----------------------------------------------------------------*/ |
| 1517 | |
| 1518 | static void read_maps_callback ( Addr addr, SizeT len, UInt prot, |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 1519 | ULong dev, ULong ino, Off64T offset, |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 1520 | const HChar* filename ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1521 | { |
| 1522 | NSegment seg; |
| 1523 | init_nsegment( &seg ); |
| 1524 | seg.start = addr; |
| 1525 | seg.end = addr+len-1; |
| 1526 | seg.dev = dev; |
| 1527 | seg.ino = ino; |
| 1528 | seg.offset = offset; |
| 1529 | seg.hasR = toBool(prot & VKI_PROT_READ); |
| 1530 | seg.hasW = toBool(prot & VKI_PROT_WRITE); |
| 1531 | seg.hasX = toBool(prot & VKI_PROT_EXEC); |
| 1532 | seg.hasT = False; |
| 1533 | |
florian | 981ffbd | 2015-09-05 21:27:58 +0000 | [diff] [blame] | 1534 | /* A segment in the initial /proc/self/maps is considered a FileV |
| 1535 | segment if either it has a file name associated with it or both its |
| 1536 | device and inode numbers are != 0. See bug #124528. */ |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1537 | seg.kind = SkAnonV; |
florian | 981ffbd | 2015-09-05 21:27:58 +0000 | [diff] [blame] | 1538 | if (filename || (dev != 0 && ino != 0)) |
sewardj | c2fe246 | 2006-04-04 16:57:15 +0000 | [diff] [blame] | 1539 | seg.kind = SkFileV; |
sewardj | 38a21ac | 2010-01-03 10:14:03 +0000 | [diff] [blame] | 1540 | |
| 1541 | # if defined(VGO_darwin) |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 1542 | // GrP fixme no dev/ino on darwin |
| 1543 | if (offset != 0) |
sewardj | 38a21ac | 2010-01-03 10:14:03 +0000 | [diff] [blame] | 1544 | seg.kind = SkFileV; |
| 1545 | # endif // defined(VGO_darwin) |
| 1546 | |
| 1547 | # if defined(VGP_arm_linux) |
| 1548 | /* The standard handling of entries read from /proc/self/maps will |
| 1549 | cause the faked up commpage segment to have type SkAnonV, which |
| 1550 | is a problem because it contains code we want the client to |
| 1551 | execute, and so later m_translate will segfault the client when |
| 1552 | it tries to go in there. Hence change the ownership of it here |
| 1553 | to the client (SkAnonC). The least-worst kludge I could think |
| 1554 | of. */ |
| 1555 | if (addr == ARM_LINUX_FAKE_COMMPAGE_START |
| 1556 | && addr + len == ARM_LINUX_FAKE_COMMPAGE_END1 |
| 1557 | && seg.kind == SkAnonV) |
| 1558 | seg.kind = SkAnonC; |
| 1559 | # endif // defined(VGP_arm_linux) |
| 1560 | |
sewardj | c2fe246 | 2006-04-04 16:57:15 +0000 | [diff] [blame] | 1561 | if (filename) |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 1562 | seg.fnIdx = ML_(am_allocate_segname)( filename ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1563 | |
| 1564 | if (0) show_nsegment( 2,0, &seg ); |
| 1565 | add_segment( &seg ); |
| 1566 | } |
| 1567 | |
florian | 82e7a54 | 2015-03-26 21:55:00 +0000 | [diff] [blame] | 1568 | Bool |
| 1569 | VG_(am_is_valid_for_aspacem_minAddr)( Addr addr, const HChar **errmsg ) |
| 1570 | { |
florian | d738b46 | 2015-03-27 08:47:22 +0000 | [diff] [blame] | 1571 | const Addr min = VKI_PAGE_SIZE; |
florian | 82e7a54 | 2015-03-26 21:55:00 +0000 | [diff] [blame] | 1572 | #if VG_WORDSIZE == 4 |
| 1573 | const Addr max = 0x40000000; // 1Gb |
| 1574 | #else |
| 1575 | const Addr max = 0x200000000; // 8Gb |
| 1576 | #endif |
| 1577 | Bool ok = VG_IS_PAGE_ALIGNED(addr) && addr >= min && addr <= max; |
| 1578 | |
| 1579 | if (errmsg) { |
| 1580 | *errmsg = ""; |
| 1581 | if (! ok) { |
| 1582 | const HChar fmt[] = "Must be a page aligned address between " |
| 1583 | "0x%lx and 0x%lx"; |
| 1584 | static HChar buf[sizeof fmt + 2 * 16]; // large enough |
| 1585 | ML_(am_sprintf)(buf, fmt, min, max); |
| 1586 | *errmsg = buf; |
| 1587 | } |
| 1588 | } |
| 1589 | return ok; |
| 1590 | } |
| 1591 | |
philippe | 38a74d2 | 2014-08-29 22:53:19 +0000 | [diff] [blame] | 1592 | /* See description in pub_core_aspacemgr.h */ |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1593 | Addr VG_(am_startup) ( Addr sp_at_startup ) |
| 1594 | { |
| 1595 | NSegment seg; |
philippe | 38a74d2 | 2014-08-29 22:53:19 +0000 | [diff] [blame] | 1596 | Addr suggested_clstack_end; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1597 | |
| 1598 | aspacem_assert(sizeof(Word) == sizeof(void*)); |
| 1599 | aspacem_assert(sizeof(Addr) == sizeof(void*)); |
| 1600 | aspacem_assert(sizeof(SizeT) == sizeof(void*)); |
| 1601 | aspacem_assert(sizeof(SSizeT) == sizeof(void*)); |
| 1602 | |
florian | 346ee2f | 2015-04-06 21:34:30 +0000 | [diff] [blame] | 1603 | /* Initialise the string table for segment names. */ |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 1604 | ML_(am_segnames_init)(); |
florian | 346ee2f | 2015-04-06 21:34:30 +0000 | [diff] [blame] | 1605 | |
sewardj | 4190600 | 2008-08-18 21:47:11 +0000 | [diff] [blame] | 1606 | /* Check that we can store the largest imaginable dev, ino and |
| 1607 | offset numbers in an NSegment. */ |
| 1608 | aspacem_assert(sizeof(seg.dev) == 8); |
| 1609 | aspacem_assert(sizeof(seg.ino) == 8); |
| 1610 | aspacem_assert(sizeof(seg.offset) == 8); |
| 1611 | aspacem_assert(sizeof(seg.mode) == 4); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1612 | |
| 1613 | /* Add a single interval covering the entire address space. */ |
| 1614 | init_nsegment(&seg); |
| 1615 | seg.kind = SkFree; |
| 1616 | seg.start = Addr_MIN; |
| 1617 | seg.end = Addr_MAX; |
| 1618 | nsegments[0] = seg; |
| 1619 | nsegments_used = 1; |
| 1620 | |
philippe | e4d7812 | 2014-04-20 14:20:37 +0000 | [diff] [blame] | 1621 | aspacem_minAddr = VG_(clo_aspacem_minAddr); |
| 1622 | |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 1623 | #if defined(VGO_darwin) |
| 1624 | |
| 1625 | # if VG_WORDSIZE == 4 |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 1626 | aspacem_maxAddr = (Addr) 0xffffffff; |
| 1627 | |
| 1628 | aspacem_cStart = aspacem_minAddr; |
| 1629 | aspacem_vStart = 0xf0000000; // 0xc0000000..0xf0000000 available |
| 1630 | # else |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 1631 | aspacem_maxAddr = (Addr) 0x7fffffffffff; |
| 1632 | |
| 1633 | aspacem_cStart = aspacem_minAddr; |
| 1634 | aspacem_vStart = 0x700000000000; // 0x7000:00000000..0x7fff:5c000000 avail |
| 1635 | // 0x7fff:5c000000..0x7fff:ffe00000? is stack, dyld, shared cache |
| 1636 | # endif |
| 1637 | |
philippe | 38a74d2 | 2014-08-29 22:53:19 +0000 | [diff] [blame] | 1638 | suggested_clstack_end = -1; // ignored; Mach-O specifies its stack |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 1639 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 1640 | #elif defined(VGO_solaris) |
| 1641 | # if VG_WORDSIZE == 4 |
| 1642 | /* |
| 1643 | Intended address space partitioning: |
| 1644 | |
| 1645 | ,--------------------------------, 0x00000000 |
| 1646 | | | |
| 1647 | |--------------------------------| |
| 1648 | | initial stack given to V by OS | |
| 1649 | |--------------------------------| 0x08000000 |
| 1650 | | client text | |
| 1651 | |--------------------------------| |
| 1652 | | | |
| 1653 | | | |
| 1654 | |--------------------------------| |
| 1655 | | client stack | |
| 1656 | |--------------------------------| 0x38000000 |
| 1657 | | V's text | |
| 1658 | |--------------------------------| |
| 1659 | | | |
| 1660 | | | |
| 1661 | |--------------------------------| |
| 1662 | | dynamic shared objects | |
| 1663 | '--------------------------------' 0xffffffff |
| 1664 | |
| 1665 | */ |
| 1666 | |
| 1667 | /* Anonymous pages need to fit under user limit (USERLIMIT32) |
| 1668 | which is 4KB + 16MB below the top of the 32-bit range. */ |
| 1669 | # ifdef ENABLE_INNER |
| 1670 | aspacem_maxAddr = (Addr)0x4fffffff; // 1.25GB |
| 1671 | aspacem_vStart = (Addr)0x40000000; // 1GB |
| 1672 | # else |
| 1673 | aspacem_maxAddr = (Addr)0xfefff000 - 1; // 4GB - 16MB - 4KB |
| 1674 | aspacem_vStart = (Addr)0x50000000; // 1.25GB |
| 1675 | # endif |
| 1676 | # elif VG_WORDSIZE == 8 |
| 1677 | /* |
| 1678 | Intended address space partitioning: |
| 1679 | |
| 1680 | ,--------------------------------, 0x00000000_00000000 |
| 1681 | | | |
| 1682 | |--------------------------------| 0x00000000_00400000 |
| 1683 | | client text | |
| 1684 | |--------------------------------| |
| 1685 | | | |
| 1686 | | | |
| 1687 | |--------------------------------| |
| 1688 | | client stack | |
| 1689 | |--------------------------------| 0x00000000_38000000 |
| 1690 | | V's text | |
| 1691 | |--------------------------------| |
| 1692 | | | |
| 1693 | |--------------------------------| |
| 1694 | | dynamic shared objects | |
| 1695 | |--------------------------------| 0x0000000f_ffffffff |
| 1696 | | | |
| 1697 | | | |
| 1698 | |--------------------------------| |
| 1699 | | initial stack given to V by OS | |
| 1700 | '--------------------------------' 0xffffffff_ffffffff |
| 1701 | |
| 1702 | */ |
| 1703 | |
| 1704 | /* Kernel likes to place objects at the end of the address space. |
| 1705 | However accessing memory beyond 64GB makes memcheck slow |
| 1706 | (see memcheck/mc_main.c, internal representation). Therefore: |
| 1707 | - mmapobj() syscall is emulated so that libraries are subject to |
| 1708 | Valgrind's aspacemgr control |
| 1709 | - Kernel shared pages (such as schedctl and hrt) are left as they are |
| 1710 | because kernel cannot be told where they should be put */ |
| 1711 | # ifdef ENABLE_INNER |
| 1712 | aspacem_maxAddr = (Addr) 0x00000007ffffffff; // 32GB |
| 1713 | aspacem_vStart = (Addr) 0x0000000400000000; // 16GB |
| 1714 | # else |
| 1715 | aspacem_maxAddr = (Addr) 0x0000000fffffffff; // 64GB |
| 1716 | aspacem_vStart = (Addr) 0x0000000800000000; // 32GB |
| 1717 | # endif |
| 1718 | # else |
| 1719 | # error "Unknown word size" |
| 1720 | # endif |
| 1721 | |
| 1722 | aspacem_cStart = aspacem_minAddr; |
| 1723 | # ifdef ENABLE_INNER |
| 1724 | suggested_clstack_end = (Addr) 0x27ff0000 - 1; // 64kB below V's text |
| 1725 | # else |
| 1726 | suggested_clstack_end = (Addr) 0x37ff0000 - 1; // 64kB below V's text |
| 1727 | # endif |
| 1728 | |
| 1729 | #else |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 1730 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1731 | /* Establish address limits and block out unusable parts |
| 1732 | accordingly. */ |
| 1733 | |
| 1734 | VG_(debugLog)(2, "aspacem", |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 1735 | " sp_at_startup = 0x%010lx (supplied)\n", |
| 1736 | sp_at_startup ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1737 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1738 | # if VG_WORDSIZE == 8 |
sewardj | 6805a4a | 2013-01-29 21:14:46 +0000 | [diff] [blame] | 1739 | aspacem_maxAddr = (Addr)0x1000000000ULL - 1; // 64G |
sewardj | 70c91dd | 2005-11-13 18:51:31 +0000 | [diff] [blame] | 1740 | # ifdef ENABLE_INNER |
sewardj | 420c655 | 2006-12-01 02:35:02 +0000 | [diff] [blame] | 1741 | { Addr cse = VG_PGROUNDDN( sp_at_startup ) - 1; |
| 1742 | if (aspacem_maxAddr > cse) |
| 1743 | aspacem_maxAddr = cse; |
| 1744 | } |
sewardj | 70c91dd | 2005-11-13 18:51:31 +0000 | [diff] [blame] | 1745 | # endif |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1746 | # else |
sewardj | 70c91dd | 2005-11-13 18:51:31 +0000 | [diff] [blame] | 1747 | aspacem_maxAddr = VG_PGROUNDDN( sp_at_startup ) - 1; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1748 | # endif |
| 1749 | |
philippe | e4d7812 | 2014-04-20 14:20:37 +0000 | [diff] [blame] | 1750 | aspacem_cStart = aspacem_minAddr; |
philippe | d4f5aac | 2012-03-08 23:42:05 +0000 | [diff] [blame] | 1751 | aspacem_vStart = VG_PGROUNDUP(aspacem_minAddr |
| 1752 | + (aspacem_maxAddr - aspacem_minAddr + 1) / 2); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1753 | # ifdef ENABLE_INNER |
| 1754 | aspacem_vStart -= 0x10000000; // 256M |
| 1755 | # endif |
| 1756 | |
philippe | 38a74d2 | 2014-08-29 22:53:19 +0000 | [diff] [blame] | 1757 | suggested_clstack_end = aspacem_maxAddr - 16*1024*1024ULL |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1758 | + VKI_PAGE_SIZE; |
| 1759 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 1760 | #endif |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 1761 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1762 | aspacem_assert(VG_IS_PAGE_ALIGNED(aspacem_minAddr)); |
| 1763 | aspacem_assert(VG_IS_PAGE_ALIGNED(aspacem_maxAddr + 1)); |
| 1764 | aspacem_assert(VG_IS_PAGE_ALIGNED(aspacem_cStart)); |
| 1765 | aspacem_assert(VG_IS_PAGE_ALIGNED(aspacem_vStart)); |
philippe | 38a74d2 | 2014-08-29 22:53:19 +0000 | [diff] [blame] | 1766 | aspacem_assert(VG_IS_PAGE_ALIGNED(suggested_clstack_end + 1)); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1767 | |
| 1768 | VG_(debugLog)(2, "aspacem", |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 1769 | " minAddr = 0x%010lx (computed)\n", |
| 1770 | aspacem_minAddr); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1771 | VG_(debugLog)(2, "aspacem", |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 1772 | " maxAddr = 0x%010lx (computed)\n", |
| 1773 | aspacem_maxAddr); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1774 | VG_(debugLog)(2, "aspacem", |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 1775 | " cStart = 0x%010lx (computed)\n", |
| 1776 | aspacem_cStart); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1777 | VG_(debugLog)(2, "aspacem", |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 1778 | " vStart = 0x%010lx (computed)\n", |
| 1779 | aspacem_vStart); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1780 | VG_(debugLog)(2, "aspacem", |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 1781 | "suggested_clstack_end = 0x%010lx (computed)\n", |
| 1782 | suggested_clstack_end); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1783 | |
| 1784 | if (aspacem_cStart > Addr_MIN) { |
| 1785 | init_resvn(&seg, Addr_MIN, aspacem_cStart-1); |
| 1786 | add_segment(&seg); |
| 1787 | } |
| 1788 | if (aspacem_maxAddr < Addr_MAX) { |
| 1789 | init_resvn(&seg, aspacem_maxAddr+1, Addr_MAX); |
| 1790 | add_segment(&seg); |
| 1791 | } |
| 1792 | |
| 1793 | /* Create a 1-page reservation at the notional initial |
| 1794 | client/valgrind boundary. This isn't strictly necessary, but |
| 1795 | because the advisor does first-fit and starts searches for |
| 1796 | valgrind allocations at the boundary, this is kind of necessary |
| 1797 | in order to get it to start allocating in the right place. */ |
| 1798 | init_resvn(&seg, aspacem_vStart, aspacem_vStart + VKI_PAGE_SIZE - 1); |
| 1799 | add_segment(&seg); |
| 1800 | |
| 1801 | VG_(am_show_nsegments)(2, "Initial layout"); |
| 1802 | |
| 1803 | VG_(debugLog)(2, "aspacem", "Reading /proc/self/maps\n"); |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 1804 | parse_procselfmaps( read_maps_callback, NULL ); |
sewardj | 38a21ac | 2010-01-03 10:14:03 +0000 | [diff] [blame] | 1805 | /* NB: on arm-linux, parse_procselfmaps automagically kludges up |
| 1806 | (iow, hands to its callbacks) a description of the ARM Commpage, |
| 1807 | since that's not listed in /proc/self/maps (kernel bug IMO). We |
| 1808 | have to fake up its existence in parse_procselfmaps and not |
| 1809 | merely add it here as an extra segment, because doing the latter |
| 1810 | causes sync checking to fail: we see we have an extra segment in |
| 1811 | the segments array, which isn't listed in /proc/self/maps. |
| 1812 | Hence we must make it appear that /proc/self/maps contained this |
| 1813 | segment all along. Sigh. */ |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1814 | |
| 1815 | VG_(am_show_nsegments)(2, "With contents of /proc/self/maps"); |
| 1816 | |
| 1817 | AM_SANITY_CHECK; |
philippe | 38a74d2 | 2014-08-29 22:53:19 +0000 | [diff] [blame] | 1818 | return suggested_clstack_end; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1819 | } |
| 1820 | |
| 1821 | |
| 1822 | /*-----------------------------------------------------------------*/ |
| 1823 | /*--- ---*/ |
| 1824 | /*--- The core query-notify mechanism. ---*/ |
| 1825 | /*--- ---*/ |
| 1826 | /*-----------------------------------------------------------------*/ |
| 1827 | |
| 1828 | /* Query aspacem to ask where a mapping should go. */ |
| 1829 | |
florian | 3297124 | 2014-10-23 17:47:15 +0000 | [diff] [blame] | 1830 | Addr VG_(am_get_advisory) ( const MapRequest* req, |
| 1831 | Bool forClient, |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1832 | /*OUT*/Bool* ok ) |
| 1833 | { |
| 1834 | /* This function implements allocation policy. |
| 1835 | |
| 1836 | The nature of the allocation request is determined by req, which |
| 1837 | specifies the start and length of the request and indicates |
| 1838 | whether the start address is mandatory, a hint, or irrelevant, |
| 1839 | and by forClient, which says whether this is for the client or |
| 1840 | for V. |
| 1841 | |
| 1842 | Return values: the request can be vetoed (*ok is set to False), |
| 1843 | in which case the caller should not attempt to proceed with |
| 1844 | making the mapping. Otherwise, *ok is set to True, the caller |
| 1845 | may proceed, and the preferred address at which the mapping |
| 1846 | should happen is returned. |
| 1847 | |
| 1848 | Note that this is an advisory system only: the kernel can in |
| 1849 | fact do whatever it likes as far as placement goes, and we have |
| 1850 | no absolute control over it. |
| 1851 | |
| 1852 | Allocations will never be granted in a reserved area. |
| 1853 | |
| 1854 | The Default Policy is: |
| 1855 | |
| 1856 | Search the address space for two free intervals: one of them |
| 1857 | big enough to contain the request without regard to the |
| 1858 | specified address (viz, as if it was a floating request) and |
| 1859 | the other being able to contain the request at the specified |
| 1860 | address (viz, as if were a fixed request). Then, depending on |
| 1861 | the outcome of the search and the kind of request made, decide |
| 1862 | whether the request is allowable and what address to advise. |
| 1863 | |
| 1864 | The Default Policy is overriden by Policy Exception #1: |
| 1865 | |
| 1866 | If the request is for a fixed client map, we are prepared to |
| 1867 | grant it providing all areas inside the request are either |
| 1868 | free, reservations, or mappings belonging to the client. In |
| 1869 | other words we are prepared to let the client trash its own |
| 1870 | mappings if it wants to. |
| 1871 | |
sewardj | caf971d | 2005-09-29 21:20:41 +0000 | [diff] [blame] | 1872 | The Default Policy is overriden by Policy Exception #2: |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1873 | |
sewardj | caf971d | 2005-09-29 21:20:41 +0000 | [diff] [blame] | 1874 | If the request is for a hinted client map, we are prepared to |
| 1875 | grant it providing all areas inside the request are either |
| 1876 | free or reservations. In other words we are prepared to let |
| 1877 | the client have a hinted mapping anywhere it likes provided |
| 1878 | it does not trash either any of its own mappings or any of |
| 1879 | valgrind's mappings. |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1880 | */ |
| 1881 | Int i, j; |
| 1882 | Addr holeStart, holeEnd, holeLen; |
| 1883 | Bool fixed_not_required; |
| 1884 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 1885 | #if defined(VGO_solaris) |
| 1886 | Addr startPoint = forClient ? aspacem_vStart - 1 : aspacem_maxAddr - 1; |
| 1887 | #else |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1888 | Addr startPoint = forClient ? aspacem_cStart : aspacem_vStart; |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 1889 | #endif /* VGO_solaris */ |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1890 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 1891 | Addr reqStart = req->rkind==MFixed || req->rkind==MHint ? req->start : 0; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1892 | Addr reqEnd = reqStart + req->len - 1; |
| 1893 | Addr reqLen = req->len; |
| 1894 | |
| 1895 | /* These hold indices for segments found during search, or -1 if not |
| 1896 | found. */ |
| 1897 | Int floatIdx = -1; |
| 1898 | Int fixedIdx = -1; |
| 1899 | |
| 1900 | aspacem_assert(nsegments_used > 0); |
| 1901 | |
| 1902 | if (0) { |
| 1903 | VG_(am_show_nsegments)(0,"getAdvisory"); |
florian | a5e06c3 | 2015-08-05 21:16:09 +0000 | [diff] [blame] | 1904 | VG_(debugLog)(0,"aspacem", "getAdvisory 0x%lx %lu\n", |
| 1905 | req->start, req->len); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1906 | } |
| 1907 | |
| 1908 | /* Reject zero-length requests */ |
| 1909 | if (req->len == 0) { |
| 1910 | *ok = False; |
| 1911 | return 0; |
| 1912 | } |
| 1913 | |
| 1914 | /* Reject wraparounds */ |
florian | e354a1f | 2015-05-26 17:59:50 +0000 | [diff] [blame] | 1915 | if (req->start + req->len < req->start) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1916 | *ok = False; |
| 1917 | return 0; |
| 1918 | } |
| 1919 | |
| 1920 | /* ------ Implement Policy Exception #1 ------ */ |
| 1921 | |
sewardj | caf971d | 2005-09-29 21:20:41 +0000 | [diff] [blame] | 1922 | if (forClient && req->rkind == MFixed) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1923 | Int iLo = find_nsegment_idx(reqStart); |
| 1924 | Int iHi = find_nsegment_idx(reqEnd); |
| 1925 | Bool allow = True; |
| 1926 | for (i = iLo; i <= iHi; i++) { |
| 1927 | if (nsegments[i].kind == SkFree |
| 1928 | || nsegments[i].kind == SkFileC |
| 1929 | || nsegments[i].kind == SkAnonC |
tom | 1340c35 | 2005-10-04 15:59:54 +0000 | [diff] [blame] | 1930 | || nsegments[i].kind == SkShmC |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1931 | || nsegments[i].kind == SkResvn) { |
| 1932 | /* ok */ |
| 1933 | } else { |
| 1934 | allow = False; |
| 1935 | break; |
| 1936 | } |
| 1937 | } |
| 1938 | if (allow) { |
| 1939 | /* Acceptable. Granted. */ |
| 1940 | *ok = True; |
| 1941 | return reqStart; |
| 1942 | } |
sewardj | caf971d | 2005-09-29 21:20:41 +0000 | [diff] [blame] | 1943 | /* Not acceptable. Fail. */ |
| 1944 | *ok = False; |
| 1945 | return 0; |
| 1946 | } |
| 1947 | |
| 1948 | /* ------ Implement Policy Exception #2 ------ */ |
| 1949 | |
| 1950 | if (forClient && req->rkind == MHint) { |
| 1951 | Int iLo = find_nsegment_idx(reqStart); |
| 1952 | Int iHi = find_nsegment_idx(reqEnd); |
| 1953 | Bool allow = True; |
| 1954 | for (i = iLo; i <= iHi; i++) { |
| 1955 | if (nsegments[i].kind == SkFree |
| 1956 | || nsegments[i].kind == SkResvn) { |
| 1957 | /* ok */ |
| 1958 | } else { |
| 1959 | allow = False; |
| 1960 | break; |
| 1961 | } |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1962 | } |
sewardj | caf971d | 2005-09-29 21:20:41 +0000 | [diff] [blame] | 1963 | if (allow) { |
| 1964 | /* Acceptable. Granted. */ |
| 1965 | *ok = True; |
| 1966 | return reqStart; |
| 1967 | } |
| 1968 | /* Not acceptable. Fall through to the default policy. */ |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1969 | } |
| 1970 | |
| 1971 | /* ------ Implement the Default Policy ------ */ |
| 1972 | |
| 1973 | /* Don't waste time looking for a fixed match if not requested to. */ |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 1974 | fixed_not_required = req->rkind == MAny || req->rkind == MAlign; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 1975 | |
| 1976 | i = find_nsegment_idx(startPoint); |
| 1977 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 1978 | #if defined(VGO_solaris) |
| 1979 | # define UPDATE_INDEX(index) \ |
| 1980 | (index)--; \ |
| 1981 | if ((index) <= 0) \ |
| 1982 | (index) = nsegments_used - 1; |
| 1983 | # define ADVISE_ADDRESS(segment) \ |
| 1984 | VG_PGROUNDDN((segment)->end + 1 - reqLen) |
| 1985 | # define ADVISE_ADDRESS_ALIGNED(segment) \ |
| 1986 | VG_ROUNDDN((segment)->end + 1 - reqLen, req->start) |
| 1987 | |
| 1988 | #else |
| 1989 | |
| 1990 | # define UPDATE_INDEX(index) \ |
| 1991 | (index)++; \ |
| 1992 | if ((index) >= nsegments_used) \ |
| 1993 | (index) = 0; |
| 1994 | # define ADVISE_ADDRESS(segment) \ |
| 1995 | (segment)->start |
| 1996 | # define ADVISE_ADDRESS_ALIGNED(segment) \ |
| 1997 | VG_ROUNDUP((segment)->start, req->start) |
| 1998 | #endif /* VGO_solaris */ |
| 1999 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2000 | /* Examine holes from index i back round to i-1. Record the |
| 2001 | index first fixed hole and the first floating hole which would |
| 2002 | satisfy the request. */ |
| 2003 | for (j = 0; j < nsegments_used; j++) { |
| 2004 | |
| 2005 | if (nsegments[i].kind != SkFree) { |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 2006 | UPDATE_INDEX(i); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2007 | continue; |
| 2008 | } |
| 2009 | |
| 2010 | holeStart = nsegments[i].start; |
| 2011 | holeEnd = nsegments[i].end; |
| 2012 | |
| 2013 | /* Stay sane .. */ |
| 2014 | aspacem_assert(holeStart <= holeEnd); |
| 2015 | aspacem_assert(aspacem_minAddr <= holeStart); |
| 2016 | aspacem_assert(holeEnd <= aspacem_maxAddr); |
| 2017 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 2018 | if (req->rkind == MAlign) { |
| 2019 | holeStart = VG_ROUNDUP(holeStart, req->start); |
| 2020 | if (holeStart >= holeEnd) { |
| 2021 | /* This hole can't be used. */ |
| 2022 | UPDATE_INDEX(i); |
| 2023 | continue; |
| 2024 | } |
| 2025 | } |
| 2026 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2027 | /* See if it's any use to us. */ |
| 2028 | holeLen = holeEnd - holeStart + 1; |
| 2029 | |
| 2030 | if (fixedIdx == -1 && holeStart <= reqStart && reqEnd <= holeEnd) |
| 2031 | fixedIdx = i; |
| 2032 | |
| 2033 | if (floatIdx == -1 && holeLen >= reqLen) |
| 2034 | floatIdx = i; |
| 2035 | |
| 2036 | /* Don't waste time searching once we've found what we wanted. */ |
| 2037 | if ((fixed_not_required || fixedIdx >= 0) && floatIdx >= 0) |
| 2038 | break; |
| 2039 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 2040 | UPDATE_INDEX(i); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2041 | } |
| 2042 | |
| 2043 | aspacem_assert(fixedIdx >= -1 && fixedIdx < nsegments_used); |
| 2044 | if (fixedIdx >= 0) |
| 2045 | aspacem_assert(nsegments[fixedIdx].kind == SkFree); |
| 2046 | |
| 2047 | aspacem_assert(floatIdx >= -1 && floatIdx < nsegments_used); |
| 2048 | if (floatIdx >= 0) |
| 2049 | aspacem_assert(nsegments[floatIdx].kind == SkFree); |
| 2050 | |
| 2051 | AM_SANITY_CHECK; |
| 2052 | |
| 2053 | /* Now see if we found anything which can satisfy the request. */ |
| 2054 | switch (req->rkind) { |
| 2055 | case MFixed: |
| 2056 | if (fixedIdx >= 0) { |
| 2057 | *ok = True; |
| 2058 | return req->start; |
| 2059 | } else { |
| 2060 | *ok = False; |
| 2061 | return 0; |
| 2062 | } |
| 2063 | break; |
| 2064 | case MHint: |
| 2065 | if (fixedIdx >= 0) { |
| 2066 | *ok = True; |
| 2067 | return req->start; |
| 2068 | } |
| 2069 | if (floatIdx >= 0) { |
| 2070 | *ok = True; |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 2071 | return ADVISE_ADDRESS(&nsegments[floatIdx]); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2072 | } |
| 2073 | *ok = False; |
| 2074 | return 0; |
| 2075 | case MAny: |
| 2076 | if (floatIdx >= 0) { |
| 2077 | *ok = True; |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 2078 | return ADVISE_ADDRESS(&nsegments[floatIdx]); |
| 2079 | } |
| 2080 | *ok = False; |
| 2081 | return 0; |
| 2082 | case MAlign: |
| 2083 | if (floatIdx >= 0) { |
| 2084 | *ok = True; |
| 2085 | return ADVISE_ADDRESS_ALIGNED(&nsegments[floatIdx]); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2086 | } |
| 2087 | *ok = False; |
| 2088 | return 0; |
| 2089 | default: |
| 2090 | break; |
| 2091 | } |
| 2092 | |
| 2093 | /*NOTREACHED*/ |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 2094 | ML_(am_barf)("getAdvisory: unknown request kind"); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2095 | *ok = False; |
| 2096 | return 0; |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 2097 | |
| 2098 | #undef UPDATE_INDEX |
| 2099 | #undef ADVISE_ADDRESS |
| 2100 | #undef ADVISE_ADDRESS_ALIGNED |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2101 | } |
| 2102 | |
| 2103 | /* Convenience wrapper for VG_(am_get_advisory) for client floating or |
| 2104 | fixed requests. If start is zero, a floating request is issued; if |
| 2105 | nonzero, a fixed request at that address is issued. Same comments |
| 2106 | about return values apply. */ |
| 2107 | |
| 2108 | Addr VG_(am_get_advisory_client_simple) ( Addr start, SizeT len, |
| 2109 | /*OUT*/Bool* ok ) |
| 2110 | { |
| 2111 | MapRequest mreq; |
| 2112 | mreq.rkind = start==0 ? MAny : MFixed; |
| 2113 | mreq.start = start; |
| 2114 | mreq.len = len; |
philippe | 515e269 | 2012-01-08 21:01:02 +0000 | [diff] [blame] | 2115 | return VG_(am_get_advisory)( &mreq, True/*forClient*/, ok ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2116 | } |
| 2117 | |
philippe | 15e301e | 2011-12-22 13:25:58 +0000 | [diff] [blame] | 2118 | /* Similar to VG_(am_find_nsegment) but only returns free segments. */ |
| 2119 | static NSegment const * VG_(am_find_free_nsegment) ( Addr a ) |
| 2120 | { |
| 2121 | Int i = find_nsegment_idx(a); |
| 2122 | aspacem_assert(i >= 0 && i < nsegments_used); |
| 2123 | aspacem_assert(nsegments[i].start <= a); |
| 2124 | aspacem_assert(a <= nsegments[i].end); |
| 2125 | if (nsegments[i].kind == SkFree) |
| 2126 | return &nsegments[i]; |
| 2127 | else |
| 2128 | return NULL; |
| 2129 | } |
| 2130 | |
| 2131 | Bool VG_(am_covered_by_single_free_segment) |
| 2132 | ( Addr start, SizeT len) |
| 2133 | { |
| 2134 | NSegment const* segLo = VG_(am_find_free_nsegment)( start ); |
| 2135 | NSegment const* segHi = VG_(am_find_free_nsegment)( start + len - 1 ); |
| 2136 | |
| 2137 | return segLo != NULL && segHi != NULL && segLo == segHi; |
| 2138 | } |
| 2139 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2140 | |
| 2141 | /* Notifies aspacem that the client completed an mmap successfully. |
| 2142 | The segment array is updated accordingly. If the returned Bool is |
| 2143 | True, the caller should immediately discard translations from the |
| 2144 | specified address range. */ |
| 2145 | |
| 2146 | Bool |
| 2147 | VG_(am_notify_client_mmap)( Addr a, SizeT len, UInt prot, UInt flags, |
sewardj | 274461d | 2005-10-02 17:01:41 +0000 | [diff] [blame] | 2148 | Int fd, Off64T offset ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2149 | { |
| 2150 | HChar buf[VKI_PATH_MAX]; |
sewardj | 4190600 | 2008-08-18 21:47:11 +0000 | [diff] [blame] | 2151 | ULong dev, ino; |
tom | f4c2310 | 2005-10-31 17:05:21 +0000 | [diff] [blame] | 2152 | UInt mode; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2153 | NSegment seg; |
| 2154 | Bool needDiscard; |
| 2155 | |
| 2156 | aspacem_assert(len > 0); |
| 2157 | aspacem_assert(VG_IS_PAGE_ALIGNED(a)); |
| 2158 | aspacem_assert(VG_IS_PAGE_ALIGNED(len)); |
sewardj | 274461d | 2005-10-02 17:01:41 +0000 | [diff] [blame] | 2159 | aspacem_assert(VG_IS_PAGE_ALIGNED(offset)); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2160 | |
| 2161 | /* Discard is needed if any of the just-trashed range had T. */ |
| 2162 | needDiscard = any_Ts_in_range( a, len ); |
| 2163 | |
| 2164 | init_nsegment( &seg ); |
| 2165 | seg.kind = (flags & VKI_MAP_ANONYMOUS) ? SkAnonC : SkFileC; |
| 2166 | seg.start = a; |
| 2167 | seg.end = a + len - 1; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2168 | seg.hasR = toBool(prot & VKI_PROT_READ); |
| 2169 | seg.hasW = toBool(prot & VKI_PROT_WRITE); |
| 2170 | seg.hasX = toBool(prot & VKI_PROT_EXEC); |
| 2171 | if (!(flags & VKI_MAP_ANONYMOUS)) { |
njn | 4395ad4 | 2006-05-09 18:46:01 +0000 | [diff] [blame] | 2172 | // Nb: We ignore offset requests in anonymous mmaps (see bug #126722) |
tom | dadff0a | 2006-05-09 09:06:56 +0000 | [diff] [blame] | 2173 | seg.offset = offset; |
njn | dad944a | 2009-05-04 05:55:46 +0000 | [diff] [blame] | 2174 | if (ML_(am_get_fd_d_i_m)(fd, &dev, &ino, &mode)) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2175 | seg.dev = dev; |
| 2176 | seg.ino = ino; |
tom | f4c2310 | 2005-10-31 17:05:21 +0000 | [diff] [blame] | 2177 | seg.mode = mode; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2178 | } |
njn | dad944a | 2009-05-04 05:55:46 +0000 | [diff] [blame] | 2179 | if (ML_(am_resolve_filename)(fd, buf, VKI_PATH_MAX)) { |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 2180 | seg.fnIdx = ML_(am_allocate_segname)( buf ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2181 | } |
| 2182 | } |
| 2183 | add_segment( &seg ); |
| 2184 | AM_SANITY_CHECK; |
| 2185 | return needDiscard; |
| 2186 | } |
| 2187 | |
tom | 1340c35 | 2005-10-04 15:59:54 +0000 | [diff] [blame] | 2188 | /* Notifies aspacem that the client completed a shmat successfully. |
| 2189 | The segment array is updated accordingly. If the returned Bool is |
| 2190 | True, the caller should immediately discard translations from the |
| 2191 | specified address range. */ |
| 2192 | |
| 2193 | Bool |
| 2194 | VG_(am_notify_client_shmat)( Addr a, SizeT len, UInt prot ) |
| 2195 | { |
| 2196 | NSegment seg; |
| 2197 | Bool needDiscard; |
| 2198 | |
| 2199 | aspacem_assert(len > 0); |
| 2200 | aspacem_assert(VG_IS_PAGE_ALIGNED(a)); |
| 2201 | aspacem_assert(VG_IS_PAGE_ALIGNED(len)); |
| 2202 | |
| 2203 | /* Discard is needed if any of the just-trashed range had T. */ |
| 2204 | needDiscard = any_Ts_in_range( a, len ); |
| 2205 | |
| 2206 | init_nsegment( &seg ); |
| 2207 | seg.kind = SkShmC; |
| 2208 | seg.start = a; |
| 2209 | seg.end = a + len - 1; |
| 2210 | seg.offset = 0; |
| 2211 | seg.hasR = toBool(prot & VKI_PROT_READ); |
| 2212 | seg.hasW = toBool(prot & VKI_PROT_WRITE); |
| 2213 | seg.hasX = toBool(prot & VKI_PROT_EXEC); |
| 2214 | add_segment( &seg ); |
| 2215 | AM_SANITY_CHECK; |
| 2216 | return needDiscard; |
| 2217 | } |
| 2218 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2219 | /* Notifies aspacem that an mprotect was completed successfully. The |
| 2220 | segment array is updated accordingly. Note, as with |
| 2221 | VG_(am_notify_munmap), it is not the job of this function to reject |
| 2222 | stupid mprotects, for example the client doing mprotect of |
| 2223 | non-client areas. Such requests should be intercepted earlier, by |
| 2224 | the syscall wrapper for mprotect. This function merely records |
| 2225 | whatever it is told. If the returned Bool is True, the caller |
| 2226 | should immediately discard translations from the specified address |
| 2227 | range. */ |
| 2228 | |
| 2229 | Bool VG_(am_notify_mprotect)( Addr start, SizeT len, UInt prot ) |
| 2230 | { |
| 2231 | Int i, iLo, iHi; |
| 2232 | Bool newR, newW, newX, needDiscard; |
| 2233 | |
| 2234 | aspacem_assert(VG_IS_PAGE_ALIGNED(start)); |
| 2235 | aspacem_assert(VG_IS_PAGE_ALIGNED(len)); |
| 2236 | |
| 2237 | if (len == 0) |
| 2238 | return False; |
| 2239 | |
| 2240 | newR = toBool(prot & VKI_PROT_READ); |
| 2241 | newW = toBool(prot & VKI_PROT_WRITE); |
| 2242 | newX = toBool(prot & VKI_PROT_EXEC); |
| 2243 | |
| 2244 | /* Discard is needed if we're dumping X permission */ |
| 2245 | needDiscard = any_Ts_in_range( start, len ) && !newX; |
| 2246 | |
| 2247 | split_nsegments_lo_and_hi( start, start+len-1, &iLo, &iHi ); |
| 2248 | |
| 2249 | iLo = find_nsegment_idx(start); |
| 2250 | iHi = find_nsegment_idx(start + len - 1); |
| 2251 | |
| 2252 | for (i = iLo; i <= iHi; i++) { |
| 2253 | /* Apply the permissions to all relevant segments. */ |
| 2254 | switch (nsegments[i].kind) { |
tom | 1340c35 | 2005-10-04 15:59:54 +0000 | [diff] [blame] | 2255 | case SkAnonC: case SkAnonV: case SkFileC: case SkFileV: case SkShmC: |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2256 | nsegments[i].hasR = newR; |
| 2257 | nsegments[i].hasW = newW; |
| 2258 | nsegments[i].hasX = newX; |
| 2259 | aspacem_assert(sane_NSegment(&nsegments[i])); |
| 2260 | break; |
| 2261 | default: |
| 2262 | break; |
| 2263 | } |
| 2264 | } |
| 2265 | |
| 2266 | /* Changing permissions could have made previously un-mergable |
| 2267 | segments mergeable. Therefore have to re-preen them. */ |
| 2268 | (void)preen_nsegments(); |
| 2269 | AM_SANITY_CHECK; |
| 2270 | return needDiscard; |
| 2271 | } |
| 2272 | |
| 2273 | |
| 2274 | /* Notifies aspacem that an munmap completed successfully. The |
| 2275 | segment array is updated accordingly. As with |
florian | 135c9f1 | 2015-05-22 09:17:28 +0000 | [diff] [blame] | 2276 | VG_(am_notify_mprotect), we merely record the given info, and don't |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2277 | check it for sensibleness. If the returned Bool is True, the |
| 2278 | caller should immediately discard translations from the specified |
| 2279 | address range. */ |
| 2280 | |
| 2281 | Bool VG_(am_notify_munmap)( Addr start, SizeT len ) |
| 2282 | { |
| 2283 | NSegment seg; |
| 2284 | Bool needDiscard; |
| 2285 | aspacem_assert(VG_IS_PAGE_ALIGNED(start)); |
| 2286 | aspacem_assert(VG_IS_PAGE_ALIGNED(len)); |
| 2287 | |
| 2288 | if (len == 0) |
| 2289 | return False; |
| 2290 | |
| 2291 | needDiscard = any_Ts_in_range( start, len ); |
| 2292 | |
| 2293 | init_nsegment( &seg ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2294 | seg.start = start; |
| 2295 | seg.end = start + len - 1; |
sewardj | 613a9f2 | 2006-08-16 01:48:19 +0000 | [diff] [blame] | 2296 | |
| 2297 | /* The segment becomes unused (free). Segments from above |
| 2298 | aspacem_maxAddr were originally SkResvn and so we make them so |
| 2299 | again. Note, this isn't really right when the segment straddles |
| 2300 | the aspacem_maxAddr boundary - then really it should be split in |
| 2301 | two, the lower part marked as SkFree and the upper part as |
| 2302 | SkResvn. Ah well. */ |
| 2303 | if (start > aspacem_maxAddr |
| 2304 | && /* check previous comparison is meaningful */ |
| 2305 | aspacem_maxAddr < Addr_MAX) |
| 2306 | seg.kind = SkResvn; |
sewardj | 7cc2040 | 2006-09-11 19:49:35 +0000 | [diff] [blame] | 2307 | else |
| 2308 | /* Ditto for segments from below aspacem_minAddr. */ |
| 2309 | if (seg.end < aspacem_minAddr && aspacem_minAddr > 0) |
| 2310 | seg.kind = SkResvn; |
sewardj | 613a9f2 | 2006-08-16 01:48:19 +0000 | [diff] [blame] | 2311 | else |
| 2312 | seg.kind = SkFree; |
| 2313 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2314 | add_segment( &seg ); |
| 2315 | |
| 2316 | /* Unmapping could create two adjacent free segments, so a preen is |
| 2317 | needed. add_segment() will do that, so no need to here. */ |
| 2318 | AM_SANITY_CHECK; |
| 2319 | return needDiscard; |
| 2320 | } |
| 2321 | |
| 2322 | |
| 2323 | /*-----------------------------------------------------------------*/ |
| 2324 | /*--- ---*/ |
| 2325 | /*--- Handling mappings which do not arise directly from the ---*/ |
| 2326 | /*--- simulation of the client. ---*/ |
| 2327 | /*--- ---*/ |
| 2328 | /*-----------------------------------------------------------------*/ |
| 2329 | |
| 2330 | /* --- --- --- map, unmap, protect --- --- --- */ |
| 2331 | |
| 2332 | /* Map a file at a fixed address for the client, and update the |
| 2333 | segment array accordingly. */ |
| 2334 | |
| 2335 | SysRes VG_(am_mmap_file_fixed_client) |
sewardj | 274461d | 2005-10-02 17:01:41 +0000 | [diff] [blame] | 2336 | ( Addr start, SizeT length, UInt prot, Int fd, Off64T offset ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2337 | { |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 2338 | UInt flags = VKI_MAP_FIXED | VKI_MAP_PRIVATE; |
| 2339 | return VG_(am_mmap_named_file_fixed_client_flags)(start, length, prot, flags, |
| 2340 | fd, offset, NULL); |
| 2341 | } |
| 2342 | |
| 2343 | SysRes VG_(am_mmap_file_fixed_client_flags) |
| 2344 | ( Addr start, SizeT length, UInt prot, UInt flags, Int fd, Off64T offset ) |
| 2345 | { |
| 2346 | return VG_(am_mmap_named_file_fixed_client_flags)(start, length, prot, flags, |
| 2347 | fd, offset, NULL); |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 2348 | } |
| 2349 | |
| 2350 | SysRes VG_(am_mmap_named_file_fixed_client) |
| 2351 | ( Addr start, SizeT length, UInt prot, Int fd, Off64T offset, const HChar *name ) |
| 2352 | { |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 2353 | UInt flags = VKI_MAP_FIXED | VKI_MAP_PRIVATE; |
| 2354 | return VG_(am_mmap_named_file_fixed_client_flags)(start, length, prot, flags, |
| 2355 | fd, offset, name); |
| 2356 | } |
| 2357 | |
| 2358 | SysRes VG_(am_mmap_named_file_fixed_client_flags) |
| 2359 | ( Addr start, SizeT length, UInt prot, UInt flags, |
| 2360 | Int fd, Off64T offset, const HChar *name ) |
| 2361 | { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2362 | SysRes sres; |
| 2363 | NSegment seg; |
| 2364 | Addr advised; |
| 2365 | Bool ok; |
| 2366 | MapRequest req; |
sewardj | 4190600 | 2008-08-18 21:47:11 +0000 | [diff] [blame] | 2367 | ULong dev, ino; |
tom | f4c2310 | 2005-10-31 17:05:21 +0000 | [diff] [blame] | 2368 | UInt mode; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2369 | HChar buf[VKI_PATH_MAX]; |
| 2370 | |
| 2371 | /* Not allowable. */ |
sewardj | 274461d | 2005-10-02 17:01:41 +0000 | [diff] [blame] | 2372 | if (length == 0 |
| 2373 | || !VG_IS_PAGE_ALIGNED(start) |
| 2374 | || !VG_IS_PAGE_ALIGNED(offset)) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2375 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2376 | |
| 2377 | /* Ask for an advisory. If it's negative, fail immediately. */ |
| 2378 | req.rkind = MFixed; |
| 2379 | req.start = start; |
| 2380 | req.len = length; |
philippe | 515e269 | 2012-01-08 21:01:02 +0000 | [diff] [blame] | 2381 | advised = VG_(am_get_advisory)( &req, True/*forClient*/, &ok ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2382 | if (!ok || advised != start) |
| 2383 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2384 | |
| 2385 | /* We have been advised that the mapping is allowable at the |
| 2386 | specified address. So hand it off to the kernel, and propagate |
| 2387 | any resulting failure immediately. */ |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 2388 | // DDD: #warning GrP fixme MAP_FIXED can clobber memory! |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2389 | sres = VG_(am_do_mmap_NO_NOTIFY)( |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 2390 | start, length, prot, flags, |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2391 | fd, offset |
| 2392 | ); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2393 | if (sr_isError(sres)) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2394 | return sres; |
| 2395 | |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2396 | if (sr_Res(sres) != start) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2397 | /* I don't think this can happen. It means the kernel made a |
| 2398 | fixed map succeed but not at the requested location. Try to |
| 2399 | repair the damage, then return saying the mapping failed. */ |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2400 | (void)ML_(am_do_munmap_NO_NOTIFY)( sr_Res(sres), length ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2401 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2402 | } |
| 2403 | |
| 2404 | /* Ok, the mapping succeeded. Now notify the interval map. */ |
| 2405 | init_nsegment( &seg ); |
| 2406 | seg.kind = SkFileC; |
| 2407 | seg.start = start; |
| 2408 | seg.end = seg.start + VG_PGROUNDUP(length) - 1; |
| 2409 | seg.offset = offset; |
| 2410 | seg.hasR = toBool(prot & VKI_PROT_READ); |
| 2411 | seg.hasW = toBool(prot & VKI_PROT_WRITE); |
| 2412 | seg.hasX = toBool(prot & VKI_PROT_EXEC); |
njn | dad944a | 2009-05-04 05:55:46 +0000 | [diff] [blame] | 2413 | if (ML_(am_get_fd_d_i_m)(fd, &dev, &ino, &mode)) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2414 | seg.dev = dev; |
| 2415 | seg.ino = ino; |
tom | f4c2310 | 2005-10-31 17:05:21 +0000 | [diff] [blame] | 2416 | seg.mode = mode; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2417 | } |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 2418 | if (name) { |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 2419 | seg.fnIdx = ML_(am_allocate_segname)( name ); |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 2420 | } else if (ML_(am_resolve_filename)(fd, buf, VKI_PATH_MAX)) { |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 2421 | seg.fnIdx = ML_(am_allocate_segname)( buf ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2422 | } |
| 2423 | add_segment( &seg ); |
| 2424 | |
| 2425 | AM_SANITY_CHECK; |
| 2426 | return sres; |
| 2427 | } |
| 2428 | |
| 2429 | |
| 2430 | /* Map anonymously at a fixed address for the client, and update |
| 2431 | the segment array accordingly. */ |
| 2432 | |
| 2433 | SysRes VG_(am_mmap_anon_fixed_client) ( Addr start, SizeT length, UInt prot ) |
| 2434 | { |
| 2435 | SysRes sres; |
| 2436 | NSegment seg; |
| 2437 | Addr advised; |
| 2438 | Bool ok; |
| 2439 | MapRequest req; |
| 2440 | |
| 2441 | /* Not allowable. */ |
| 2442 | if (length == 0 || !VG_IS_PAGE_ALIGNED(start)) |
| 2443 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2444 | |
| 2445 | /* Ask for an advisory. If it's negative, fail immediately. */ |
| 2446 | req.rkind = MFixed; |
| 2447 | req.start = start; |
| 2448 | req.len = length; |
philippe | 515e269 | 2012-01-08 21:01:02 +0000 | [diff] [blame] | 2449 | advised = VG_(am_get_advisory)( &req, True/*forClient*/, &ok ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2450 | if (!ok || advised != start) |
| 2451 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2452 | |
| 2453 | /* We have been advised that the mapping is allowable at the |
| 2454 | specified address. So hand it off to the kernel, and propagate |
| 2455 | any resulting failure immediately. */ |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 2456 | // DDD: #warning GrP fixme MAP_FIXED can clobber memory! |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2457 | sres = VG_(am_do_mmap_NO_NOTIFY)( |
| 2458 | start, length, prot, |
| 2459 | VKI_MAP_FIXED|VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS, |
| 2460 | 0, 0 |
| 2461 | ); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2462 | if (sr_isError(sres)) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2463 | return sres; |
| 2464 | |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2465 | if (sr_Res(sres) != start) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2466 | /* I don't think this can happen. It means the kernel made a |
| 2467 | fixed map succeed but not at the requested location. Try to |
| 2468 | repair the damage, then return saying the mapping failed. */ |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2469 | (void)ML_(am_do_munmap_NO_NOTIFY)( sr_Res(sres), length ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2470 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2471 | } |
| 2472 | |
| 2473 | /* Ok, the mapping succeeded. Now notify the interval map. */ |
| 2474 | init_nsegment( &seg ); |
| 2475 | seg.kind = SkAnonC; |
| 2476 | seg.start = start; |
| 2477 | seg.end = seg.start + VG_PGROUNDUP(length) - 1; |
| 2478 | seg.hasR = toBool(prot & VKI_PROT_READ); |
| 2479 | seg.hasW = toBool(prot & VKI_PROT_WRITE); |
| 2480 | seg.hasX = toBool(prot & VKI_PROT_EXEC); |
| 2481 | add_segment( &seg ); |
| 2482 | |
| 2483 | AM_SANITY_CHECK; |
| 2484 | return sres; |
| 2485 | } |
| 2486 | |
| 2487 | |
| 2488 | /* Map anonymously at an unconstrained address for the client, and |
| 2489 | update the segment array accordingly. */ |
| 2490 | |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame^] | 2491 | static SysRes am_mmap_anon_float_client ( SizeT length, Int prot, Bool isCH ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2492 | { |
| 2493 | SysRes sres; |
| 2494 | NSegment seg; |
| 2495 | Addr advised; |
| 2496 | Bool ok; |
| 2497 | MapRequest req; |
| 2498 | |
| 2499 | /* Not allowable. */ |
| 2500 | if (length == 0) |
| 2501 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2502 | |
| 2503 | /* Ask for an advisory. If it's negative, fail immediately. */ |
| 2504 | req.rkind = MAny; |
| 2505 | req.start = 0; |
| 2506 | req.len = length; |
philippe | 515e269 | 2012-01-08 21:01:02 +0000 | [diff] [blame] | 2507 | advised = VG_(am_get_advisory)( &req, True/*forClient*/, &ok ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2508 | if (!ok) |
| 2509 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2510 | |
| 2511 | /* We have been advised that the mapping is allowable at the |
| 2512 | advised address. So hand it off to the kernel, and propagate |
| 2513 | any resulting failure immediately. */ |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 2514 | // DDD: #warning GrP fixme MAP_FIXED can clobber memory! |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2515 | sres = VG_(am_do_mmap_NO_NOTIFY)( |
| 2516 | advised, length, prot, |
| 2517 | VKI_MAP_FIXED|VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS, |
| 2518 | 0, 0 |
| 2519 | ); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2520 | if (sr_isError(sres)) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2521 | return sres; |
| 2522 | |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2523 | if (sr_Res(sres) != advised) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2524 | /* I don't think this can happen. It means the kernel made a |
| 2525 | fixed map succeed but not at the requested location. Try to |
| 2526 | repair the damage, then return saying the mapping failed. */ |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2527 | (void)ML_(am_do_munmap_NO_NOTIFY)( sr_Res(sres), length ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2528 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2529 | } |
| 2530 | |
| 2531 | /* Ok, the mapping succeeded. Now notify the interval map. */ |
| 2532 | init_nsegment( &seg ); |
| 2533 | seg.kind = SkAnonC; |
| 2534 | seg.start = advised; |
| 2535 | seg.end = seg.start + VG_PGROUNDUP(length) - 1; |
| 2536 | seg.hasR = toBool(prot & VKI_PROT_READ); |
| 2537 | seg.hasW = toBool(prot & VKI_PROT_WRITE); |
| 2538 | seg.hasX = toBool(prot & VKI_PROT_EXEC); |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame^] | 2539 | seg.isCH = isCH; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2540 | add_segment( &seg ); |
| 2541 | |
| 2542 | AM_SANITY_CHECK; |
| 2543 | return sres; |
| 2544 | } |
| 2545 | |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame^] | 2546 | SysRes VG_(am_mmap_anon_float_client) ( SizeT length, Int prot ) |
| 2547 | { |
| 2548 | return am_mmap_anon_float_client (length, prot, False /* isCH */); |
| 2549 | } |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2550 | |
| 2551 | /* Map anonymously at an unconstrained address for V, and update the |
| 2552 | segment array accordingly. This is fundamentally how V allocates |
| 2553 | itself more address space when needed. */ |
| 2554 | |
| 2555 | SysRes VG_(am_mmap_anon_float_valgrind)( SizeT length ) |
| 2556 | { |
| 2557 | SysRes sres; |
| 2558 | NSegment seg; |
| 2559 | Addr advised; |
| 2560 | Bool ok; |
| 2561 | MapRequest req; |
| 2562 | |
| 2563 | /* Not allowable. */ |
| 2564 | if (length == 0) |
| 2565 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2566 | |
| 2567 | /* Ask for an advisory. If it's negative, fail immediately. */ |
| 2568 | req.rkind = MAny; |
| 2569 | req.start = 0; |
| 2570 | req.len = length; |
philippe | 515e269 | 2012-01-08 21:01:02 +0000 | [diff] [blame] | 2571 | advised = VG_(am_get_advisory)( &req, False/*forClient*/, &ok ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2572 | if (!ok) |
| 2573 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2574 | |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 2575 | // On Darwin, for anonymous maps you can pass in a tag which is used by |
| 2576 | // programs like vmmap for statistical purposes. |
| 2577 | #ifndef VM_TAG_VALGRIND |
| 2578 | # define VM_TAG_VALGRIND 0 |
| 2579 | #endif |
| 2580 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2581 | /* We have been advised that the mapping is allowable at the |
| 2582 | specified address. So hand it off to the kernel, and propagate |
| 2583 | any resulting failure immediately. */ |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 2584 | /* GrP fixme darwin: use advisory as a hint only, otherwise syscall in |
| 2585 | another thread can pre-empt our spot. [At one point on the DARWIN |
| 2586 | branch the VKI_MAP_FIXED was commented out; unclear if this is |
| 2587 | necessary or not given the second Darwin-only call that immediately |
philippe | 3bf117d | 2013-08-12 22:17:47 +0000 | [diff] [blame] | 2588 | follows if this one fails. --njn] |
| 2589 | Also, an inner valgrind cannot observe the mmap syscalls done by |
| 2590 | the outer valgrind. The outer Valgrind might make the mmap |
| 2591 | fail here, as the inner valgrind believes that a segment is free, |
| 2592 | while it is in fact used by the outer valgrind. |
| 2593 | So, for an inner valgrind, similarly to DARWIN, if the fixed mmap |
| 2594 | fails, retry the mmap without map fixed. |
| 2595 | This is a kludge which on linux is only activated for the inner. |
| 2596 | The state of the inner aspacemgr is not made correct by this kludge |
| 2597 | and so a.o. VG_(am_do_sync_check) could fail. |
| 2598 | A proper solution implies a better collaboration between the |
| 2599 | inner and the outer (e.g. inner VG_(am_get_advisory) should do |
| 2600 | a client request to call the outer VG_(am_get_advisory). */ |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2601 | sres = VG_(am_do_mmap_NO_NOTIFY)( |
| 2602 | advised, length, |
| 2603 | VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC, |
| 2604 | VKI_MAP_FIXED|VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS, |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 2605 | VM_TAG_VALGRIND, 0 |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2606 | ); |
philippe | 3bf117d | 2013-08-12 22:17:47 +0000 | [diff] [blame] | 2607 | #if defined(VGO_darwin) || defined(ENABLE_INNER) |
| 2608 | /* Kludge on Darwin and inner linux if the fixed mmap failed. */ |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 2609 | if (sr_isError(sres)) { |
| 2610 | /* try again, ignoring the advisory */ |
| 2611 | sres = VG_(am_do_mmap_NO_NOTIFY)( |
| 2612 | 0, length, |
| 2613 | VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC, |
| 2614 | /*VKI_MAP_FIXED|*/VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS, |
| 2615 | VM_TAG_VALGRIND, 0 |
| 2616 | ); |
| 2617 | } |
| 2618 | #endif |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2619 | if (sr_isError(sres)) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2620 | return sres; |
| 2621 | |
philippe | 3bf117d | 2013-08-12 22:17:47 +0000 | [diff] [blame] | 2622 | #if defined(VGO_linux) && !defined(ENABLE_INNER) |
| 2623 | /* Doing the check only in linux not inner, as the below |
| 2624 | check can fail when the kludge above has been used. */ |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2625 | if (sr_Res(sres) != advised) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2626 | /* I don't think this can happen. It means the kernel made a |
| 2627 | fixed map succeed but not at the requested location. Try to |
| 2628 | repair the damage, then return saying the mapping failed. */ |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2629 | (void)ML_(am_do_munmap_NO_NOTIFY)( sr_Res(sres), length ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2630 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2631 | } |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 2632 | #endif |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2633 | |
| 2634 | /* Ok, the mapping succeeded. Now notify the interval map. */ |
| 2635 | init_nsegment( &seg ); |
| 2636 | seg.kind = SkAnonV; |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2637 | seg.start = sr_Res(sres); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2638 | seg.end = seg.start + VG_PGROUNDUP(length) - 1; |
| 2639 | seg.hasR = True; |
| 2640 | seg.hasW = True; |
| 2641 | seg.hasX = True; |
| 2642 | add_segment( &seg ); |
| 2643 | |
| 2644 | AM_SANITY_CHECK; |
| 2645 | return sres; |
| 2646 | } |
| 2647 | |
| 2648 | /* Really just a wrapper around VG_(am_mmap_anon_float_valgrind). */ |
| 2649 | |
| 2650 | void* VG_(am_shadow_alloc)(SizeT size) |
| 2651 | { |
| 2652 | SysRes sres = VG_(am_mmap_anon_float_valgrind)( size ); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2653 | return sr_isError(sres) ? NULL : (void*)sr_Res(sres); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2654 | } |
| 2655 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2656 | /* Map a file at an unconstrained address for V, and update the |
sewardj | 3b29048 | 2011-05-06 21:02:55 +0000 | [diff] [blame] | 2657 | segment array accordingly. Use the provided flags */ |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2658 | |
sewardj | 3b29048 | 2011-05-06 21:02:55 +0000 | [diff] [blame] | 2659 | static SysRes VG_(am_mmap_file_float_valgrind_flags) ( SizeT length, UInt prot, |
| 2660 | UInt flags, |
| 2661 | Int fd, Off64T offset ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2662 | { |
| 2663 | SysRes sres; |
| 2664 | NSegment seg; |
| 2665 | Addr advised; |
| 2666 | Bool ok; |
| 2667 | MapRequest req; |
sewardj | 4190600 | 2008-08-18 21:47:11 +0000 | [diff] [blame] | 2668 | ULong dev, ino; |
tom | f4c2310 | 2005-10-31 17:05:21 +0000 | [diff] [blame] | 2669 | UInt mode; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2670 | HChar buf[VKI_PATH_MAX]; |
| 2671 | |
| 2672 | /* Not allowable. */ |
sewardj | 274461d | 2005-10-02 17:01:41 +0000 | [diff] [blame] | 2673 | if (length == 0 || !VG_IS_PAGE_ALIGNED(offset)) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2674 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2675 | |
| 2676 | /* Ask for an advisory. If it's negative, fail immediately. */ |
| 2677 | req.rkind = MAny; |
| 2678 | req.start = 0; |
philippe | 0eb0d5a | 2014-02-11 23:50:16 +0000 | [diff] [blame] | 2679 | #if defined(VGA_arm) || defined(VGA_arm64) \ |
| 2680 | || defined(VGA_mips32) || defined(VGA_mips64) |
philippe | 53b0d9a | 2012-02-02 21:33:55 +0000 | [diff] [blame] | 2681 | aspacem_assert(VKI_SHMLBA >= VKI_PAGE_SIZE); |
| 2682 | #else |
| 2683 | aspacem_assert(VKI_SHMLBA == VKI_PAGE_SIZE); |
| 2684 | #endif |
| 2685 | if ((VKI_SHMLBA > VKI_PAGE_SIZE) && (VKI_MAP_SHARED & flags)) { |
| 2686 | /* arm-linux only. See ML_(generic_PRE_sys_shmat) and bug 290974 */ |
| 2687 | req.len = length + VKI_SHMLBA - VKI_PAGE_SIZE; |
| 2688 | } else { |
| 2689 | req.len = length; |
| 2690 | } |
philippe | 515e269 | 2012-01-08 21:01:02 +0000 | [diff] [blame] | 2691 | advised = VG_(am_get_advisory)( &req, False/*forClient*/, &ok ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2692 | if (!ok) |
| 2693 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
philippe | 53b0d9a | 2012-02-02 21:33:55 +0000 | [diff] [blame] | 2694 | if ((VKI_SHMLBA > VKI_PAGE_SIZE) && (VKI_MAP_SHARED & flags)) |
| 2695 | advised = VG_ROUNDUP(advised, VKI_SHMLBA); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2696 | |
| 2697 | /* We have been advised that the mapping is allowable at the |
| 2698 | specified address. So hand it off to the kernel, and propagate |
| 2699 | any resulting failure immediately. */ |
| 2700 | sres = VG_(am_do_mmap_NO_NOTIFY)( |
| 2701 | advised, length, prot, |
sewardj | 3b29048 | 2011-05-06 21:02:55 +0000 | [diff] [blame] | 2702 | flags, |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2703 | fd, offset |
| 2704 | ); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2705 | if (sr_isError(sres)) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2706 | return sres; |
| 2707 | |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2708 | if (sr_Res(sres) != advised) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2709 | /* I don't think this can happen. It means the kernel made a |
| 2710 | fixed map succeed but not at the requested location. Try to |
| 2711 | repair the damage, then return saying the mapping failed. */ |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2712 | (void)ML_(am_do_munmap_NO_NOTIFY)( sr_Res(sres), length ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2713 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2714 | } |
| 2715 | |
| 2716 | /* Ok, the mapping succeeded. Now notify the interval map. */ |
| 2717 | init_nsegment( &seg ); |
| 2718 | seg.kind = SkFileV; |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2719 | seg.start = sr_Res(sres); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2720 | seg.end = seg.start + VG_PGROUNDUP(length) - 1; |
| 2721 | seg.offset = offset; |
| 2722 | seg.hasR = toBool(prot & VKI_PROT_READ); |
| 2723 | seg.hasW = toBool(prot & VKI_PROT_WRITE); |
| 2724 | seg.hasX = toBool(prot & VKI_PROT_EXEC); |
njn | dad944a | 2009-05-04 05:55:46 +0000 | [diff] [blame] | 2725 | if (ML_(am_get_fd_d_i_m)(fd, &dev, &ino, &mode)) { |
sewardj | cf4ac71 | 2005-11-01 00:03:40 +0000 | [diff] [blame] | 2726 | seg.dev = dev; |
| 2727 | seg.ino = ino; |
| 2728 | seg.mode = mode; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2729 | } |
njn | dad944a | 2009-05-04 05:55:46 +0000 | [diff] [blame] | 2730 | if (ML_(am_resolve_filename)(fd, buf, VKI_PATH_MAX)) { |
florian | 4ecd483 | 2015-04-30 17:34:04 +0000 | [diff] [blame] | 2731 | seg.fnIdx = ML_(am_allocate_segname)( buf ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2732 | } |
| 2733 | add_segment( &seg ); |
| 2734 | |
| 2735 | AM_SANITY_CHECK; |
| 2736 | return sres; |
| 2737 | } |
sewardj | 3b29048 | 2011-05-06 21:02:55 +0000 | [diff] [blame] | 2738 | /* Map privately a file at an unconstrained address for V, and update the |
| 2739 | segment array accordingly. This is used by V for transiently |
| 2740 | mapping in object files to read their debug info. */ |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2741 | |
sewardj | 3b29048 | 2011-05-06 21:02:55 +0000 | [diff] [blame] | 2742 | SysRes VG_(am_mmap_file_float_valgrind) ( SizeT length, UInt prot, |
| 2743 | Int fd, Off64T offset ) |
| 2744 | { |
| 2745 | return VG_(am_mmap_file_float_valgrind_flags) (length, prot, |
| 2746 | VKI_MAP_FIXED|VKI_MAP_PRIVATE, |
| 2747 | fd, offset ); |
| 2748 | } |
| 2749 | |
sewardj | d663333 | 2012-06-28 19:29:01 +0000 | [diff] [blame] | 2750 | SysRes VG_(am_shared_mmap_file_float_valgrind) |
sewardj | 3b29048 | 2011-05-06 21:02:55 +0000 | [diff] [blame] | 2751 | ( SizeT length, UInt prot, Int fd, Off64T offset ) |
| 2752 | { |
| 2753 | return VG_(am_mmap_file_float_valgrind_flags) (length, prot, |
| 2754 | VKI_MAP_FIXED|VKI_MAP_SHARED, |
| 2755 | fd, offset ); |
| 2756 | } |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2757 | |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame^] | 2758 | /* Similar to VG_(am_mmap_anon_float_client) but also |
florian | 2fa66ce | 2015-03-07 23:01:14 +0000 | [diff] [blame] | 2759 | marks the segment as containing the client heap. This is for the benefit |
| 2760 | of the leak checker which needs to be able to identify such segments |
| 2761 | so as not to use them as sources of roots during leak checks. */ |
| 2762 | SysRes VG_(am_mmap_client_heap) ( SizeT length, Int prot ) |
| 2763 | { |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame^] | 2764 | return am_mmap_anon_float_client (length, prot, True /* isCH */); |
florian | 2fa66ce | 2015-03-07 23:01:14 +0000 | [diff] [blame] | 2765 | } |
| 2766 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2767 | /* --- --- munmap helper --- --- */ |
| 2768 | |
| 2769 | static |
| 2770 | SysRes am_munmap_both_wrk ( /*OUT*/Bool* need_discard, |
| 2771 | Addr start, SizeT len, Bool forClient ) |
| 2772 | { |
| 2773 | Bool d; |
| 2774 | SysRes sres; |
| 2775 | |
| 2776 | if (!VG_IS_PAGE_ALIGNED(start)) |
| 2777 | goto eINVAL; |
| 2778 | |
| 2779 | if (len == 0) { |
| 2780 | *need_discard = False; |
| 2781 | return VG_(mk_SysRes_Success)( 0 ); |
| 2782 | } |
| 2783 | |
| 2784 | if (start + len < len) |
| 2785 | goto eINVAL; |
| 2786 | |
| 2787 | len = VG_PGROUNDUP(len); |
| 2788 | aspacem_assert(VG_IS_PAGE_ALIGNED(start)); |
| 2789 | aspacem_assert(VG_IS_PAGE_ALIGNED(len)); |
| 2790 | |
| 2791 | if (forClient) { |
| 2792 | if (!VG_(am_is_valid_for_client_or_free_or_resvn) |
| 2793 | ( start, len, VKI_PROT_NONE )) |
| 2794 | goto eINVAL; |
| 2795 | } else { |
philippe | adfff76 | 2014-04-20 22:10:24 +0000 | [diff] [blame] | 2796 | if (!VG_(am_is_valid_for_valgrind) |
| 2797 | ( start, len, VKI_PROT_NONE )) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2798 | goto eINVAL; |
| 2799 | } |
| 2800 | |
| 2801 | d = any_Ts_in_range( start, len ); |
| 2802 | |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 2803 | sres = ML_(am_do_munmap_NO_NOTIFY)( start, len ); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2804 | if (sr_isError(sres)) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2805 | return sres; |
| 2806 | |
| 2807 | VG_(am_notify_munmap)( start, len ); |
| 2808 | AM_SANITY_CHECK; |
| 2809 | *need_discard = d; |
| 2810 | return sres; |
| 2811 | |
| 2812 | eINVAL: |
| 2813 | return VG_(mk_SysRes_Error)( VKI_EINVAL ); |
| 2814 | } |
| 2815 | |
| 2816 | /* Unmap the given address range and update the segment array |
| 2817 | accordingly. This fails if the range isn't valid for the client. |
| 2818 | If *need_discard is True after a successful return, the caller |
| 2819 | should immediately discard translations from the specified address |
| 2820 | range. */ |
| 2821 | |
| 2822 | SysRes VG_(am_munmap_client)( /*OUT*/Bool* need_discard, |
| 2823 | Addr start, SizeT len ) |
| 2824 | { |
| 2825 | return am_munmap_both_wrk( need_discard, start, len, True/*client*/ ); |
| 2826 | } |
| 2827 | |
| 2828 | /* Unmap the given address range and update the segment array |
| 2829 | accordingly. This fails if the range isn't valid for valgrind. */ |
| 2830 | |
| 2831 | SysRes VG_(am_munmap_valgrind)( Addr start, SizeT len ) |
| 2832 | { |
| 2833 | Bool need_discard; |
| 2834 | SysRes r = am_munmap_both_wrk( &need_discard, |
| 2835 | start, len, False/*valgrind*/ ); |
| 2836 | /* If this assertion fails, it means we allowed translations to be |
| 2837 | made from a V-owned section. Which shouldn't happen. */ |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 2838 | if (!sr_isError(r)) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2839 | aspacem_assert(!need_discard); |
| 2840 | return r; |
| 2841 | } |
| 2842 | |
| 2843 | /* Let (start,len) denote an area within a single Valgrind-owned |
| 2844 | segment (anon or file). Change the ownership of [start, start+len) |
| 2845 | to the client instead. Fails if (start,len) does not denote a |
| 2846 | suitable segment. */ |
| 2847 | |
| 2848 | Bool VG_(am_change_ownership_v_to_c)( Addr start, SizeT len ) |
| 2849 | { |
| 2850 | Int i, iLo, iHi; |
| 2851 | |
| 2852 | if (len == 0) |
| 2853 | return True; |
| 2854 | if (start + len < start) |
| 2855 | return False; |
| 2856 | if (!VG_IS_PAGE_ALIGNED(start) || !VG_IS_PAGE_ALIGNED(len)) |
| 2857 | return False; |
| 2858 | |
| 2859 | i = find_nsegment_idx(start); |
| 2860 | if (nsegments[i].kind != SkFileV && nsegments[i].kind != SkAnonV) |
| 2861 | return False; |
| 2862 | if (start+len-1 > nsegments[i].end) |
| 2863 | return False; |
| 2864 | |
| 2865 | aspacem_assert(start >= nsegments[i].start); |
| 2866 | aspacem_assert(start+len-1 <= nsegments[i].end); |
| 2867 | |
| 2868 | /* This scheme is like how mprotect works: split the to-be-changed |
| 2869 | range into its own segment(s), then mess with them (it). There |
| 2870 | should be only one. */ |
| 2871 | split_nsegments_lo_and_hi( start, start+len-1, &iLo, &iHi ); |
| 2872 | aspacem_assert(iLo == iHi); |
| 2873 | switch (nsegments[iLo].kind) { |
| 2874 | case SkFileV: nsegments[iLo].kind = SkFileC; break; |
| 2875 | case SkAnonV: nsegments[iLo].kind = SkAnonC; break; |
| 2876 | default: aspacem_assert(0); /* can't happen - guarded above */ |
| 2877 | } |
| 2878 | |
| 2879 | preen_nsegments(); |
| 2880 | return True; |
| 2881 | } |
| 2882 | |
florian | 2fa66ce | 2015-03-07 23:01:14 +0000 | [diff] [blame] | 2883 | /* Set the 'hasT' bit on the segment containing ADDR indicating that |
| 2884 | translations have or may have been taken from this segment. ADDR is |
| 2885 | expected to belong to a client segment. */ |
| 2886 | void VG_(am_set_segment_hasT)( Addr addr ) |
sewardj | 716f31a | 2006-10-17 01:23:57 +0000 | [diff] [blame] | 2887 | { |
florian | 2fa66ce | 2015-03-07 23:01:14 +0000 | [diff] [blame] | 2888 | Int i = find_nsegment_idx(addr); |
| 2889 | SegKind kind = nsegments[i].kind; |
| 2890 | aspacem_assert(kind == SkAnonC || kind == SkFileC || kind == SkShmC); |
| 2891 | nsegments[i].hasT = True; |
sewardj | 716f31a | 2006-10-17 01:23:57 +0000 | [diff] [blame] | 2892 | } |
| 2893 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2894 | |
| 2895 | /* --- --- --- reservations --- --- --- */ |
| 2896 | |
| 2897 | /* Create a reservation from START .. START+LENGTH-1, with the given |
| 2898 | ShrinkMode. When checking whether the reservation can be created, |
| 2899 | also ensure that at least abs(EXTRA) extra free bytes will remain |
| 2900 | above (> 0) or below (< 0) the reservation. |
| 2901 | |
| 2902 | The reservation will only be created if it, plus the extra-zone, |
| 2903 | falls entirely within a single free segment. The returned Bool |
| 2904 | indicates whether the creation succeeded. */ |
| 2905 | |
| 2906 | Bool VG_(am_create_reservation) ( Addr start, SizeT length, |
| 2907 | ShrinkMode smode, SSizeT extra ) |
| 2908 | { |
| 2909 | Int startI, endI; |
| 2910 | NSegment seg; |
| 2911 | |
| 2912 | /* start and end, not taking into account the extra space. */ |
| 2913 | Addr start1 = start; |
| 2914 | Addr end1 = start + length - 1; |
| 2915 | |
| 2916 | /* start and end, taking into account the extra space. */ |
| 2917 | Addr start2 = start1; |
| 2918 | Addr end2 = end1; |
| 2919 | |
| 2920 | if (extra < 0) start2 += extra; // this moves it down :-) |
| 2921 | if (extra > 0) end2 += extra; |
| 2922 | |
| 2923 | aspacem_assert(VG_IS_PAGE_ALIGNED(start)); |
| 2924 | aspacem_assert(VG_IS_PAGE_ALIGNED(start+length)); |
| 2925 | aspacem_assert(VG_IS_PAGE_ALIGNED(start2)); |
| 2926 | aspacem_assert(VG_IS_PAGE_ALIGNED(end2+1)); |
| 2927 | |
| 2928 | startI = find_nsegment_idx( start2 ); |
| 2929 | endI = find_nsegment_idx( end2 ); |
| 2930 | |
| 2931 | /* If the start and end points don't fall within the same (free) |
| 2932 | segment, we're hosed. This does rely on the assumption that all |
| 2933 | mergeable adjacent segments can be merged, but add_segment() |
| 2934 | should ensure that. */ |
| 2935 | if (startI != endI) |
| 2936 | return False; |
| 2937 | |
| 2938 | if (nsegments[startI].kind != SkFree) |
| 2939 | return False; |
| 2940 | |
| 2941 | /* Looks good - make the reservation. */ |
| 2942 | aspacem_assert(nsegments[startI].start <= start2); |
| 2943 | aspacem_assert(end2 <= nsegments[startI].end); |
| 2944 | |
| 2945 | init_nsegment( &seg ); |
| 2946 | seg.kind = SkResvn; |
| 2947 | seg.start = start1; /* NB: extra space is not included in the |
| 2948 | reservation. */ |
| 2949 | seg.end = end1; |
| 2950 | seg.smode = smode; |
| 2951 | add_segment( &seg ); |
| 2952 | |
| 2953 | AM_SANITY_CHECK; |
| 2954 | return True; |
| 2955 | } |
| 2956 | |
| 2957 | |
florian | 888b815 | 2015-02-26 16:07:12 +0000 | [diff] [blame] | 2958 | /* ADDR is the start address of an anonymous client mapping. This fn extends |
| 2959 | the mapping by DELTA bytes, taking the space from a reservation section |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2960 | which must be adjacent. If DELTA is positive, the segment is |
| 2961 | extended forwards in the address space, and the reservation must be |
| 2962 | the next one along. If DELTA is negative, the segment is extended |
| 2963 | backwards in the address space and the reservation must be the |
sewardj | 6684d2a | 2005-09-28 01:46:31 +0000 | [diff] [blame] | 2964 | previous one. DELTA must be page aligned. abs(DELTA) must not |
| 2965 | exceed the size of the reservation segment minus one page, that is, |
| 2966 | the reservation segment after the operation must be at least one |
florian | 888b815 | 2015-02-26 16:07:12 +0000 | [diff] [blame] | 2967 | page long. The function returns a pointer to the resized segment. */ |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2968 | |
florian | 888b815 | 2015-02-26 16:07:12 +0000 | [diff] [blame] | 2969 | const NSegment *VG_(am_extend_into_adjacent_reservation_client)( Addr addr, |
florian | 15fa8a2 | 2015-03-03 14:56:17 +0000 | [diff] [blame] | 2970 | SSizeT delta, |
| 2971 | Bool *overflow) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2972 | { |
| 2973 | Int segA, segR; |
| 2974 | UInt prot; |
| 2975 | SysRes sres; |
| 2976 | |
florian | 15fa8a2 | 2015-03-03 14:56:17 +0000 | [diff] [blame] | 2977 | *overflow = False; |
| 2978 | |
florian | 888b815 | 2015-02-26 16:07:12 +0000 | [diff] [blame] | 2979 | segA = find_nsegment_idx(addr); |
| 2980 | aspacem_assert(nsegments[segA].kind == SkAnonC); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2981 | |
| 2982 | if (delta == 0) |
florian | 888b815 | 2015-02-26 16:07:12 +0000 | [diff] [blame] | 2983 | return nsegments + segA; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 2984 | |
| 2985 | prot = (nsegments[segA].hasR ? VKI_PROT_READ : 0) |
| 2986 | | (nsegments[segA].hasW ? VKI_PROT_WRITE : 0) |
| 2987 | | (nsegments[segA].hasX ? VKI_PROT_EXEC : 0); |
| 2988 | |
| 2989 | aspacem_assert(VG_IS_PAGE_ALIGNED(delta<0 ? -delta : delta)); |
| 2990 | |
| 2991 | if (delta > 0) { |
| 2992 | |
| 2993 | /* Extending the segment forwards. */ |
| 2994 | segR = segA+1; |
| 2995 | if (segR >= nsegments_used |
| 2996 | || nsegments[segR].kind != SkResvn |
florian | 15fa8a2 | 2015-03-03 14:56:17 +0000 | [diff] [blame] | 2997 | || nsegments[segR].smode != SmLower) |
| 2998 | return NULL; |
| 2999 | |
| 3000 | if (delta + VKI_PAGE_SIZE |
| 3001 | > (nsegments[segR].end - nsegments[segR].start + 1)) { |
| 3002 | *overflow = True; |
| 3003 | return NULL; |
| 3004 | } |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3005 | |
| 3006 | /* Extend the kernel's mapping. */ |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3007 | // DDD: #warning GrP fixme MAP_FIXED can clobber memory! |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3008 | sres = VG_(am_do_mmap_NO_NOTIFY)( |
| 3009 | nsegments[segR].start, delta, |
| 3010 | prot, |
| 3011 | VKI_MAP_FIXED|VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS, |
| 3012 | 0, 0 |
| 3013 | ); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 3014 | if (sr_isError(sres)) |
florian | 888b815 | 2015-02-26 16:07:12 +0000 | [diff] [blame] | 3015 | return NULL; /* kernel bug if this happens? */ |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 3016 | if (sr_Res(sres) != nsegments[segR].start) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3017 | /* kernel bug if this happens? */ |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 3018 | (void)ML_(am_do_munmap_NO_NOTIFY)( sr_Res(sres), delta ); |
florian | 888b815 | 2015-02-26 16:07:12 +0000 | [diff] [blame] | 3019 | return NULL; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3020 | } |
| 3021 | |
| 3022 | /* Ok, success with the kernel. Update our structures. */ |
florian | 3bfb914 | 2015-07-06 20:22:16 +0000 | [diff] [blame] | 3023 | nsegments[segR].start += delta; |
| 3024 | nsegments[segA].end += delta; |
| 3025 | aspacem_assert(nsegments[segR].start <= nsegments[segR].end); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3026 | |
| 3027 | } else { |
| 3028 | |
| 3029 | /* Extending the segment backwards. */ |
| 3030 | delta = -delta; |
| 3031 | aspacem_assert(delta > 0); |
| 3032 | |
| 3033 | segR = segA-1; |
| 3034 | if (segR < 0 |
| 3035 | || nsegments[segR].kind != SkResvn |
florian | 15fa8a2 | 2015-03-03 14:56:17 +0000 | [diff] [blame] | 3036 | || nsegments[segR].smode != SmUpper) |
| 3037 | return NULL; |
| 3038 | |
| 3039 | if (delta + VKI_PAGE_SIZE |
| 3040 | > (nsegments[segR].end - nsegments[segR].start + 1)) { |
| 3041 | *overflow = True; |
| 3042 | return NULL; |
| 3043 | } |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3044 | |
| 3045 | /* Extend the kernel's mapping. */ |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3046 | // DDD: #warning GrP fixme MAP_FIXED can clobber memory! |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3047 | sres = VG_(am_do_mmap_NO_NOTIFY)( |
| 3048 | nsegments[segA].start-delta, delta, |
| 3049 | prot, |
| 3050 | VKI_MAP_FIXED|VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS, |
| 3051 | 0, 0 |
| 3052 | ); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 3053 | if (sr_isError(sres)) |
florian | 888b815 | 2015-02-26 16:07:12 +0000 | [diff] [blame] | 3054 | return NULL; /* kernel bug if this happens? */ |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 3055 | if (sr_Res(sres) != nsegments[segA].start-delta) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3056 | /* kernel bug if this happens? */ |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 3057 | (void)ML_(am_do_munmap_NO_NOTIFY)( sr_Res(sres), delta ); |
florian | 888b815 | 2015-02-26 16:07:12 +0000 | [diff] [blame] | 3058 | return NULL; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3059 | } |
| 3060 | |
| 3061 | /* Ok, success with the kernel. Update our structures. */ |
florian | 3bfb914 | 2015-07-06 20:22:16 +0000 | [diff] [blame] | 3062 | nsegments[segR].end -= delta; |
| 3063 | nsegments[segA].start -= delta; |
| 3064 | aspacem_assert(nsegments[segR].start <= nsegments[segR].end); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3065 | } |
| 3066 | |
| 3067 | AM_SANITY_CHECK; |
florian | 888b815 | 2015-02-26 16:07:12 +0000 | [diff] [blame] | 3068 | return nsegments + segA; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3069 | } |
| 3070 | |
| 3071 | |
| 3072 | /* --- --- --- resizing/move a mapping --- --- --- */ |
| 3073 | |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3074 | #if HAVE_MREMAP |
| 3075 | |
florian | dd7318b | 2015-02-25 10:06:06 +0000 | [diff] [blame] | 3076 | /* This function grows a client mapping in place into an adjacent free segment. |
| 3077 | ADDR is the client mapping's start address and DELTA, which must be page |
| 3078 | aligned, is the growth amount. The function returns a pointer to the |
| 3079 | resized segment. The function is used in support of mremap. */ |
| 3080 | const NSegment *VG_(am_extend_map_client)( Addr addr, SizeT delta ) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3081 | { |
| 3082 | Addr xStart; |
| 3083 | SysRes sres; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3084 | |
sewardj | 63a622f | 2006-08-16 14:22:29 +0000 | [diff] [blame] | 3085 | if (0) |
| 3086 | VG_(am_show_nsegments)(0, "VG_(am_extend_map_client) BEFORE"); |
| 3087 | |
florian | dd7318b | 2015-02-25 10:06:06 +0000 | [diff] [blame] | 3088 | /* Get the client segment */ |
| 3089 | Int ix = find_nsegment_idx(addr); |
| 3090 | aspacem_assert(ix >= 0 && ix < nsegments_used); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3091 | |
florian | dd7318b | 2015-02-25 10:06:06 +0000 | [diff] [blame] | 3092 | NSegment *seg = nsegments + ix; |
| 3093 | |
florian | 7a5681f | 2015-05-12 21:52:08 +0000 | [diff] [blame] | 3094 | aspacem_assert(seg->kind == SkFileC || seg->kind == SkAnonC || |
| 3095 | seg->kind == SkShmC); |
florian | dd7318b | 2015-02-25 10:06:06 +0000 | [diff] [blame] | 3096 | aspacem_assert(delta > 0 && VG_IS_PAGE_ALIGNED(delta)) ; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3097 | |
| 3098 | xStart = seg->end+1; |
florian | dd7318b | 2015-02-25 10:06:06 +0000 | [diff] [blame] | 3099 | aspacem_assert(xStart + delta >= delta); // no wrap-around |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3100 | |
florian | dd7318b | 2015-02-25 10:06:06 +0000 | [diff] [blame] | 3101 | /* The segment following the client segment must be a free segment and |
| 3102 | it must be large enough to cover the additional memory. */ |
| 3103 | NSegment *segf = seg + 1; |
| 3104 | aspacem_assert(segf->kind == SkFree); |
| 3105 | aspacem_assert(segf->start == xStart); |
| 3106 | aspacem_assert(xStart + delta - 1 <= segf->end); |
| 3107 | |
| 3108 | SizeT seg_old_len = seg->end + 1 - seg->start; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3109 | |
| 3110 | AM_SANITY_CHECK; |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 3111 | sres = ML_(am_do_extend_mapping_NO_NOTIFY)( seg->start, |
| 3112 | seg_old_len, |
| 3113 | seg_old_len + delta ); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 3114 | if (sr_isError(sres)) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3115 | AM_SANITY_CHECK; |
florian | dd7318b | 2015-02-25 10:06:06 +0000 | [diff] [blame] | 3116 | return NULL; |
sewardj | 63a622f | 2006-08-16 14:22:29 +0000 | [diff] [blame] | 3117 | } else { |
| 3118 | /* the area must not have moved */ |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 3119 | aspacem_assert(sr_Res(sres) == seg->start); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3120 | } |
| 3121 | |
florian | dd7318b | 2015-02-25 10:06:06 +0000 | [diff] [blame] | 3122 | NSegment seg_copy = *seg; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3123 | seg_copy.end += delta; |
| 3124 | add_segment( &seg_copy ); |
| 3125 | |
sewardj | 63a622f | 2006-08-16 14:22:29 +0000 | [diff] [blame] | 3126 | if (0) |
| 3127 | VG_(am_show_nsegments)(0, "VG_(am_extend_map_client) AFTER"); |
| 3128 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3129 | AM_SANITY_CHECK; |
florian | dd7318b | 2015-02-25 10:06:06 +0000 | [diff] [blame] | 3130 | return nsegments + find_nsegment_idx(addr); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3131 | } |
| 3132 | |
| 3133 | |
| 3134 | /* Remap the old address range to the new address range. Fails if any |
| 3135 | parameter is not page aligned, if the either size is zero, if any |
| 3136 | wraparound is implied, if the old address range does not fall |
| 3137 | entirely within a single segment, if the new address range overlaps |
| 3138 | with the old one, or if the old address range is not a valid client |
| 3139 | mapping. If *need_discard is True after a successful return, the |
| 3140 | caller should immediately discard translations from both specified |
| 3141 | address ranges. */ |
| 3142 | |
| 3143 | Bool VG_(am_relocate_nooverlap_client)( /*OUT*/Bool* need_discard, |
| 3144 | Addr old_addr, SizeT old_len, |
| 3145 | Addr new_addr, SizeT new_len ) |
| 3146 | { |
| 3147 | Int iLo, iHi; |
| 3148 | SysRes sres; |
tom | fcb3bc9 | 2006-05-22 11:20:15 +0000 | [diff] [blame] | 3149 | NSegment seg; |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3150 | |
| 3151 | if (old_len == 0 || new_len == 0) |
| 3152 | return False; |
| 3153 | |
| 3154 | if (!VG_IS_PAGE_ALIGNED(old_addr) || !VG_IS_PAGE_ALIGNED(old_len) |
| 3155 | || !VG_IS_PAGE_ALIGNED(new_addr) || !VG_IS_PAGE_ALIGNED(new_len)) |
| 3156 | return False; |
| 3157 | |
| 3158 | if (old_addr + old_len < old_addr |
| 3159 | || new_addr + new_len < new_addr) |
| 3160 | return False; |
| 3161 | |
| 3162 | if (old_addr + old_len - 1 < new_addr |
| 3163 | || new_addr + new_len - 1 < old_addr) { |
| 3164 | /* no overlap */ |
| 3165 | } else |
| 3166 | return False; |
| 3167 | |
| 3168 | iLo = find_nsegment_idx( old_addr ); |
| 3169 | iHi = find_nsegment_idx( old_addr + old_len - 1 ); |
| 3170 | if (iLo != iHi) |
| 3171 | return False; |
| 3172 | |
florian | 7a5681f | 2015-05-12 21:52:08 +0000 | [diff] [blame] | 3173 | if (nsegments[iLo].kind != SkFileC && nsegments[iLo].kind != SkAnonC && |
| 3174 | nsegments[iLo].kind != SkShmC) |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3175 | return False; |
| 3176 | |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 3177 | sres = ML_(am_do_relocate_nooverlap_mapping_NO_NOTIFY) |
| 3178 | ( old_addr, old_len, new_addr, new_len ); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 3179 | if (sr_isError(sres)) { |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3180 | AM_SANITY_CHECK; |
| 3181 | return False; |
sewardj | ee2de2c | 2006-08-16 15:06:53 +0000 | [diff] [blame] | 3182 | } else { |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 3183 | aspacem_assert(sr_Res(sres) == new_addr); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3184 | } |
| 3185 | |
| 3186 | *need_discard = any_Ts_in_range( old_addr, old_len ) |
| 3187 | || any_Ts_in_range( new_addr, new_len ); |
| 3188 | |
tom | fcb3bc9 | 2006-05-22 11:20:15 +0000 | [diff] [blame] | 3189 | seg = nsegments[iLo]; |
| 3190 | |
| 3191 | /* Mark the new area based on the old seg. */ |
| 3192 | if (seg.kind == SkFileC) { |
| 3193 | seg.offset += ((ULong)old_addr) - ((ULong)seg.start); |
tom | fcb3bc9 | 2006-05-22 11:20:15 +0000 | [diff] [blame] | 3194 | } |
| 3195 | seg.start = new_addr; |
| 3196 | seg.end = new_addr + new_len - 1; |
| 3197 | add_segment( &seg ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3198 | |
| 3199 | /* Create a free hole in the old location. */ |
| 3200 | init_nsegment( &seg ); |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3201 | seg.start = old_addr; |
| 3202 | seg.end = old_addr + old_len - 1; |
sewardj | 613a9f2 | 2006-08-16 01:48:19 +0000 | [diff] [blame] | 3203 | /* See comments in VG_(am_notify_munmap) about this SkResvn vs |
| 3204 | SkFree thing. */ |
| 3205 | if (old_addr > aspacem_maxAddr |
| 3206 | && /* check previous comparison is meaningful */ |
| 3207 | aspacem_maxAddr < Addr_MAX) |
| 3208 | seg.kind = SkResvn; |
| 3209 | else |
| 3210 | seg.kind = SkFree; |
| 3211 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3212 | add_segment( &seg ); |
| 3213 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3214 | AM_SANITY_CHECK; |
| 3215 | return True; |
| 3216 | } |
| 3217 | |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3218 | #endif // HAVE_MREMAP |
| 3219 | |
| 3220 | |
bart | c401ae7 | 2009-08-23 11:17:25 +0000 | [diff] [blame] | 3221 | #if defined(VGO_linux) |
| 3222 | |
sewardj | 45f4e7c | 2005-09-27 19:20:21 +0000 | [diff] [blame] | 3223 | /*-----------------------------------------------------------------*/ |
| 3224 | /*--- ---*/ |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3225 | /*--- A simple parser for /proc/self/maps on Linux 2.4.X/2.6.X. ---*/ |
| 3226 | /*--- Almost completely independent of the stuff above. The ---*/ |
| 3227 | /*--- only function it 'exports' to the code above this comment ---*/ |
| 3228 | /*--- is parse_procselfmaps. ---*/ |
| 3229 | /*--- ---*/ |
| 3230 | /*-----------------------------------------------------------------*/ |
| 3231 | |
sewardj | 38a21ac | 2010-01-03 10:14:03 +0000 | [diff] [blame] | 3232 | /*------BEGIN-procmaps-parser-for-Linux--------------------------*/ |
| 3233 | |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3234 | /* Size of a smallish table used to read /proc/self/map entries. */ |
| 3235 | #define M_PROCMAP_BUF 100000 |
| 3236 | |
| 3237 | /* static ... to keep it out of the stack frame. */ |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 3238 | static HChar procmap_buf[M_PROCMAP_BUF]; |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3239 | |
| 3240 | /* Records length of /proc/self/maps read into procmap_buf. */ |
| 3241 | static Int buf_n_tot; |
| 3242 | |
| 3243 | /* Helper fns. */ |
| 3244 | |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 3245 | static Int hexdigit ( HChar c ) |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3246 | { |
| 3247 | if (c >= '0' && c <= '9') return (Int)(c - '0'); |
| 3248 | if (c >= 'a' && c <= 'f') return 10 + (Int)(c - 'a'); |
| 3249 | if (c >= 'A' && c <= 'F') return 10 + (Int)(c - 'A'); |
| 3250 | return -1; |
| 3251 | } |
| 3252 | |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 3253 | static Int decdigit ( HChar c ) |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3254 | { |
| 3255 | if (c >= '0' && c <= '9') return (Int)(c - '0'); |
| 3256 | return -1; |
| 3257 | } |
| 3258 | |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 3259 | static Int readchar ( const HChar* buf, HChar* ch ) |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3260 | { |
| 3261 | if (*buf == 0) return 0; |
| 3262 | *ch = *buf; |
| 3263 | return 1; |
| 3264 | } |
| 3265 | |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 3266 | static Int readhex ( const HChar* buf, UWord* val ) |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3267 | { |
sewardj | 274461d | 2005-10-02 17:01:41 +0000 | [diff] [blame] | 3268 | /* Read a word-sized hex number. */ |
| 3269 | Int n = 0; |
| 3270 | *val = 0; |
| 3271 | while (hexdigit(*buf) >= 0) { |
| 3272 | *val = (*val << 4) + hexdigit(*buf); |
| 3273 | n++; buf++; |
| 3274 | } |
| 3275 | return n; |
| 3276 | } |
| 3277 | |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 3278 | static Int readhex64 ( const HChar* buf, ULong* val ) |
sewardj | 274461d | 2005-10-02 17:01:41 +0000 | [diff] [blame] | 3279 | { |
| 3280 | /* Read a potentially 64-bit hex number. */ |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3281 | Int n = 0; |
| 3282 | *val = 0; |
| 3283 | while (hexdigit(*buf) >= 0) { |
| 3284 | *val = (*val << 4) + hexdigit(*buf); |
| 3285 | n++; buf++; |
| 3286 | } |
| 3287 | return n; |
| 3288 | } |
| 3289 | |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 3290 | static Int readdec64 ( const HChar* buf, ULong* val ) |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3291 | { |
| 3292 | Int n = 0; |
| 3293 | *val = 0; |
florian | 54212b6 | 2011-12-26 18:30:18 +0000 | [diff] [blame] | 3294 | while (decdigit(*buf) >= 0) { |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3295 | *val = (*val * 10) + decdigit(*buf); |
| 3296 | n++; buf++; |
| 3297 | } |
| 3298 | return n; |
| 3299 | } |
| 3300 | |
| 3301 | |
| 3302 | /* Get the contents of /proc/self/maps into a static buffer. If |
| 3303 | there's a syntax error, it won't fit, or other failure, just |
| 3304 | abort. */ |
| 3305 | |
| 3306 | static void read_procselfmaps_into_buf ( void ) |
| 3307 | { |
| 3308 | Int n_chunk; |
| 3309 | SysRes fd; |
| 3310 | |
| 3311 | /* Read the initial memory mapping from the /proc filesystem. */ |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 3312 | fd = ML_(am_open)( "/proc/self/maps", VKI_O_RDONLY, 0 ); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 3313 | if (sr_isError(fd)) |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 3314 | ML_(am_barf)("can't open /proc/self/maps"); |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3315 | |
| 3316 | buf_n_tot = 0; |
| 3317 | do { |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 3318 | n_chunk = ML_(am_read)( sr_Res(fd), &procmap_buf[buf_n_tot], |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3319 | M_PROCMAP_BUF - buf_n_tot ); |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 3320 | if (n_chunk >= 0) |
| 3321 | buf_n_tot += n_chunk; |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3322 | } while ( n_chunk > 0 && buf_n_tot < M_PROCMAP_BUF ); |
| 3323 | |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 3324 | ML_(am_close)(sr_Res(fd)); |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3325 | |
| 3326 | if (buf_n_tot >= M_PROCMAP_BUF-5) |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 3327 | ML_(am_barf_toolow)("M_PROCMAP_BUF"); |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3328 | if (buf_n_tot == 0) |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 3329 | ML_(am_barf)("I/O error on /proc/self/maps"); |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3330 | |
| 3331 | procmap_buf[buf_n_tot] = 0; |
| 3332 | } |
| 3333 | |
| 3334 | /* Parse /proc/self/maps. For each map entry, call |
| 3335 | record_mapping, passing it, in this order: |
| 3336 | |
| 3337 | start address in memory |
| 3338 | length |
| 3339 | page protections (using the VKI_PROT_* flags) |
| 3340 | mapped file device and inode |
| 3341 | offset in file, or zero if no file |
| 3342 | filename, zero terminated, or NULL if no file |
| 3343 | |
| 3344 | So the sig of the called fn might be |
| 3345 | |
| 3346 | void (*record_mapping)( Addr start, SizeT size, UInt prot, |
| 3347 | UInt dev, UInt info, |
| 3348 | ULong foffset, UChar* filename ) |
| 3349 | |
| 3350 | Note that the supplied filename is transiently stored; record_mapping |
| 3351 | should make a copy if it wants to keep it. |
| 3352 | |
| 3353 | Nb: it is important that this function does not alter the contents of |
| 3354 | procmap_buf! |
| 3355 | */ |
| 3356 | static void parse_procselfmaps ( |
| 3357 | void (*record_mapping)( Addr addr, SizeT len, UInt prot, |
njn | c4431bf | 2009-01-15 21:29:24 +0000 | [diff] [blame] | 3358 | ULong dev, ULong ino, Off64T offset, |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 3359 | const HChar* filename ), |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3360 | void (*record_gap)( Addr addr, SizeT len ) |
| 3361 | ) |
| 3362 | { |
| 3363 | Int i, j, i_eol; |
| 3364 | Addr start, endPlusOne, gapStart; |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 3365 | HChar* filename; |
| 3366 | HChar rr, ww, xx, pp, ch, tmp; |
sewardj | 4190600 | 2008-08-18 21:47:11 +0000 | [diff] [blame] | 3367 | UInt prot; |
| 3368 | UWord maj, min; |
| 3369 | ULong foffset, dev, ino; |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3370 | |
sewardj | 4c3faae | 2006-03-15 11:50:32 +0000 | [diff] [blame] | 3371 | foffset = ino = 0; /* keep gcc-4.1.0 happy */ |
| 3372 | |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3373 | read_procselfmaps_into_buf(); |
| 3374 | |
| 3375 | aspacem_assert('\0' != procmap_buf[0] && 0 != buf_n_tot); |
| 3376 | |
| 3377 | if (0) |
| 3378 | VG_(debugLog)(0, "procselfmaps", "raw:\n%s\n", procmap_buf); |
| 3379 | |
| 3380 | /* Ok, it's safely aboard. Parse the entries. */ |
| 3381 | i = 0; |
| 3382 | gapStart = Addr_MIN; |
| 3383 | while (True) { |
| 3384 | if (i >= buf_n_tot) break; |
| 3385 | |
| 3386 | /* Read (without fscanf :) the pattern %16x-%16x %c%c%c%c %16x %2x:%2x %d */ |
| 3387 | j = readhex(&procmap_buf[i], &start); |
| 3388 | if (j > 0) i += j; else goto syntaxerror; |
| 3389 | j = readchar(&procmap_buf[i], &ch); |
| 3390 | if (j == 1 && ch == '-') i += j; else goto syntaxerror; |
| 3391 | j = readhex(&procmap_buf[i], &endPlusOne); |
| 3392 | if (j > 0) i += j; else goto syntaxerror; |
| 3393 | |
| 3394 | j = readchar(&procmap_buf[i], &ch); |
| 3395 | if (j == 1 && ch == ' ') i += j; else goto syntaxerror; |
| 3396 | |
| 3397 | j = readchar(&procmap_buf[i], &rr); |
| 3398 | if (j == 1 && (rr == 'r' || rr == '-')) i += j; else goto syntaxerror; |
| 3399 | j = readchar(&procmap_buf[i], &ww); |
| 3400 | if (j == 1 && (ww == 'w' || ww == '-')) i += j; else goto syntaxerror; |
| 3401 | j = readchar(&procmap_buf[i], &xx); |
| 3402 | if (j == 1 && (xx == 'x' || xx == '-')) i += j; else goto syntaxerror; |
| 3403 | /* This field is the shared/private flag */ |
| 3404 | j = readchar(&procmap_buf[i], &pp); |
| 3405 | if (j == 1 && (pp == 'p' || pp == '-' || pp == 's')) |
| 3406 | i += j; else goto syntaxerror; |
| 3407 | |
| 3408 | j = readchar(&procmap_buf[i], &ch); |
| 3409 | if (j == 1 && ch == ' ') i += j; else goto syntaxerror; |
| 3410 | |
sewardj | 274461d | 2005-10-02 17:01:41 +0000 | [diff] [blame] | 3411 | j = readhex64(&procmap_buf[i], &foffset); |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3412 | if (j > 0) i += j; else goto syntaxerror; |
| 3413 | |
| 3414 | j = readchar(&procmap_buf[i], &ch); |
| 3415 | if (j == 1 && ch == ' ') i += j; else goto syntaxerror; |
| 3416 | |
| 3417 | j = readhex(&procmap_buf[i], &maj); |
| 3418 | if (j > 0) i += j; else goto syntaxerror; |
| 3419 | j = readchar(&procmap_buf[i], &ch); |
| 3420 | if (j == 1 && ch == ':') i += j; else goto syntaxerror; |
| 3421 | j = readhex(&procmap_buf[i], &min); |
| 3422 | if (j > 0) i += j; else goto syntaxerror; |
| 3423 | |
| 3424 | j = readchar(&procmap_buf[i], &ch); |
| 3425 | if (j == 1 && ch == ' ') i += j; else goto syntaxerror; |
| 3426 | |
sewardj | 4190600 | 2008-08-18 21:47:11 +0000 | [diff] [blame] | 3427 | j = readdec64(&procmap_buf[i], &ino); |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3428 | if (j > 0) i += j; else goto syntaxerror; |
| 3429 | |
| 3430 | goto read_line_ok; |
| 3431 | |
| 3432 | syntaxerror: |
| 3433 | VG_(debugLog)(0, "Valgrind:", |
| 3434 | "FATAL: syntax error reading /proc/self/maps\n"); |
| 3435 | { Int k, m; |
| 3436 | HChar buf50[51]; |
| 3437 | m = 0; |
| 3438 | buf50[m] = 0; |
| 3439 | k = i - 50; |
| 3440 | if (k < 0) k = 0; |
| 3441 | for (; k <= i; k++) { |
| 3442 | buf50[m] = procmap_buf[k]; |
| 3443 | buf50[m+1] = 0; |
| 3444 | if (m < 50-1) m++; |
| 3445 | } |
| 3446 | VG_(debugLog)(0, "procselfmaps", "Last 50 chars: '%s'\n", buf50); |
| 3447 | } |
sewardj | 297f6b0 | 2006-10-14 22:25:30 +0000 | [diff] [blame] | 3448 | ML_(am_exit)(1); |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3449 | |
| 3450 | read_line_ok: |
| 3451 | |
florian | bd28c5e | 2011-12-26 18:35:29 +0000 | [diff] [blame] | 3452 | aspacem_assert(i < buf_n_tot); |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3453 | |
florian | bd28c5e | 2011-12-26 18:35:29 +0000 | [diff] [blame] | 3454 | /* Try and find the name of the file mapped to this segment, if |
| 3455 | it exists. Note that file names can contain spaces. */ |
| 3456 | |
| 3457 | // Move i to the next non-space char, which should be either a '/', |
| 3458 | // a '[', or a newline. |
| 3459 | while (procmap_buf[i] == ' ') i++; |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3460 | |
| 3461 | // Move i_eol to the end of the line. |
| 3462 | i_eol = i; |
florian | bd28c5e | 2011-12-26 18:35:29 +0000 | [diff] [blame] | 3463 | while (procmap_buf[i_eol] != '\n') i_eol++; |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3464 | |
| 3465 | // If there's a filename... |
florian | bd28c5e | 2011-12-26 18:35:29 +0000 | [diff] [blame] | 3466 | if (procmap_buf[i] == '/') { |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3467 | /* Minor hack: put a '\0' at the filename end for the call to |
| 3468 | 'record_mapping', then restore the old char with 'tmp'. */ |
| 3469 | filename = &procmap_buf[i]; |
| 3470 | tmp = filename[i_eol - i]; |
| 3471 | filename[i_eol - i] = '\0'; |
| 3472 | } else { |
| 3473 | tmp = 0; |
| 3474 | filename = NULL; |
| 3475 | foffset = 0; |
| 3476 | } |
| 3477 | |
| 3478 | prot = 0; |
| 3479 | if (rr == 'r') prot |= VKI_PROT_READ; |
| 3480 | if (ww == 'w') prot |= VKI_PROT_WRITE; |
| 3481 | if (xx == 'x') prot |= VKI_PROT_EXEC; |
| 3482 | |
tom | 99ffec4 | 2005-11-13 16:52:56 +0000 | [diff] [blame] | 3483 | /* Linux has two ways to encode a device number when it |
| 3484 | is exposed to user space (via fstat etc). The old way |
| 3485 | is the traditional unix scheme that produces a 16 bit |
| 3486 | device number with the top 8 being the major number and |
| 3487 | the bottom 8 the minor number. |
| 3488 | |
| 3489 | The new scheme allows for a 12 bit major number and |
| 3490 | a 20 bit minor number by using a 32 bit device number |
| 3491 | and putting the top 12 bits of the minor number into |
| 3492 | the top 12 bits of the device number thus leaving an |
| 3493 | extra 4 bits for the major number. |
| 3494 | |
| 3495 | If the minor and major number are both single byte |
| 3496 | values then both schemes give the same result so we |
| 3497 | use the new scheme here in case either number is |
| 3498 | outside the 0-255 range and then use fstat64 when |
| 3499 | available (or fstat on 64 bit systems) so that we |
| 3500 | should always have a new style device number and |
| 3501 | everything should match. */ |
| 3502 | dev = (min & 0xff) | (maj << 8) | ((min & ~0xff) << 12); |
| 3503 | |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3504 | if (record_gap && gapStart < start) |
| 3505 | (*record_gap) ( gapStart, start-gapStart ); |
| 3506 | |
sewardj | e6bbc36 | 2007-01-07 02:03:01 +0000 | [diff] [blame] | 3507 | if (record_mapping && start < endPlusOne) |
| 3508 | (*record_mapping) ( start, endPlusOne-start, |
| 3509 | prot, dev, ino, |
| 3510 | foffset, filename ); |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3511 | |
| 3512 | if ('\0' != tmp) { |
| 3513 | filename[i_eol - i] = tmp; |
| 3514 | } |
| 3515 | |
| 3516 | i = i_eol + 1; |
| 3517 | gapStart = endPlusOne; |
| 3518 | } |
| 3519 | |
sewardj | 38a21ac | 2010-01-03 10:14:03 +0000 | [diff] [blame] | 3520 | # if defined(VGP_arm_linux) |
| 3521 | /* ARM puts code at the end of memory that contains processor |
| 3522 | specific stuff (cmpxchg, getting the thread local storage, etc.) |
| 3523 | This isn't specified in /proc/self/maps, so do it here. This |
| 3524 | kludgery causes the view of memory, as presented to |
| 3525 | record_gap/record_mapping, to actually reflect reality. IMO |
| 3526 | (JRS, 2010-Jan-03) the fact that /proc/.../maps does not list |
| 3527 | the commpage should be regarded as a bug in the kernel. */ |
| 3528 | { const Addr commpage_start = ARM_LINUX_FAKE_COMMPAGE_START; |
| 3529 | const Addr commpage_end1 = ARM_LINUX_FAKE_COMMPAGE_END1; |
| 3530 | if (gapStart < commpage_start) { |
| 3531 | if (record_gap) |
| 3532 | (*record_gap)( gapStart, commpage_start - gapStart ); |
| 3533 | if (record_mapping) |
| 3534 | (*record_mapping)( commpage_start, commpage_end1 - commpage_start, |
| 3535 | VKI_PROT_READ|VKI_PROT_EXEC, |
| 3536 | 0/*dev*/, 0/*ino*/, 0/*foffset*/, |
| 3537 | NULL); |
| 3538 | gapStart = commpage_end1; |
| 3539 | } |
| 3540 | } |
| 3541 | # endif |
| 3542 | |
sewardj | cb249ab | 2005-09-28 09:37:16 +0000 | [diff] [blame] | 3543 | if (record_gap && gapStart < Addr_MAX) |
| 3544 | (*record_gap) ( gapStart, Addr_MAX - gapStart + 1 ); |
| 3545 | } |
| 3546 | |
sewardj | 38a21ac | 2010-01-03 10:14:03 +0000 | [diff] [blame] | 3547 | /*------END-procmaps-parser-for-Linux----------------------------*/ |
| 3548 | |
| 3549 | /*------BEGIN-procmaps-parser-for-Darwin-------------------------*/ |
| 3550 | |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3551 | #elif defined(VGO_darwin) |
| 3552 | #include <mach/mach.h> |
| 3553 | #include <mach/mach_vm.h> |
| 3554 | |
| 3555 | static unsigned int mach2vki(unsigned int vm_prot) |
| 3556 | { |
| 3557 | return |
| 3558 | ((vm_prot & VM_PROT_READ) ? VKI_PROT_READ : 0) | |
| 3559 | ((vm_prot & VM_PROT_WRITE) ? VKI_PROT_WRITE : 0) | |
| 3560 | ((vm_prot & VM_PROT_EXECUTE) ? VKI_PROT_EXEC : 0) ; |
| 3561 | } |
| 3562 | |
| 3563 | static UInt stats_machcalls = 0; |
| 3564 | |
| 3565 | static void parse_procselfmaps ( |
| 3566 | void (*record_mapping)( Addr addr, SizeT len, UInt prot, |
| 3567 | ULong dev, ULong ino, Off64T offset, |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 3568 | const HChar* filename ), |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3569 | void (*record_gap)( Addr addr, SizeT len ) |
| 3570 | ) |
| 3571 | { |
| 3572 | vm_address_t iter; |
| 3573 | unsigned int depth; |
| 3574 | vm_address_t last; |
| 3575 | |
| 3576 | iter = 0; |
| 3577 | depth = 0; |
| 3578 | last = 0; |
| 3579 | while (1) { |
| 3580 | mach_vm_address_t addr = iter; |
| 3581 | mach_vm_size_t size; |
| 3582 | vm_region_submap_short_info_data_64_t info; |
| 3583 | kern_return_t kr; |
| 3584 | |
| 3585 | while (1) { |
| 3586 | mach_msg_type_number_t info_count |
| 3587 | = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; |
| 3588 | stats_machcalls++; |
| 3589 | kr = mach_vm_region_recurse(mach_task_self(), &addr, &size, &depth, |
| 3590 | (vm_region_info_t)&info, &info_count); |
| 3591 | if (kr) |
| 3592 | return; |
| 3593 | if (info.is_submap) { |
| 3594 | depth++; |
| 3595 | continue; |
| 3596 | } |
| 3597 | break; |
| 3598 | } |
| 3599 | iter = addr + size; |
| 3600 | |
| 3601 | if (addr > last && record_gap) { |
| 3602 | (*record_gap)(last, addr - last); |
| 3603 | } |
| 3604 | if (record_mapping) { |
| 3605 | (*record_mapping)(addr, size, mach2vki(info.protection), |
| 3606 | 0, 0, info.offset, NULL); |
| 3607 | } |
| 3608 | last = addr + size; |
| 3609 | } |
| 3610 | |
| 3611 | if ((Addr)-1 > last && record_gap) |
| 3612 | (*record_gap)(last, (Addr)-1 - last); |
| 3613 | } |
| 3614 | |
sewardj | 9edd09f | 2010-10-07 10:01:40 +0000 | [diff] [blame] | 3615 | // Urr. So much for thread safety. |
| 3616 | static Bool css_overflowed; |
| 3617 | static ChangedSeg* css_local; |
| 3618 | static Int css_size_local; |
| 3619 | static Int css_used_local; |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3620 | |
sewardj | e139a46 | 2012-03-07 13:28:05 +0000 | [diff] [blame] | 3621 | static Addr Addr__max ( Addr a, Addr b ) { return a > b ? a : b; } |
| 3622 | static Addr Addr__min ( Addr a, Addr b ) { return a < b ? a : b; } |
| 3623 | |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3624 | static void add_mapping_callback(Addr addr, SizeT len, UInt prot, |
| 3625 | ULong dev, ULong ino, Off64T offset, |
florian | dbb3584 | 2012-10-27 18:39:11 +0000 | [diff] [blame] | 3626 | const HChar *filename) |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3627 | { |
| 3628 | // derived from sync_check_mapping_callback() |
| 3629 | |
sewardj | e139a46 | 2012-03-07 13:28:05 +0000 | [diff] [blame] | 3630 | /* JRS 2012-Mar-07: this all seems very dubious to me. It would be |
| 3631 | safer to see if we can find, in V's segment collection, one |
| 3632 | single segment that completely covers the range [addr, +len) |
| 3633 | (and possibly more), and that has the exact same other |
| 3634 | properties (prot, dev, ino, offset, etc) as the data presented |
| 3635 | here. If found, we just skip. Otherwise add the data presented |
| 3636 | here into css_local[]. */ |
| 3637 | |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3638 | Int iLo, iHi, i; |
| 3639 | |
| 3640 | if (len == 0) return; |
| 3641 | |
| 3642 | /* The kernel should not give us wraparounds. */ |
| 3643 | aspacem_assert(addr <= addr + len - 1); |
| 3644 | |
| 3645 | iLo = find_nsegment_idx( addr ); |
| 3646 | iHi = find_nsegment_idx( addr + len - 1 ); |
| 3647 | |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3648 | /* NSegments iLo .. iHi inclusive should agree with the presented |
| 3649 | data. */ |
| 3650 | for (i = iLo; i <= iHi; i++) { |
| 3651 | |
| 3652 | UInt seg_prot; |
| 3653 | |
| 3654 | if (nsegments[i].kind == SkAnonV || nsegments[i].kind == SkFileV) { |
| 3655 | /* Ignore V regions */ |
| 3656 | continue; |
| 3657 | } |
| 3658 | else if (nsegments[i].kind == SkFree || nsegments[i].kind == SkResvn) { |
sewardj | e139a46 | 2012-03-07 13:28:05 +0000 | [diff] [blame] | 3659 | /* Add mapping for SkResvn regions */ |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3660 | ChangedSeg* cs = &css_local[css_used_local]; |
njn | fd1b461 | 2009-06-24 08:32:42 +0000 | [diff] [blame] | 3661 | if (css_used_local < css_size_local) { |
| 3662 | cs->is_added = True; |
| 3663 | cs->start = addr; |
| 3664 | cs->end = addr + len - 1; |
| 3665 | cs->prot = prot; |
| 3666 | cs->offset = offset; |
| 3667 | css_used_local++; |
| 3668 | } else { |
| 3669 | css_overflowed = True; |
| 3670 | } |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3671 | return; |
| 3672 | |
sewardj | f1a9bd2 | 2013-09-02 13:02:02 +0000 | [diff] [blame] | 3673 | } |
| 3674 | else if (nsegments[i].kind == SkAnonC || |
| 3675 | nsegments[i].kind == SkFileC || |
| 3676 | nsegments[i].kind == SkShmC) |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3677 | { |
| 3678 | /* Check permissions on client regions */ |
| 3679 | // GrP fixme |
| 3680 | seg_prot = 0; |
| 3681 | if (nsegments[i].hasR) seg_prot |= VKI_PROT_READ; |
| 3682 | if (nsegments[i].hasW) seg_prot |= VKI_PROT_WRITE; |
| 3683 | # if defined(VGA_x86) |
| 3684 | // GrP fixme sloppyXcheck |
| 3685 | // darwin: kernel X ignored and spuriously changes? (vm_copy) |
| 3686 | seg_prot |= (prot & VKI_PROT_EXEC); |
| 3687 | # else |
| 3688 | if (nsegments[i].hasX) seg_prot |= VKI_PROT_EXEC; |
| 3689 | # endif |
| 3690 | if (seg_prot != prot) { |
| 3691 | if (VG_(clo_trace_syscalls)) |
sewardj | 08a6319 | 2010-07-01 11:42:22 +0000 | [diff] [blame] | 3692 | VG_(debugLog)(0,"aspacem","region %p..%p permission " |
| 3693 | "mismatch (kernel %x, V %x)\n", |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3694 | (void*)nsegments[i].start, |
| 3695 | (void*)(nsegments[i].end+1), prot, seg_prot); |
sewardj | f1a9bd2 | 2013-09-02 13:02:02 +0000 | [diff] [blame] | 3696 | /* Add mapping for regions with protection changes */ |
| 3697 | ChangedSeg* cs = &css_local[css_used_local]; |
| 3698 | if (css_used_local < css_size_local) { |
| 3699 | cs->is_added = True; |
| 3700 | cs->start = addr; |
| 3701 | cs->end = addr + len - 1; |
| 3702 | cs->prot = prot; |
| 3703 | cs->offset = offset; |
| 3704 | css_used_local++; |
| 3705 | } else { |
| 3706 | css_overflowed = True; |
| 3707 | } |
| 3708 | return; |
| 3709 | |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3710 | } |
| 3711 | |
| 3712 | } else { |
| 3713 | aspacem_assert(0); |
| 3714 | } |
| 3715 | } |
| 3716 | } |
| 3717 | |
| 3718 | static void remove_mapping_callback(Addr addr, SizeT len) |
| 3719 | { |
| 3720 | // derived from sync_check_gap_callback() |
| 3721 | |
| 3722 | Int iLo, iHi, i; |
| 3723 | |
| 3724 | if (len == 0) |
| 3725 | return; |
| 3726 | |
| 3727 | /* The kernel should not give us wraparounds. */ |
| 3728 | aspacem_assert(addr <= addr + len - 1); |
| 3729 | |
| 3730 | iLo = find_nsegment_idx( addr ); |
| 3731 | iHi = find_nsegment_idx( addr + len - 1 ); |
| 3732 | |
| 3733 | /* NSegments iLo .. iHi inclusive should agree with the presented data. */ |
| 3734 | for (i = iLo; i <= iHi; i++) { |
sewardj | e139a46 | 2012-03-07 13:28:05 +0000 | [diff] [blame] | 3735 | if (nsegments[i].kind != SkFree && nsegments[i].kind != SkResvn) { |
| 3736 | /* V has a mapping, kernel doesn't. Add to css_local[], |
| 3737 | directives to chop off the part of the V mapping that |
| 3738 | falls within the gap that the kernel tells us is |
| 3739 | present. */ |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3740 | ChangedSeg* cs = &css_local[css_used_local]; |
njn | fd1b461 | 2009-06-24 08:32:42 +0000 | [diff] [blame] | 3741 | if (css_used_local < css_size_local) { |
| 3742 | cs->is_added = False; |
sewardj | e139a46 | 2012-03-07 13:28:05 +0000 | [diff] [blame] | 3743 | cs->start = Addr__max(nsegments[i].start, addr); |
| 3744 | cs->end = Addr__min(nsegments[i].end, addr + len - 1); |
| 3745 | aspacem_assert(VG_IS_PAGE_ALIGNED(cs->start)); |
| 3746 | aspacem_assert(VG_IS_PAGE_ALIGNED(cs->end+1)); |
| 3747 | /* I don't think the following should fail. But if it |
| 3748 | does, just omit the css_used_local++ in the cases where |
| 3749 | it doesn't hold. */ |
| 3750 | aspacem_assert(cs->start < cs->end); |
njn | fd1b461 | 2009-06-24 08:32:42 +0000 | [diff] [blame] | 3751 | cs->prot = 0; |
| 3752 | cs->offset = 0; |
| 3753 | css_used_local++; |
| 3754 | } else { |
| 3755 | css_overflowed = True; |
| 3756 | } |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3757 | } |
| 3758 | } |
| 3759 | } |
| 3760 | |
| 3761 | |
njn | fd1b461 | 2009-06-24 08:32:42 +0000 | [diff] [blame] | 3762 | // Returns False if 'css' wasn't big enough. |
| 3763 | Bool VG_(get_changed_segments)( |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3764 | const HChar* when, const HChar* where, /*OUT*/ChangedSeg* css, |
| 3765 | Int css_size, /*OUT*/Int* css_used) |
| 3766 | { |
| 3767 | static UInt stats_synccalls = 1; |
| 3768 | aspacem_assert(when && where); |
| 3769 | |
| 3770 | if (0) |
| 3771 | VG_(debugLog)(0,"aspacem", |
| 3772 | "[%u,%u] VG_(get_changed_segments)(%s, %s)\n", |
| 3773 | stats_synccalls++, stats_machcalls, when, where |
| 3774 | ); |
| 3775 | |
njn | fd1b461 | 2009-06-24 08:32:42 +0000 | [diff] [blame] | 3776 | css_overflowed = False; |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3777 | css_local = css; |
| 3778 | css_size_local = css_size; |
| 3779 | css_used_local = 0; |
| 3780 | |
| 3781 | // Get the list of segs that need to be added/removed. |
| 3782 | parse_procselfmaps(&add_mapping_callback, &remove_mapping_callback); |
| 3783 | |
| 3784 | *css_used = css_used_local; |
njn | fd1b461 | 2009-06-24 08:32:42 +0000 | [diff] [blame] | 3785 | |
| 3786 | if (css_overflowed) { |
| 3787 | aspacem_assert(css_used_local == css_size_local); |
| 3788 | } |
| 3789 | |
| 3790 | return !css_overflowed; |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 3791 | } |
| 3792 | |
sewardj | 38a21ac | 2010-01-03 10:14:03 +0000 | [diff] [blame] | 3793 | #endif // defined(VGO_darwin) |
bart | c401ae7 | 2009-08-23 11:17:25 +0000 | [diff] [blame] | 3794 | |
sewardj | 38a21ac | 2010-01-03 10:14:03 +0000 | [diff] [blame] | 3795 | /*------END-procmaps-parser-for-Darwin---------------------------*/ |
njn | 8b68b64 | 2009-06-24 00:37:09 +0000 | [diff] [blame] | 3796 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 3797 | /*------BEGIN-procmaps-parser-for-Solaris------------------------*/ |
| 3798 | |
| 3799 | #if defined(VGO_solaris) |
| 3800 | |
| 3801 | /* Note: /proc/self/xmap contains extended information about already |
| 3802 | materialized mappings whereas /proc/self/rmap contains information about |
| 3803 | all mappings including reserved but yet-to-materialize mappings (mmap'ed |
| 3804 | with MAP_NORESERVE flag, such as thread stacks). But /proc/self/rmap does |
| 3805 | not contain extended information found in /proc/self/xmap. Therefore |
| 3806 | information from both sources need to be combined. |
| 3807 | */ |
| 3808 | |
| 3809 | typedef struct |
| 3810 | { |
| 3811 | Addr addr; |
| 3812 | SizeT size; |
| 3813 | UInt prot; |
| 3814 | ULong dev; |
| 3815 | ULong ino; |
| 3816 | Off64T foffset; |
| 3817 | HChar filename[VKI_PATH_MAX]; |
| 3818 | } Mapping; |
| 3819 | |
| 3820 | static SizeT read_proc_file(const HChar *filename, HChar *buf, |
| 3821 | SizeT buf_size, const HChar *buf_size_name, |
| 3822 | SizeT entry_size) |
| 3823 | { |
| 3824 | SysRes res = ML_(am_open)(filename, VKI_O_RDONLY, 0); |
| 3825 | if (sr_isError(res)) { |
| 3826 | HChar message[100]; |
| 3827 | ML_(am_sprintf)(message, "Cannot open %s.", filename); |
| 3828 | ML_(am_barf)(message); |
| 3829 | } |
| 3830 | Int fd = sr_Res(res); |
| 3831 | |
| 3832 | Int r = ML_(am_read)(fd, buf, buf_size); |
| 3833 | ML_(am_close)(fd); |
| 3834 | if (r < 0) { |
| 3835 | HChar message[100]; |
| 3836 | ML_(am_sprintf)(message, "I/O error on %s.", filename); |
| 3837 | ML_(am_barf)(message); |
| 3838 | } |
| 3839 | |
| 3840 | if (r >= buf_size) |
| 3841 | ML_(am_barf_toolow)(buf_size_name); |
| 3842 | |
| 3843 | if (r % entry_size != 0) { |
| 3844 | HChar message[100]; |
| 3845 | ML_(am_sprintf)(message, "Bogus values read from %s.", filename); |
| 3846 | ML_(am_barf)(message); |
| 3847 | } |
| 3848 | |
| 3849 | return r / entry_size; |
| 3850 | } |
| 3851 | |
| 3852 | static Mapping *next_xmap(const HChar *buffer, SizeT entries, SizeT *idx, |
| 3853 | Mapping *mapping) |
| 3854 | { |
| 3855 | aspacem_assert(idx); |
| 3856 | aspacem_assert(mapping); |
| 3857 | |
| 3858 | if (*idx >= entries) |
| 3859 | return NULL; /* No more entries */ |
| 3860 | |
| 3861 | const vki_prxmap_t *map = (const vki_prxmap_t *)buffer + *idx; |
| 3862 | |
| 3863 | mapping->addr = map->pr_vaddr; |
| 3864 | mapping->size = map->pr_size; |
| 3865 | |
| 3866 | mapping->prot = 0; |
| 3867 | if (map->pr_mflags & VKI_MA_READ) |
| 3868 | mapping->prot |= VKI_PROT_READ; |
| 3869 | if (map->pr_mflags & VKI_MA_WRITE) |
| 3870 | mapping->prot |= VKI_PROT_WRITE; |
| 3871 | if (map->pr_mflags & VKI_MA_EXEC) |
| 3872 | mapping->prot |= VKI_PROT_EXEC; |
| 3873 | |
| 3874 | if (map->pr_dev != VKI_PRNODEV) { |
| 3875 | mapping->dev = map->pr_dev; |
| 3876 | mapping->ino = map->pr_ino; |
| 3877 | mapping->foffset = map->pr_offset; |
| 3878 | } |
| 3879 | else { |
| 3880 | mapping->dev = 0; |
| 3881 | mapping->ino = 0; |
| 3882 | mapping->foffset = 0; |
| 3883 | } |
| 3884 | |
| 3885 | /* Try to get the filename. */ |
| 3886 | mapping->filename[0] = '\0'; |
| 3887 | if (map->pr_mapname[0] != '\0') { |
| 3888 | ML_(am_sprintf)(mapping->filename, "/proc/self/path/%s", |
| 3889 | map->pr_mapname); |
| 3890 | Int r = ML_(am_readlink)(mapping->filename, mapping->filename, |
| 3891 | sizeof(mapping->filename) - 1); |
| 3892 | if (r == -1) { |
| 3893 | /* If Valgrind is executed in a non-global zone and the link in |
| 3894 | /proc/self/path/ represents a file that is available through lofs |
| 3895 | from a global zone then the kernel may not be able to resolve the |
| 3896 | link. |
| 3897 | |
| 3898 | In such a case, return a corresponding /proc/self/object/ file to |
| 3899 | allow Valgrind to read the file if it is necessary. |
| 3900 | |
| 3901 | This can create some discrepancy for the sanity check. For |
| 3902 | instance, if a client program mmaps some file then the address |
| 3903 | space manager will have a correct zone-local name of that file, |
| 3904 | but the sanity check will receive a different file name from this |
| 3905 | code. This currently does not represent a problem because the |
| 3906 | sanity check ignores the file names (it uses device and inode |
| 3907 | numbers for the comparison). |
| 3908 | */ |
| 3909 | ML_(am_sprintf)(mapping->filename, "/proc/self/object/%s", |
| 3910 | map->pr_mapname); |
| 3911 | } |
| 3912 | else { |
| 3913 | aspacem_assert(r >= 0); |
| 3914 | mapping->filename[r] = '\0'; |
| 3915 | } |
| 3916 | } |
| 3917 | |
| 3918 | *idx += 1; |
| 3919 | return mapping; |
| 3920 | } |
| 3921 | |
| 3922 | static Mapping *next_rmap(const HChar *buffer, SizeT entries, SizeT *idx, |
| 3923 | Mapping *mapping) |
| 3924 | { |
| 3925 | aspacem_assert(idx); |
| 3926 | aspacem_assert(mapping); |
| 3927 | |
| 3928 | if (*idx >= entries) |
| 3929 | return NULL; /* No more entries */ |
| 3930 | |
| 3931 | const vki_prmap_t *map = (const vki_prmap_t *)buffer + *idx; |
| 3932 | |
| 3933 | mapping->addr = map->pr_vaddr; |
| 3934 | mapping->size = map->pr_size; |
| 3935 | |
| 3936 | mapping->prot = 0; |
| 3937 | if (map->pr_mflags & VKI_MA_READ) |
| 3938 | mapping->prot |= VKI_PROT_READ; |
| 3939 | if (map->pr_mflags & VKI_MA_WRITE) |
| 3940 | mapping->prot |= VKI_PROT_WRITE; |
| 3941 | if (map->pr_mflags & VKI_MA_EXEC) |
| 3942 | mapping->prot |= VKI_PROT_EXEC; |
| 3943 | |
| 3944 | mapping->dev = 0; |
| 3945 | mapping->ino = 0; |
| 3946 | mapping->foffset = 0; |
| 3947 | mapping->filename[0] = '\0'; |
| 3948 | |
| 3949 | *idx += 1; |
| 3950 | return mapping; |
| 3951 | } |
| 3952 | |
| 3953 | /* Used for two purposes: |
| 3954 | 1. Establish initial mappings upon the process startup |
| 3955 | 2. Check mappings during aspacemgr sanity check |
| 3956 | */ |
| 3957 | static void parse_procselfmaps ( |
| 3958 | void (*record_mapping)( Addr addr, SizeT len, UInt prot, |
| 3959 | ULong dev, ULong ino, Off64T offset, |
| 3960 | const HChar *filename ), |
| 3961 | void (*record_gap)( Addr addr, SizeT len ) |
| 3962 | ) |
| 3963 | { |
| 3964 | Addr start = Addr_MIN; |
| 3965 | Addr gap_start = Addr_MIN; |
| 3966 | |
| 3967 | #define M_XMAP_BUF (VG_N_SEGMENTS * sizeof(vki_prxmap_t)) |
| 3968 | /* Static to keep it out of stack frame... */ |
| 3969 | static HChar xmap_buf[M_XMAP_BUF]; |
| 3970 | const Mapping *xmap = NULL; |
| 3971 | SizeT xmap_index = 0; /* Current entry */ |
| 3972 | SizeT xmap_entries; |
| 3973 | Mapping xmap_mapping; |
| 3974 | Bool advance_xmap; |
| 3975 | |
| 3976 | #define M_RMAP_BUF (VG_N_SEGMENTS * sizeof(vki_prmap_t)) |
| 3977 | static HChar rmap_buf[M_RMAP_BUF]; |
| 3978 | const Mapping *rmap = NULL; |
| 3979 | SizeT rmap_index = 0; /* Current entry */ |
| 3980 | SizeT rmap_entries; |
| 3981 | Mapping rmap_mapping; |
| 3982 | Bool advance_rmap; |
| 3983 | |
| 3984 | /* Read fully /proc/self/xmap and /proc/self/rmap. */ |
| 3985 | xmap_entries = read_proc_file("/proc/self/xmap", xmap_buf, M_XMAP_BUF, |
| 3986 | "M_XMAP_BUF", sizeof(vki_prxmap_t)); |
| 3987 | |
| 3988 | rmap_entries = read_proc_file("/proc/self/rmap", rmap_buf, M_RMAP_BUF, |
| 3989 | "M_RMAP_BUF", sizeof(vki_prmap_t)); |
| 3990 | |
| 3991 | /* Get the first xmap and rmap. */ |
| 3992 | advance_xmap = True; |
| 3993 | advance_rmap = True; |
| 3994 | |
| 3995 | while (1) { |
| 3996 | /* Get next xmap or rmap if necessary. */ |
| 3997 | if (advance_xmap) { |
| 3998 | xmap = next_xmap(xmap_buf, xmap_entries, &xmap_index, &xmap_mapping); |
| 3999 | advance_xmap = False; |
| 4000 | } |
| 4001 | if (advance_rmap) { |
| 4002 | rmap = next_rmap(rmap_buf, rmap_entries, &rmap_index, &rmap_mapping); |
| 4003 | advance_rmap = False; |
| 4004 | } |
| 4005 | |
| 4006 | /* Check if the end has been reached. */ |
| 4007 | if (rmap == NULL) |
| 4008 | break; |
| 4009 | |
| 4010 | /* Invariants */ |
| 4011 | if (xmap != NULL) { |
| 4012 | aspacem_assert(start <= xmap->addr); |
| 4013 | aspacem_assert(rmap->addr <= xmap->addr); |
| 4014 | } |
| 4015 | |
| 4016 | if (xmap != NULL && start == xmap->addr) { |
| 4017 | /* xmap mapping reached. */ |
| 4018 | aspacem_assert(xmap->addr >= rmap->addr && |
| 4019 | xmap->addr + xmap->size <= rmap->addr + rmap->size); |
| 4020 | aspacem_assert(xmap->prot == rmap->prot); |
| 4021 | |
| 4022 | if (record_mapping != NULL) |
| 4023 | (*record_mapping)(xmap->addr, xmap->size, xmap->prot, xmap->dev, |
| 4024 | xmap->ino, xmap->foffset, |
| 4025 | (xmap->filename[0] != '\0') ? |
| 4026 | xmap->filename : NULL); |
| 4027 | |
| 4028 | start = xmap->addr + xmap->size; |
| 4029 | advance_xmap = True; |
| 4030 | } |
| 4031 | else if (start >= rmap->addr) { |
| 4032 | /* Reserved-only part. */ |
| 4033 | /* First calculate size until the end of this reserved mapping... */ |
| 4034 | SizeT size = rmap->addr + rmap->size - start; |
| 4035 | /* ... but shrink it if some xmap is in a way. */ |
| 4036 | if (xmap != NULL && size > xmap->addr - start) |
| 4037 | size = xmap->addr - start; |
| 4038 | |
| 4039 | if (record_mapping != NULL) |
| 4040 | (*record_mapping)(start, size, rmap->prot, 0, 0, 0, NULL); |
| 4041 | start += size; |
| 4042 | } |
| 4043 | else { |
| 4044 | /* Gap. */ |
| 4045 | if (record_gap != NULL && gap_start < start) |
| 4046 | (*record_gap)(gap_start, start - gap_start); |
| 4047 | start = rmap->addr; |
| 4048 | } |
| 4049 | |
| 4050 | if (rmap->addr + rmap->size <= start) |
| 4051 | advance_rmap = True; |
| 4052 | |
| 4053 | gap_start = start; |
| 4054 | } |
| 4055 | |
| 4056 | if (record_gap != NULL && gap_start < Addr_MAX) |
| 4057 | (*record_gap)(gap_start, Addr_MAX - gap_start + 1); |
| 4058 | } |
| 4059 | |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame^] | 4060 | /* parse_procselfmaps() callbacks do not allow for easy thread safety. */ |
| 4061 | static Addr found_addr; |
| 4062 | static SizeT found_size; |
| 4063 | static UInt found_prot; |
| 4064 | |
| 4065 | /* Reports a new mapping into variables above. */ |
| 4066 | static void new_segment_found_callback(Addr addr, SizeT len, UInt prot, |
| 4067 | ULong dev, ULong ino, Off64T offset, const HChar *filename) |
| 4068 | { |
| 4069 | aspacem_assert(addr <= addr + len - 1); |
| 4070 | |
| 4071 | Int iLo = find_nsegment_idx(addr); |
| 4072 | Int iHi = find_nsegment_idx(addr + len - 1); |
| 4073 | aspacem_assert(iLo <= iHi); |
| 4074 | aspacem_assert(nsegments[iLo].start <= addr); |
| 4075 | aspacem_assert(nsegments[iHi].end >= addr + len - 1); |
| 4076 | |
| 4077 | /* Do not perform any sanity checks. That is done in other places. |
| 4078 | Just find if a reported mapping is found in aspacemgr's book keeping. */ |
| 4079 | for (Int i = iLo; i <= iHi; i++) { |
| 4080 | if ((nsegments[i].kind == SkFree) || (nsegments[i].kind == SkResvn)) { |
| 4081 | found_addr = addr; |
| 4082 | found_size = len; |
| 4083 | found_prot = prot; |
| 4084 | break; |
| 4085 | } |
| 4086 | } |
| 4087 | } |
| 4088 | |
| 4089 | /* Returns True if a new segment was found. */ |
| 4090 | Bool VG_(am_search_for_new_segment)(Addr *addr, SizeT *size, UInt *prot) |
| 4091 | { |
| 4092 | found_addr = 0; |
| 4093 | parse_procselfmaps(new_segment_found_callback, NULL); |
| 4094 | |
| 4095 | if (found_addr != 0) { |
| 4096 | *addr = found_addr; |
| 4097 | *size = found_size; |
| 4098 | *prot = found_prot; |
| 4099 | return True; |
| 4100 | } else { |
| 4101 | return False; |
| 4102 | } |
| 4103 | } |
| 4104 | |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 4105 | #endif // defined(VGO_solaris) |
| 4106 | |
| 4107 | /*------END-procmaps-parser-for-Solaris--------------------------*/ |
| 4108 | |
| 4109 | #endif // defined(VGO_linux) || defined(VGO_darwin) || defined(VGO_solaris) |
njn | f76d27a | 2009-05-28 01:53:07 +0000 | [diff] [blame] | 4110 | |
fitzhardinge | 98abfc7 | 2003-12-16 02:05:15 +0000 | [diff] [blame] | 4111 | /*--------------------------------------------------------------------*/ |
njn | 945ed2e | 2005-06-24 03:28:30 +0000 | [diff] [blame] | 4112 | /*--- end ---*/ |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 4113 | /*--------------------------------------------------------------------*/ |