Skip to content

PeRvaToOffset

Description

Convert an RVA (offset from ImageBase) to a file offset by finding the section whose [VirtualAddress, VirtualAddress + VirtualSize) contains rva, then adding the in-section delta to the section’s PointerToRawData.

Success

Returns true; *out_offset is set.

Failure

Returns false when no section covers rva or the result would point past BufLength(&self->data).

Usage example (Cross-references)

Usage examples (Cross-references)
    
        u64 dir_offset;
        if (!PeRvaToOffset(ctx->out, (u32)ctx->debug_dir_rva, &dir_offset)) {
            LOG_ERROR("PE: debug directory RVA not in any section");
            return;
    }
    
    bool PeRvaToOffset(const Pe *self, u32 rva, u64 *out_offset) {
        if (!self || !out_offset)
            return false;
        // The CodeView record sits at RVA 0x1020 -> file offset 0x420.
        u64  off = 0;
        bool ok  = PeRvaToOffset(&pe, CV_REC_RVA, &off) && off == CV_REC_RAW_OFF;
    
        // RVA outside any section should fail cleanly.
        // RVA outside any section should fail cleanly.
        u64 garbage = 0;
        ok          = ok && !PeRvaToOffset(&pe, 0xdead0000, &garbage);
    
        PeDeinit(&pe);
        // Inside the section maps to raw_offset + delta.
        u64  off = 0;
        bool ok  = PeRvaToOffset(&pe, SECTION_VA + 0x10, &off) && off == DEBUG_RAW_OFF + 0x10;
        // RVA == VirtualAddress + VirtualSize is one past the end -> not covered.
        u64 end_off = 0;
        // RVA == VirtualAddress + VirtualSize is one past the end -> not covered.
        u64 end_off = 0;
        ok          = ok && !PeRvaToOffset(&pe, SECTION_VA + SECTION_VSIZE, &end_off);
        // RVA below the first section -> not covered.
        u64 low = 0;
        // RVA below the first section -> not covered.
        u64 low = 0;
        ok      = ok && !PeRvaToOffset(&pe, 0x100, &low);
    
        PeDeinit(&pe);
        // RVA 0x2040 is inside section 1 only -> offset 0x300 + 0x40.
        u64  off = 0;
        bool ok  = PeRvaToOffset(&pe, 0x2040, &off) && off == 0x340;
        PeDeinit(&pe);
        DefaultAllocatorDeinit(&alloc);
    
        u64  off = 0;
        bool ok  = PeRvaToOffset(&pe, 0x1000, &off) && off == 0x200;       // start
        ok       = ok && PeRvaToOffset(&pe, 0x10FF, &off) && off == 0x2FF; // last in-range
        u64 tmp  = 0xdead;
        u64  off = 0;
        bool ok  = PeRvaToOffset(&pe, 0x1000, &off) && off == 0x200;       // start
        ok       = ok && PeRvaToOffset(&pe, 0x10FF, &off) && off == 0x2FF; // last in-range
        u64 tmp  = 0xdead;
        ok       = ok && !PeRvaToOffset(&pe, 0x1100, &tmp);                // one past end
        ok       = ok && PeRvaToOffset(&pe, 0x10FF, &off) && off == 0x2FF; // last in-range
        u64 tmp  = 0xdead;
        ok       = ok && !PeRvaToOffset(&pe, 0x1100, &tmp);                // one past end
        ok       = ok && !PeRvaToOffset(&pe, 0x0FFF, &tmp);                // just below start
        u64 tmp  = 0xdead;
        ok       = ok && !PeRvaToOffset(&pe, 0x1100, &tmp);                // one past end
        ok       = ok && !PeRvaToOffset(&pe, 0x0FFF, &tmp);                // just below start
    
        PeDeinit(&pe);
        // rva-va == 0xF -> off == total-1 (last valid byte) -> accepted.
        u64  off = 0;
        bool ok  = PeRvaToOffset(&pe, 0x100F, &off) && off == (u64)total - 1;
        // rva-va == 0x10 -> off == total (one past last) -> rejected.
        u64 past = 0xdead;
        // rva-va == 0x10 -> off == total (one past last) -> rejected.
        u64 past = 0xdead;
        ok       = ok && !PeRvaToOffset(&pe, 0x1010, &past);
    
        PeDeinit(&pe);
        }
        u64  off = 0xdead;
        bool ok  = !PeRvaToOffset(&pe, 0x9000, &off);
        PeDeinit(&pe);
        DefaultAllocatorDeinit(&alloc);
Last updated on