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) {
        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 && ZstrCompare(m->path, path) == 0 && (m->perms & PROC_MAP_PERM_WRITE) && addr >= m->start &&
                    addr < m->end) {
        // 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;
        }
        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;
        bool ok = VecLen(&m.entries) == 1;
        if (ok) {
            const ProcMapEntry *e = VecPtrAt(&m.entries, 0);
            ok                    = e->start == 0x1000ULL && e->end == 0x2000ULL && e->file_offset == 0xdeadULL &&
                 e->perms == (u32)(PROC_MAP_PERM_READ | PROC_MAP_PERM_EXEC | PROC_MAP_PERM_PRIVATE) &&
        bool ok = VecLen(&m.entries) == 1;
        if (ok) {
            const ProcMapEntry *e = VecPtrAt(&m.entries, 0);
            ok                    = e->start == 0xABCDEFULL && e->end == 0xFEDCBAULL && e->file_offset == 0xCA00ULL;
        }
        bool ok = VecLen(&m.entries) == 1;
        if (ok) {
            const ProcMapEntry *e = VecPtrAt(&m.entries, 0);
            ok                    = e->start == 0x9ULL && e->end == 0x1990ULL;
        }
        bool ok = VecLen(&m.entries) == 2;
        if (ok) {
            const ProcMapEntry *e0 = VecPtrAt(&m.entries, 0);
            const ProcMapEntry *e1 = VecPtrAt(&m.entries, 1);
            ok                     = e0->start == 0x5000ULL && e1->start == 0x1000ULL && m.min_addr == 0x1000ULL;
        if (ok) {
            const ProcMapEntry *e0 = VecPtrAt(&m.entries, 0);
            const ProcMapEntry *e1 = VecPtrAt(&m.entries, 1);
            ok                     = e0->start == 0x5000ULL && e1->start == 0x1000ULL && m.min_addr == 0x1000ULL;
        }
        bool ok = VecLen(&m.entries) == 1;
        if (ok) {
            const ProcMapEntry *e = VecPtrAt(&m.entries, 0);
            ok                    = e->start == 0x3000ULL && e->end == 0x4000ULL && ZstrCompare(e->path, "/good") == 0;
        }
            // The trailing high mapping lives past the first read chunk; a loader
            // that stopped early would never have parsed it.
            const ProcMapEntry *e = ProcMapsFindByAddr(&m, 0xdeadbeef050ULL);
            ok                    = e != NULL && e->start == 0xdeadbeef000ULL && e->end == 0xdeadbeef100ULL &&
                 ZstrCompare(e->path, "/late") == 0;
Last updated on