Question

I'm trying to reproduce the work of mremap to grow an mmap'ed memory/file. I've roughly followed this example but it's intended for anonymous maps, whereas I want it to be a named file (this is a programming language interpreter, so this file serves the equivalent of a "core dump" of the interpreter state).

The original is hard to read due to the nesting of #if-blocks. So this is "extracted", but real code (for the compilation path I'm debugging). The full source can be viewed here, but it doesn't contain this code in question, because it hasn't been committed, because it doesn't work. :) The mremap version is there, though, as well as a malloc fallback.

unsigned pgsz /*= getpagesize()*/ = 4096;

typedef struct {
    int fd;
    unsigned char *base;
    unsigned used;
    unsigned max;
} mfile;

/* reallocate and possibly move mem->base */
mfile *growmem(mfile *mem,
               unsigned sz)
{
    void *tmp;
    int newfd;

    printf("growmem: %p %u + %u\n", mem->base, mem->max, sz);
    if (sz < pgsz) sz = pgsz;
    else sz = (sz/pgsz + 1) * pgsz;
    sz += mem->max;
    printf("growmem: new size: %u\n", sz);

    msync(mem->base, mem->used, MS_SYNC);
    newfd = dup(mem->fd);
    tmp = mmap(NULL, sz, 
            PROT_READ|PROT_WRITE,
            MAP_SHARED | (mem->fd == -1? MAP_ANONYMOUS : 0), 
            newfd, 0); 

    if (tmp == MAP_FAILED)
        error(VMerror, "unable to grow memory");

    memcpy(tmp, mem->base, mem->used);
    munmap(mem->base, mem->max);
    mem->fd = newfd;

    mem->base = tmp;
    mem->max = sz;
    return mem;
}

This appears to work when called, but the program crashes later. I haven't tracked down precisely where, but based on tracing the main eval loop it looks very much like the new portion of the memory is not write accessible. The operator that fails allocates a new dictionary object. And I suspect I've done something stupid by re-using the same file-descriptor. edit: new file descriptor, same segfault.

Breakpoint 2, eval (ctx=0x80049a70) at src/bin/itp.c:482
482         object t = top(ctx->lo, ctx->es, 0);
(gdb) 
Continuing.
evalload <name "dict">
eval
Executing: <operator 24>
Stack: 0:<integer 1>1:<integer 0>2:<integer 0>3:<integer 1>4:<integer 0>5:<integer 0>6:<array L 229 100 78 0>7:<integer 1>8:<dict L 102 16 658 0>9:<integer 8>
Dict Stack: 0:<dict G 358 10 10 0>1:<dict G 358 100 188 0>2:<dict L 102 100 9 0>3:<dict G 486 24 310 0>4:<dict L 102 6 80 0>
Exec Stack: 0:<operator 60>1:<array G 357 2 387 6>2:<boolean false>3:<array G 325 17 416 14>4:<operator 54>5:<operator 63>6:<array G 453 18 414 0>7:<array G 325 1 414 17>8:<array G 325 4 413 5>9:<boolean false>10:<file>11:<file>12:<array L 101 3 103 16>13:<array L 101 1 113 4>14:<operator 24>
   2929 [main] itp 613952 exception::handle: Exception: STATUS_ACCESS_VIOLATION
  10097 [main] itp 613952 open_stackdumpfile: Dumping stack trace to itp.exe.stackdump





Program received signal SIGSEGV, Segmentation fault.
0x61129ab9 in memset () from /usr/bin/cygwin1.dll
(gdb) 
Continuing.
warning: SuspendThread failed. (winerr 6)
[Inferior 1 (process 613952) exited with code 0105400]
(gdb) 

Tracked down the exact line. It fails in a later allocation, when trying to touch some of the new memory.

unsigned mfalloc(mfile *mem,
                 unsigned sz) 
{
    unsigned adr = mem->used;

    if (sz) {
        if (sz + mem->used >= mem->max)
            mem = growmem(mem,sz);
        mem->used += sz; 
        memset(mem->base+adr, 0, sz);  //bzero(mem->base+adr, sz);
        /* bus error with mremap(SHARED,ANON)! */
    }   
    return adr;
}

What am I doing wrong?

PS. I've got the debugger paused right before the error call. If any of the variable values would be useful, I can get them. The mem->fd is 12, so we aren't hitting quite the same SHARED/ANON error that this code has encountered before, but there is some adjustment to be made in the mmap call to avoid this combination.

Edit: It appears to be accessing the old contents of the memory/file with no trouble. It's the new space that isn't usable. Would having an existing open file prevent a larger contiguous map? Hmm. Perhaps I should msync, then munmap, then fdclose, then reopen and mmap. ... That'll require some redesign to test it since the mfile struct will need to store the filename. ...

Was it helpful?

Solution

It appears the Cygwin's mmap won't automatically increase the file size if the size requested for the map is larger. So ftruncate first.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top