#define AT_SECURE 23 /* secure mode boolean */
#endif /* AT_SECURE */
-/* Amount to reserve for Valgrind's internal mappings */
-#define VALGRIND_MAPSIZE (128*1024*1024)
-
/* redzone gap between client address space and shadow */
#define REDZONE_SIZE (1 * 1024*1024)
Addr VG_(shadow_end);
Addr VG_(valgrind_base); /* valgrind's address range */
-Addr VG_(valgrind_mmap_end); /* valgrind's mmaps are between valgrind_base and here */
Addr VG_(valgrind_end);
vki_rlimit VG_(client_rlimit_data);
static void layout_client_space(Addr argc_addr)
{
VG_(client_base) = CLIENT_BASE;
- VG_(valgrind_mmap_end) = (addr_t)&kickstart_base; /* end of V's mmaps */
- VG_(valgrind_base) = VG_(valgrind_mmap_end) - VALGRIND_MAPSIZE;
+ VG_(valgrind_base) = (addr_t)&kickstart_base;
VG_(valgrind_end) = ROUNDUP(argc_addr, 0x10000); /* stack */
as_pad((void *)VG_(client_base), (void *)VG_(valgrind_base));
"shadow_base %8x (%dMB)\n"
"shadow_end %8x (%dMB)\n"
"valgrind_base %8x (%dMB)\n"
- "valgrind_mmap_end %8x (%dMB)\n"
"valgrind_end %8x\n",
VG_(client_base), SEGSIZE(client_base, client_mapbase),
VG_(client_mapbase), SEGSIZE(client_mapbase, client_end),
VG_(client_end), SEGSIZE(client_end, shadow_base),
VG_(shadow_base), SEGSIZE(shadow_base, shadow_end),
VG_(shadow_end), SEGSIZE(shadow_end, valgrind_base),
- VG_(valgrind_base), SEGSIZE(valgrind_base, valgrind_mmap_end),
- VG_(valgrind_mmap_end), SEGSIZE(valgrind_mmap_end, valgrind_end),
+ VG_(valgrind_base), SEGSIZE(valgrind_base, valgrind_end),
VG_(valgrind_end)
);
/*=== main() ===*/
/*====================================================================*/
+/*
+ This code decides on the layout of the client and Valgrind address
+ spaces, loads valgrind.so and the skin.so into the valgrind part,
+ loads the client executable (and the dynamic linker, if necessary)
+ into the client part, and calls into Valgrind proper.
+
+ The code is careful not to allow spurious mappings to appear in the
+ wrong parts of the address space. In particular, to make sure
+ dlopen puts things in the right place, it will pad out the forbidden
+ chunks of address space so that dlopen is forced to put things where
+ we want them.
+
+ The memory map it creates is:
+
+ CLIENT_BASE +-------------------------+
+ | client address space |
+ : :
+ : :
+ | client stack |
+ client_end +-------------------------+
+ | redzone |
+ shadow_base +-------------------------+
+ | |
+ : shadow memory for skins :
+ | (may be 0 sized) |
+ shadow_end +-------------------------+
+ : gap (may be 0 sized) :
+ valgrind_base +-------------------------+
+ | kickstart executable |
+ | valgrind heap vvvvvvvvv| (barely used)
+ - -
+ | valgrind .so files |
+ | and mappings |
+ - -
+ | valgrind stack ^^^^^^^^^|
+ valgrind_end +-------------------------+
+ : kernel :
+
+ Nb: Before we can do general allocations with VG_(arena_malloc)() and
+ VG_(mmap)(), we need to build the segment skip-list, so we know where
+ we can put things. However, building that structure requires
+ allocating memory. So we need to a bootstrapping process. It's done
+ by making VG_(arena_malloc)() have a special static superblock that's
+ used for the first 1MB's worth of allocations. This is enough to
+ build the segment skip-list.
+*/
+
static int prmap(void *start, void *end, const char *perm, off_t off,
int maj, int min, int ino) {
printf("mapping %10p-%10p %s %02x:%02x %d\n",
//--------------------------------------------------------------
// Check we were launched by stage1
- // p: n/a [must be first step]
+ // p: n/a
//--------------------------------------------------------------
scan_auxv();
//==============================================================
// Can use VG_(malloc)() and VG_(arena_malloc)() only after load_tool()
- // -- redzone size is now set.
+ // -- redzone size is now set. This is checked by vg_malloc2.c.
//==============================================================
//--------------------------------------------------------------
// Finalise address space layout
- // p: layout_client_space(), load_tool() [for 'toolinfo']
+ // p: layout_client_space(), load_tool() [for 'toolinfo']
//--------------------------------------------------------------
layout_remaining_space( toolinfo->shadow_ratio );
// Valgrind. (This is where the old VG_(main)() started.)
//==============================================================
- //--------------------------------------------------------------
- // Read /proc/self/maps into a buffer
- // p: all memory layout, environment setup [so memory maps are right]
- //--------------------------------------------------------------
- VG_(read_procselfmaps)();
-
//--------------------------------------------------------------
// atfork
// p: n/a
setup_file_descriptors();
//--------------------------------------------------------------
- // Setup tool
- // p: VG_(read_procselfmaps)() [so if sk_pre_clo_init calls
- // VG_(malloc), any mmap'd superblocks aren't erroneously
- // identified later as being owned by the client]
- // XXX: is that necessary, now that we look for V's segments separately?
- // XXX: alternatively, if sk_pre_clo_init does use VG_(malloc)(), is it
- // wrong to ignore any segments that might add in parse_procselfmaps?
- // p: setup_client_stack() [for 'VG_(client_arg[cv]']
+ // Read /proc/self/maps into a buffer
+ // p: all memory layout, environment setup [so memory maps are right]
+ //--------------------------------------------------------------
+ VG_(read_procselfmaps)();
+
+ //--------------------------------------------------------------
+ // Build segment map (Valgrind segments only)
+ // p: read proc/self/maps
+ // p: sk_pre_clo_init() [to setup new_mem_startup tracker]
+ //--------------------------------------------------------------
+ VG_(parse_procselfmaps) ( build_valgrind_map_callback );
+
+ //==============================================================
+ // Can use VG_(arena_malloc)() with non-CORE arena after segments set up
+ //==============================================================
+
+ //--------------------------------------------------------------
+ // Init tool: pre_clo_init, process cmd line, post_clo_init
+ // p: setup_client_stack() [for 'VG_(client_arg[cv]']
+ // p: load_tool() [for 'tool']
+ // p: setup_file_descriptors() [for 'VG_(fd_xxx_limit)']
+ // p: parse_procselfmaps [so VG segments are setup so tool can
+ // call VG_(malloc)]
//--------------------------------------------------------------
(*toolinfo->sk_pre_clo_init)();
VG_(tool_init_dlsym)(tool_dlhandle);
VG_(sanity_check_needs)();
- //--------------------------------------------------------------
// If --tool and --help/--help-debug was given, now give the core+tool
// help message
- // p: pre_clo_init()
- //--------------------------------------------------------------
if (need_help) {
usage(/*--help-debug?*/2 == need_help);
}
+ process_cmd_line_options(client_auxv, tool);
+
+ SK_(post_clo_init)();
//--------------------------------------------------------------
- // Process Valgrind's + tool's command-line options
- // p: load_tool() [for 'tool']
- // p: setup_file_descriptors() [for 'VG_(fd_xxx_limit)']
- // p: sk_pre_clo_init [to set 'command_line_options' need]
+ // Build segment map (all segments)
+ // p: setup_client_stack() [for 'esp_at_startup']
+ // p: init tool [for 'new_mem_startup']
//--------------------------------------------------------------
- process_cmd_line_options(client_auxv, tool);
+ esp_at_startup___global_arg = esp_at_startup;
+ VG_(parse_procselfmaps) ( build_segment_map_callback ); /* everything */
+ esp_at_startup___global_arg = 0;
+
+ //--------------------------------------------------------------
+ // Initialize our trampoline page (which is also sysinfo stuff)
+ // p: setup_client_stack() [for 'esp_at_startup']
+ //--------------------------------------------------------------
+ VG_(memcpy)( (void *)VG_(client_trampoline_code),
+ &VG_(trampoline_code_start), VG_(trampoline_code_length) );
+ VG_(mprotect)( (void *)VG_(client_trampoline_code),
+ VG_(trampoline_code_length), VKI_PROT_READ|VKI_PROT_EXEC );
+
+ //==============================================================
+ // Can use VG_(map)() after segments set up
+ //==============================================================
//--------------------------------------------------------------
// Allow GDB attach
VG_(do_syscall)(__NR_pause);
}
- //--------------------------------------------------------------
- // Setup tool, post command-line processing
- // p: process_cmd_line_options [tool assumes it]
- //--------------------------------------------------------------
- SK_(post_clo_init)();
-
//--------------------------------------------------------------
// Set up baseBlock
// p: {pre,post}_clo_init() [for tool helper registration]
VGP_PUSHCC(VgpStartup);
- //--------------------------------------------------------------
- // Reserve Valgrind's kickstart, heap and stack
- // p: XXX ???
- //--------------------------------------------------------------
- VG_(map_segment)(VG_(valgrind_mmap_end),
- VG_(valgrind_end)-VG_(valgrind_mmap_end),
- VKI_PROT_NONE, SF_VALGRIND|SF_FIXED);
-
- //--------------------------------------------------------------
- // Identify Valgrind's segments
- // p: read proc/self/maps
- // p: VG_(map_segment) [XXX ???]
- // p: sk_pre_clo_init() [to setup new_mem_startup tracker]
- //--------------------------------------------------------------
- VG_(parse_procselfmaps) ( build_valgrind_map_callback );
-
- // XXX: I can't see why these two need to be separate; could they be
- // folded together? If not, need a comment explaining why.
- //
- // XXX: can we merge reading and parsing of /proc/self/maps?
- //
- // XXX: can we dynamically allocate the /proc/self/maps buffer? (or mmap
- // it?) Or does that disturb its contents...
-
- //--------------------------------------------------------------
- // Build segment map (all segments)
- // p: setup_client_stack() [for 'esp_at_startup']
- //--------------------------------------------------------------
- esp_at_startup___global_arg = esp_at_startup;
- VG_(parse_procselfmaps) ( build_segment_map_callback ); /* everything */
- esp_at_startup___global_arg = 0;
-
- //==============================================================
- // Can only use VG_(map)() after VG_(map_segment)() [XXX ???]
- //==============================================================
-
- //--------------------------------------------------------------
- // Build segment map (all segments)
- // p: setup_client_stack() [for 'esp_at_startup']
- //--------------------------------------------------------------
- /* Initialize our trampoline page (which is also sysinfo stuff) */
- VG_(memcpy)( (void *)VG_(client_trampoline_code),
- &VG_(trampoline_code_start), VG_(trampoline_code_length) );
- VG_(mprotect)( (void *)VG_(client_trampoline_code),
- VG_(trampoline_code_length), VKI_PROT_READ|VKI_PROT_EXEC );
-
//--------------------------------------------------------------
// Read suppression file
// p: process_cmd_line_options() [for VG_(clo_suppressions)]
//--------------------------------------------------------------
setup_pointercheck();
-
-
//--------------------------------------------------------------
// Run!
//--------------------------------------------------------------
/*--- Arena management stuff ---*/
/*------------------------------------------------------------*/
+#define CORE_ARENA_MIN_SZW 262144
+
/* The arena structures themselves. */
static Arena vg_arena[VG_N_ARENAS];
static
void ensure_mm_init ( void )
{
- Int client_rz_szW;
+ static Int client_rz_szW;
static Bool init_done = False;
- if (init_done) return;
+ if (init_done) {
+ // Make sure the client arena's redzone size never changes. Could
+ // happen if VG_(arena_malloc) was called too early, ie. before the
+ // tool was loaded.
+ vg_assert(client_rz_szW == VG_(vg_malloc_redzone_szB)/4);
+ return;
+ }
/* Use checked red zones (of various sizes) for our internal stuff,
and an unchecked zone of arbitrary size for the client. Of
here, which merely checks at the time of freeing that the red
zone words are unchanged. */
- arena_init ( &vg_arena[VG_AR_CORE], "core", 2, True, 262144, False );
+ arena_init ( &vg_arena[VG_AR_CORE], "core", 2, True, CORE_ARENA_MIN_SZW, False );
arena_init ( &vg_arena[VG_AR_TOOL], "tool", 2, True, 262144, False );
/*--- Superblock management stuff ---*/
/*------------------------------------------------------------*/
+// If not enough memory available, either aborts (for non-client memory)
+// or returns 0 (for client memory).
static
Superblock* newSuperblock ( Arena* a, Int cszW )
{
+ static Bool called_before = False;
+ static Word bootstrap_superblock[CORE_ARENA_MIN_SZW];
+ Int cszB;
Superblock* sb;
+
cszW += 2; /* Take into account sb->next and sb->n_words fields */
if (cszW < a->min_sblockW) cszW = a->min_sblockW;
while ((cszW % VKI_WORDS_PER_PAGE) > 0) cszW++;
- if (a->clientmem) {
+ cszB = cszW * sizeof(Word);
+
+ if (!called_before) {
+ // First time we're called -- use the special static bootstrap
+ // superblock (see comment at top of main() for details).
+ called_before = True;
+ vg_assert(a == &vg_arena[VG_AR_CORE]);
+ vg_assert(CORE_ARENA_MIN_SZW*sizeof(Word) >= cszB);
+ sb = (Superblock*)bootstrap_superblock;
+
+ } else if (a->clientmem) {
// client allocation -- return 0 to client if it fails
sb = (Superblock *)
- VG_(client_alloc)(0, cszW * sizeof(Word),
+ VG_(client_alloc)(0, cszB,
VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC, 0);
if (NULL == sb) {
return 0;
}
} else {
// non-client allocation -- abort if it fails
- sb = VG_(get_memory_from_mmap) ( cszW * sizeof(Word),
- "newSuperblock" );
+ sb = VG_(get_memory_from_mmap) ( cszB, "newSuperblock" );
}
sb->n_payload_words = cszW - 2;
- a->bytes_mmaped += cszW * sizeof(Word);
+ a->bytes_mmaped += cszB;
if (0)
VG_(message)(Vg_DebugMsg, "newSuperblock, %d payload words",
sb->n_payload_words);
return 0;
}
-extern Char _end;
-Char *VG_(curbrk) = NULL;
-extern void *__curbrk; /* in glibc */
-
-void* VG_(brk) ( void* end_data_segment )
-{
- Addr end;
- Addr brkpage;
- Addr endpage;
-
- if (VG_(curbrk) == NULL) {
- VG_(curbrk) = &_end;
- __curbrk = (void *)VG_(curbrk);
- }
-
- end = (Addr)end_data_segment;
- brkpage = PGROUNDUP(VG_(curbrk));
- endpage = PGROUNDUP(end);
-
- if (0 && VG_(curbrk) != __curbrk)
- VG_(printf)("__curbrk changed unexpectedly: VG_(curbrk)=%p, __curbrk=%p\n",
- VG_(curbrk), __curbrk);
-
- if (0)
- VG_(printf)("brk(end_data_segment=%p); brkpage=%p endpage=%p end=%p curbrk=%p &_end=%p\n",
- end_data_segment, brkpage, endpage, end, VG_(curbrk), &_end);
-
- if (endpage < (Addr)&_end) {
- __curbrk = (void *)VG_(curbrk);
- return (void *)VG_(curbrk);
- }
-
- if (brkpage != endpage) {
- if (brkpage > endpage) {
- Int res = munmap_inner((void *)brkpage, brkpage-endpage);
- vg_assert(0 == res);
- } else {
- Addr res = mmap_inner((void *)brkpage, endpage-brkpage,
- VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC,
- VKI_MAP_FIXED|VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS, -1, 0);
- vg_assert((Addr)-1 != res);
- }
- }
- VG_(curbrk) = (Char *)__curbrk = end_data_segment;
-
- return end_data_segment;
-}
-
/* ---------------------------------------------------------------------
printf implementation. The key function, vg_vprintf(), emits chars
{
static UInt tot_alloc = 0;
void* p;
- Char *b = VG_(brk)(0);
-
- p = (void *)PGROUNDUP(b);
- b = VG_(brk)(p + PGROUNDUP(nBytes));
-
- if (b != (p + PGROUNDUP(nBytes)))
- p = (void *)-1;
+ p = VG_(mmap)(0, nBytes,
+ VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC,
+ VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS, 0, -1, 0);
if (p != ((void*)(-1))) {
- vg_assert(p >= (void *)VG_(valgrind_mmap_end) && p < (void *)VG_(valgrind_end));
+ vg_assert(p >= (void*)VG_(valgrind_base) && p < (void*)VG_(valgrind_end));
tot_alloc += (UInt)nBytes;
if (0)
VG_(printf)(