From: Ivo Raisr Date: Thu, 20 Aug 2015 20:25:19 +0000 (+0000) Subject: Initial data (brk) segment is setup on demand, when a first brk() syscall X-Git-Tag: svn/VALGRIND_3_11_0~85 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=542fa886dc861dbe605f4195d2bd6ec06f34a6ab;p=thirdparty%2Fvalgrind.git Initial data (brk) segment is setup on demand, when a first brk() syscall is made. It cannot be established during client image initialization because that would conflict with a temporary stack which ld.so.1 (when executed directly) uses for loading the target dynamic executable. See PRE(sys_brk) in syswrap-solaris.c. Preparatory work for ldsoexec support. n-i-bz git-svn-id: svn://svn.valgrind.org/valgrind/trunk@15572 --- diff --git a/coregrind/m_initimg/initimg-solaris.c b/coregrind/m_initimg/initimg-solaris.c index 49e4981e6a..d71b0e8963 100644 --- a/coregrind/m_initimg/initimg-solaris.c +++ b/coregrind/m_initimg/initimg-solaris.c @@ -747,63 +747,6 @@ static Addr setup_client_stack(void *init_sp, return client_SP; } - -/* Allocate the client data segment. It is an expandable anonymous mapping - abutting a 1-page reservation. The data segment starts at VG_(brk_base) - and runs up to VG_(brk_limit). None of these two values have to be - page-aligned. - Reservation segment is used to protect the data segment merging with - a pre-existing segment. This should be no problem because address space - manager ensures that requests for client address space are satisfied from - the highest available addresses. However when memory is low, data segment - can meet with mmap'ed objects and the reservation segment separates these. - The page that contains VG_(brk_base) is already allocated by the program's - loaded data segment. The brk syscall wrapper handles this special case. - See the brk syscall wrapper for more information. */ -static void setup_client_dataseg(SizeT initial_size) -{ - Bool ok; - SysRes sres; - Addr anon_start = VG_PGROUNDUP(VG_(brk_base)); - SizeT anon_size = VG_PGROUNDUP(initial_size); - Addr resvn_start = anon_start + anon_size; - SizeT resvn_size = VKI_PAGE_SIZE; - const NSegment *seg; - UInt prot; - - vg_assert(VG_IS_PAGE_ALIGNED(anon_size)); - vg_assert(VG_IS_PAGE_ALIGNED(resvn_size)); - vg_assert(VG_IS_PAGE_ALIGNED(anon_start)); - vg_assert(VG_IS_PAGE_ALIGNED(resvn_start)); - - /* Stay sane (because there's been no brk activity yet). */ - vg_assert(VG_(brk_base) == VG_(brk_limit)); - - /* Find the loaded data segment and remember its protection. */ - seg = VG_(am_find_nsegment)(VG_(brk_base) - 1); - vg_assert(seg); - prot = (seg->hasR ? VKI_PROT_READ : 0) - | (seg->hasW ? VKI_PROT_WRITE : 0) - | (seg->hasX ? VKI_PROT_EXEC : 0); - - /* Try to create the data segment and associated reservation where - VG_(brk_base) says. */ - ok = VG_(am_create_reservation)(resvn_start, resvn_size, SmLower, anon_size); - if (!ok) { - /* That didn't work, we're hosed. */ - VG_(printf)("valgrind: cannot initialize a brk segment\n"); - VG_(exit)(1); - /*NOTREACHED*/ - } - vg_assert(ok); - - /* Map the data segment. */ - sres = VG_(am_mmap_anon_fixed_client)(anon_start, anon_size, prot); - vg_assert(!sr_isError(sres)); - vg_assert(sr_Res(sres) == anon_start); -} - - /*====================================================================*/ /*=== TOP-LEVEL: VG_(setup_client_initial_image) ===*/ /*====================================================================*/ @@ -898,26 +841,10 @@ IIFinaliseImageInfo VG_(ii_create_image)(IICreateImageInfo iicii, iifii.clstack_max_size); } - //-------------------------------------------------------------- - // Setup client data (brk) segment. Initially segment at least - // 1 MB and at most 8 MB large which abuts a 1-page reservation. - // p: load_client() [for 'info' and hence VG_(brk_base)] - //-------------------------------------------------------------- - { - SizeT m1 = 1024 * 1024; - SizeT m8 = 8 * m1; - SizeT dseg_max_size = VG_(client_rlimit_data).rlim_cur; - VG_(debugLog)(1, "initimg", "Setup client data (brk) segment at %#lx\n", - VG_(brk_base)); - if (dseg_max_size < m1) - dseg_max_size = m1; - if (dseg_max_size > m8) - dseg_max_size = m8; - dseg_max_size = VG_PGROUNDUP(dseg_max_size); - - setup_client_dataseg(dseg_max_size); - } - + /* Initial data (brk) segment is setup on demand, when a first brk() syscall + is made. It cannot be established now because it would conflict with + a temporary stack which ld.so.1 (when executed directly) uses for loading + the target dynamic executable. See PRE(sys_brk) in syswrap-solaris.c. */ return iifii; } @@ -934,7 +861,6 @@ IIFinaliseImageInfo VG_(ii_create_image)(IICreateImageInfo iicii, void VG_(ii_finalise_image)(IIFinaliseImageInfo iifii) { ThreadArchState *arch = &VG_(threads)[1].arch; - const NSegment *seg; # if defined(VGA_x86) vg_assert(0 == sizeof(VexGuestX86State) % LibVEX_GUEST_STATE_ALIGN); @@ -990,14 +916,16 @@ void VG_(ii_finalise_image)(IIFinaliseImageInfo iifii) VG_TRACK(post_reg_write, Vg_CoreStartup, 1/*tid*/, 0/*offset*/, sizeof(VexGuestArchState)); - /* Tell the tool about the client data segment and then kill it which will - make it inaccessible/unaddressable. */ - seg = VG_(am_find_nsegment)(VG_PGROUNDUP(VG_(brk_base))); - vg_assert(seg); - vg_assert(seg->kind == SkAnonC); - VG_TRACK(new_mem_brk, VG_(brk_base), seg->end + 1 - VG_(brk_base), - 1/*tid*/); - VG_TRACK(die_mem_brk, VG_(brk_base), seg->end + 1 - VG_(brk_base)); + /* Make inaccessible/unaddressable the end of the client data segment. + See PRE(sys_brk) in syswrap-solaris.c for details. Nothing to do in + case VG_(brk_base) starts on the beginning of a free page. */ + const NSegment *seg = VG_(am_find_nsegment)(VG_(brk_base)); + if (seg != NULL) { + vg_assert((seg->kind == SkFileC) || (seg->kind == SkAnonC)); + VG_TRACK(new_mem_brk, VG_(brk_base), seg->end + 1 - VG_(brk_base), + 1 /* tid */); + VG_TRACK(die_mem_brk, VG_(brk_base), seg->end + 1 - VG_(brk_base)); + } } #endif // defined(VGO_solaris) diff --git a/coregrind/m_syswrap/syswrap-solaris.c b/coregrind/m_syswrap/syswrap-solaris.c index d1f010c9e0..4ea2463f45 100644 --- a/coregrind/m_syswrap/syswrap-solaris.c +++ b/coregrind/m_syswrap/syswrap-solaris.c @@ -1803,15 +1803,18 @@ PRE(sys_time) PRE_REG_READ0(long, "time"); } -/* Data segment for brk (heap). - Initial data segment is established during image initialization - (initimg-solaris.c). Notable facts: +/* Data segment for brk (heap). It is an expandable anonymous mapping + abutting a 1-page reservation. The data segment starts at VG_(brk_base) + and runs up to VG_(brk_limit). None of these two values have to be + page-aligned. Initial data segment is established on demand here (see + initimg-solaris.c for rationale). + Notable facts: - VG_(brk_base) is not page aligned; does not move - VG_(brk_limit) moves between [VG_(brk_base), data segment end] - data segment end is always page aligned - right after data segment end is 1-page reservation - | heap | + | heap | 1 page +------+------+--------------+-------+ | BSS | anon | anon | resvn | +------+------+--------------+-------+ @@ -1824,9 +1827,63 @@ PRE(sys_time) VG_(brk_base) -- not page aligned -- does not move Because VG_(brk_base) is not page-aligned and is initially located within - pre-established data segment, special care has to be taken in the code below - to handle this feature. -*/ + pre-established BSS (data) segment, special care has to be taken in the code + below to handle this feature. + + Reservation segment is used to protect the data segment merging with + a pre-existing segment. This should be no problem because address space + manager ensures that requests for client address space are satisfied from + the highest available addresses. However when memory is low, data segment + can meet with mmap'ed objects and the reservation segment separates these. + The page that contains VG_(brk_base) is already allocated by the program's + loaded data segment. The brk syscall wrapper handles this special case. */ + +/* Establishes initial data segment for brk (heap). */ +static Bool setup_client_dataseg(SizeT initial_size, ThreadId tid) +{ + Addr anon_start = VG_PGROUNDUP(VG_(brk_base)); + SizeT anon_size = VG_PGROUNDUP(initial_size); + Addr resvn_start = anon_start + anon_size; + SizeT resvn_size = VKI_PAGE_SIZE; + + vg_assert(VG_IS_PAGE_ALIGNED(anon_size)); + vg_assert(VG_IS_PAGE_ALIGNED(resvn_size)); + vg_assert(VG_IS_PAGE_ALIGNED(anon_start)); + vg_assert(VG_IS_PAGE_ALIGNED(resvn_start)); + vg_assert(VG_(brk_base) == VG_(brk_limit)); + + /* Find the loaded data segment and remember its protection. */ + const NSegment *seg = VG_(am_find_nsegment)(VG_(brk_base) - 1); + vg_assert(seg != NULL); + UInt prot = (seg->hasR ? VKI_PROT_READ : 0) + | (seg->hasW ? VKI_PROT_WRITE : 0) + | (seg->hasX ? VKI_PROT_EXEC : 0); + + /* Try to create the data segment and associated reservation where + VG_(brk_base) says. */ + Bool ok = VG_(am_create_reservation)(resvn_start, resvn_size, SmLower, + anon_size); + if (!ok) { + /* That didn't work, we're hosed. */ + return False; + } + + /* Map the data segment. */ + SysRes sres = VG_(am_mmap_anon_fixed_client)(anon_start, anon_size, prot); + vg_assert(!sr_isError(sres)); + vg_assert(sr_Res(sres) == anon_start); + + /* Tell the tool about the client data segment and then kill it which will + make it initially inaccessible/unaddressable. */ + seg = VG_(am_find_nsegment)(anon_start); + vg_assert(seg != NULL); + vg_assert(seg->kind == SkAnonC); + VG_TRACK(new_mem_brk, VG_(brk_base), seg->end + 1 - VG_(brk_base), tid); + VG_TRACK(die_mem_brk, VG_(brk_base), seg->end + 1 - VG_(brk_base)); + return True; +} + +static Bool brk_segment_established = False; PRE(sys_brk) { @@ -1845,7 +1902,7 @@ PRE(sys_brk) PRINT("sys_brk ( %#lx )", ARG1); PRE_REG_READ1(unsigned long, "brk", vki_caddr_t, end_data_segment); - if (!new_brk) { + if (new_brk == 0) { /* brk(0) - specific to Solaris 11 only. */ SET_STATUS_Success(old_brk_limit); return; @@ -1866,6 +1923,33 @@ PRE(sys_brk) return; } + if (!brk_segment_established) { + /* Stay sane (because there should have been no brk activity yet). */ + vg_assert(VG_(brk_base) == VG_(brk_limit)); + + /* Establish an initial data segment for brk (heap). + Initially at least 1 MB and at most 8 MB large. */ + SizeT m1 = 1024 * 1024; + SizeT m8 = 8 * m1; + SizeT dseg_max_size = VG_(client_rlimit_data).rlim_cur; + VG_(debugLog)(1, "syswrap-solaris", "Setup client data (brk) segment " + "at %#lx\n", VG_(brk_base)); + if (dseg_max_size < m1) + dseg_max_size = m1; + if (dseg_max_size > m8) + dseg_max_size = m8; + dseg_max_size = VG_PGROUNDUP(dseg_max_size); + + if (!setup_client_dataseg(dseg_max_size, tid)) { + VG_(umsg)("Cannot map memory to initialize brk segment in thread #%d " + "at %#lx\n", tid, VG_(brk_base)); + SET_STATUS_Failure(VKI_ENOMEM); + return; + } + + brk_segment_established = True; + } + if (new_brk < old_brk_limit) { /* Shrinking the data segment. Be lazy and don't munmap the excess area. */