Skip to content

ProcMapEntry

Description

One line of /proc/self/maps. path is borrowed from the ProcMaps.raw buffer and stays valid until ProcMapsDeinit. May be empty for anonymous mappings (heap, stacks, vdso, etc.).

Usage example (Cross-references)

Usage examples (Cross-references)
        u64  file_offset; // offset within the backing file
        Zstr path;        // backing file path, or "" if anonymous
    } ProcMapEntry;
    
    typedef Vec(ProcMapEntry) ProcMapEntries;
    } ProcMapEntry;
    
    typedef Vec(ProcMapEntry) ProcMapEntries;
    
    typedef struct ProcMaps {
    /// TAGS: Sys, ProcMaps, Find, Lookup
    ///
    const ProcMapEntry *ProcMapsFindByAddr(const ProcMaps *self, u64 addr);
    
    #endif // MISRA_SYS_PROC_MAPS_H
    
        u64                 addr  = (u64)runtime_addr;
        const ProcMapEntry *entry = ProcMapsFindByAddr(&self->maps, addr);
        if (!entry || !entry->path || entry->path[0] == '\0')
            return false;
        u64 addr = (u64)runtime_addr;
    
        const ProcMapEntry *entry = ProcMapsFindByAddr(&self->maps, addr);
        if (!entry || !entry->path || entry->path[0] == '\0') {
            return false;
    // advanced past the line terminator so the next call resumes at the
    // next line.
    static bool parse_one_line(StrIter *si, ProcMapEntry *out) {
        u64 start = 0, ende = 0, offset = 0;
        if (!parse_hex_u64(si, &start))
        StrIter si = StrIterFromStr(out->raw);
        while (StrIterRemainingLength(&si)) {
            ProcMapEntry e = {0};
            if (!parse_one_line(&si, &e)) {
                // Skip past whatever line we couldn't parse.
        // Cache the lowest mapped address so callers don't rescan the vector.
        for (u64 i = 0; i < VecLen(&out->entries); ++i) {
            const ProcMapEntry *e = VecPtrAt(&out->entries, i);
            if (i == 0 || e->start < out->min_addr)
                out->min_addr = e->start;
    }
    
    const ProcMapEntry *ProcMapsFindByAddr(const ProcMaps *self, u64 addr) {
        if (!self)
            return NULL;
            return NULL;
        for (u64 i = 0; i < VecLen(&self->entries); ++i) {
            const ProcMapEntry *e = VecPtrAt(&self->entries, i);
            if (addr >= e->start && addr < e->end) {
                return e;
        if (ProcMapsLoad(&maps, ALLOCATOR_OF(&a))) {
            for (u64 i = 0; i < VecLen(&maps.entries); ++i) {
                const ProcMapEntry *m = VecPtrAt(&maps.entries, i);
                if (m->path && m->path[0] == '/' && (m->perms & PROC_MAP_PERM_EXEC) &&
                    ZstrCompare(m->path, self_path) != 0) {
        pm.entries = VecInitT(pm.entries, ALLOCATOR_OF(&alloc));
    
        ProcMapEntry e0 = {.start = 0x1000, .end = 0x2000, .perms = 0, .file_offset = 0, .path = ""};
        ProcMapEntry e1 = {.start = 0x3000, .end = 0x4000, .perms = 0, .file_offset = 0, .path = ""};
        ProcMapEntry e2 = {.start = 0x5000, .end = 0x6000, .perms = 0, .file_offset = 0, .path = ""};
    
        ProcMapEntry e0 = {.start = 0x1000, .end = 0x2000, .perms = 0, .file_offset = 0, .path = ""};
        ProcMapEntry e1 = {.start = 0x3000, .end = 0x4000, .perms = 0, .file_offset = 0, .path = ""};
        ProcMapEntry e2 = {.start = 0x5000, .end = 0x6000, .perms = 0, .file_offset = 0, .path = ""};
        ProcMapEntry e0 = {.start = 0x1000, .end = 0x2000, .perms = 0, .file_offset = 0, .path = ""};
        ProcMapEntry e1 = {.start = 0x3000, .end = 0x4000, .perms = 0, .file_offset = 0, .path = ""};
        ProcMapEntry e2 = {.start = 0x5000, .end = 0x6000, .perms = 0, .file_offset = 0, .path = ""};
    
        bool pushed = VecPushBackR(&pm.entries, e0) && VecPushBackR(&pm.entries, e1) && VecPushBackR(&pm.entries, e2);
    
        // 0x5500 is inside the stale e2 range only — must be unreachable.
        const ProcMapEntry *hit = ProcMapsFindByAddr(&pm, 0x5500);
        bool                ok  = (hit == NULL);
    
        // Sanity: live entries are still found at the right slots.
        const ProcMapEntry *h0 = ProcMapsFindByAddr(&pm, 0x1500);
        const ProcMapEntry *h1 = ProcMapsFindByAddr(&pm, 0x3500);
        ok                     = ok && h0 != NULL && h0->start == 0x1000 && h1 != NULL && h1->start == 0x3000;
        // Sanity: live entries are still found at the right slots.
        const ProcMapEntry *h0 = ProcMapsFindByAddr(&pm, 0x1500);
        const ProcMapEntry *h1 = ProcMapsFindByAddr(&pm, 0x3500);
        ok                     = ok && h0 != NULL && h0->start == 0x1000 && h1 != NULL && h1->start == 0x3000;
        u64          n  = copy_line(buf, sizeof(buf), "1000-2000 r-xp 0000dead 08:01 12345 /x/y\n");
        StrIter      si = StrIterFromCstr(buf, n); // includes trailing '\n'
        ProcMapEntry e  = {0};
        bool         ok = parse_one_line(&si, &e);
        if (!ok)
        u64          n  = copy_line(buf, sizeof(buf), "0-1 rw-s 0 0:0 0 \n");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e  = {0};
        if (!parse_one_line(&si, &e))
            return false;
        u64          n  = copy_line(buf, sizeof(buf), "0-1 ---p 0 0:0 0 \n");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e  = {0};
        if (!parse_one_line(&si, &e))
            return false;
        u64          n  = copy_line(buf, sizeof(buf), "0-1 r--s 0 0:0 0 \n");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e  = {0};
        if (!parse_one_line(&si, &e))
            return false;
        u64          n  = copy_line(buf, sizeof(buf), "0-1 -w-s 0 0:0 0 \n");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e  = {0};
        if (!parse_one_line(&si, &e))
            return false;
        u64          n  = copy_line(buf, sizeof(buf), "0-1 --xs 0 0:0 0 \n");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e  = {0};
        if (!parse_one_line(&si, &e))
            return false;
        u64          n  = copy_line(buf, sizeof(buf), "0-1 ---p 0 0:0 0 \n");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e  = {0};
        if (!parse_one_line(&si, &e))
            return false;
        u64          n  = copy_line(buf, sizeof(buf), "0-1 ---p 0 0:0 0 \n");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e  = {0};
        if (!parse_one_line(&si, &e))
            return false;
        u64          n  = copy_line(buf, sizeof(buf), "0-1 ---p 0 0:0 0 [stack]\n");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e  = {0};
        if (!parse_one_line(&si, &e))
            return false;
        u64          n  = copy_line(buf, sizeof(buf), "0-1 ---p 0 0:0 0 /a b/c\n");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e  = {0};
        if (!parse_one_line(&si, &e))
            return false;
        u64          n = copy_line(buf, sizeof(buf), "7f8b8c000000-7f8b8c021000 r-xp 00001000 08:01 1234 /lib/libc.so.6\n");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e  = {0};
        if (!parse_one_line(&si, &e))
            return false;
        u64          n  = copy_line(buf, sizeof(buf), "1000 2000 r-xp 0 0:0 0 \n");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e  = {0};
        return !parse_one_line(&si, &e);
    }
        u64          n  = copy_line(buf, sizeof(buf), "xyz-2000 r-xp 0 0:0 0 \n");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e  = {0};
        return !parse_one_line(&si, &e);
    }
        u64          n  = copy_line(buf, sizeof(buf), "1000-2000 rw");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e  = {0};
        return !parse_one_line(&si, &e);
    }
        u64          n  = copy_line(buf, sizeof(buf), "1000-2000 r-xp dead 08:01 99 /z");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e  = {0};
        if (!parse_one_line(&si, &e))
            return false;
        u64          n  = copy_line(buf, sizeof(buf), "1000-2000 r-xp 0 0:0 0 /a\n3000-4000 rw-p 0 0:0 0 /b\n");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e1 = {0};
        ProcMapEntry e2 = {0};
        if (!parse_one_line(&si, &e1))
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e1 = {0};
        ProcMapEntry e2 = {0};
        if (!parse_one_line(&si, &e1))
            return false;
        u64          n  = copy_line(buf, sizeof(buf), "1000-2000 r-xp 0 0:0 0 /first\n3000-4000 rw-p 0 0:0 0 /second\n");
        StrIter      si = StrIterFromCstr(buf, n);
        ProcMapEntry e1 = {0};
        if (!parse_one_line(&si, &e1))
            return false;
        // mapping of the test binary itself.
        u64                 self_addr = (u64)&test_procmaps_find_self;
        const ProcMapEntry *entry     = ProcMapsFindByAddr(&maps, self_addr);
    
        bool ok = entry != NULL && (entry->perms & PROC_MAP_PERM_EXEC) != 0;
    
        u64                 fn_addr = (u64)&pm2_marker_fn;
        const ProcMapEntry *e       = ProcMapsFindByAddr(&maps, fn_addr);
    
        bool ok = e != NULL;
    
        u64                 stack_addr = (u64)&local;
        const ProcMapEntry *e          = ProcMapsFindByAddr(&maps, stack_addr);
    
        bool ok = e != NULL;
        if (ok) {
            // Pick a non-degenerate region and query its exact start.
            const ProcMapEntry *region = NULL;
            for (u64 i = 0; i < VecLen(&maps.entries); ++i) {
                const ProcMapEntry *cand = VecPtrAt(&maps.entries, i);
            const ProcMapEntry *region = NULL;
            for (u64 i = 0; i < VecLen(&maps.entries); ++i) {
                const ProcMapEntry *cand = VecPtrAt(&maps.entries, i);
                if (cand->end > cand->start) {
                    region = cand;
            if (ok) {
                u64                 start = region->start;
                const ProcMapEntry *hit   = ProcMapsFindByAddr(&maps, start);
                // The start address must resolve, and to a region whose
                // start is exactly that address.
        if (ok) {
            // The entry with the highest `end` -- `end` itself maps to nothing.
            const ProcMapEntry *top = VecPtrAt(&maps.entries, 0);
            for (u64 i = 1; i < VecLen(&maps.entries); ++i) {
                const ProcMapEntry *cand = VecPtrAt(&maps.entries, i);
            const ProcMapEntry *top = VecPtrAt(&maps.entries, 0);
            for (u64 i = 1; i < VecLen(&maps.entries); ++i) {
                const ProcMapEntry *cand = VecPtrAt(&maps.entries, i);
                if (cand->end > top->end)
                    top = cand;
            // end - 1 is the last byte inside the region: must resolve to it.
            if (ok) {
                const ProcMapEntry *last_in = ProcMapsFindByAddr(&maps, top->end - 1);
                ok                          = last_in != NULL && last_in->end == top->end;
            }
            // end is one-past the top region and above all mappings: NULL.
            if (ok) {
                const ProcMapEntry *past = ProcMapsFindByAddr(&maps, top->end);
                ok                       = past == NULL;
            }
    
        // Address 0 / a tiny address is never mapped in a normal process.
        const ProcMapEntry *e = ProcMapsFindByAddr(&maps, (u64)0x1);
    
        bool ok = e == NULL;
            bool inside = false;
            for (u64 j = 0; j < VecLen(&maps.entries); ++j) {
                const ProcMapEntry *e = VecPtrAt(&maps.entries, j);
                if (hole >= e->start && hole < e->end) {
                    inside = true;
            if (!inside) {
                found_gap                  = true;
                const ProcMapEntry *at_gap = ProcMapsFindByAddr(&maps, hole);
                ok                         = at_gap == NULL;
            }
        // definitely-unmapped high canonical-hole address.
        if (!found_gap) {
            const ProcMapEntry *at = ProcMapsFindByAddr(&maps, (u64)0x0000800000000000ULL);
            ok                     = at == NULL;
        }
Last updated on