Search code examples
c++memorymmap

ntohl() returning 0 when reading from mmap()


Good evening, I am attempting to read some binary information from a .img file. I can retrieve 16-bit numbers (uint16_t) from ntohs(), but when I try to retrieve from the same position using ntohl(), it gives me 0 instead.

Here are the critical pieces of my program.

#include <iostream>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <arpa/inet.h>
#include <cmath>

int fd;
struct blockInfo {
    long blockSize = 0;
    long blockCount = 0;
    long fatStart = 0;
    long fatBlocks = 0;
    long rootStart = 0;
    long rootBlocks = 0;
    long freeBlocks = 0;
    long resBlocks = 0;
    long alloBlocks = 0;
};


int main(int argc, char *argv[]) {

   fd = open(argv[1], O_RDWR);

   // Get file size
   struct stat buf{};
   stat(path, &buf);
   size_t size = buf.st_size;

   // A struct to hold data retrieved from a big endian image.
   blockInfo info;

   auto mapPointer = (char*) mmap(nullptr, size,
                     (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0);

   info.blockSize = ntohs((uint16_t) mapPointer[12]);
   long anotherBlockSize = ntohl((uint32_t) mapPointer[11]);

   printf("%ld", info.blockSize); // == 512, correct
   printf("%ld", anotherBlockSize); // == 0, what?
}

I understand that blockSize and anotherBlockSize are not supposed to be equal, but anotherBlockSize should be non-zero at the least, right?

Something else, I go to access data at ntohs(pointer[16]), which should return 2, but also returns 0. What is going on here? Any help would be appreciated.


Solution

  • No, anotherBlockSize will not necessarily be non-zero

    info.blockSize = ntohs((uint16_t) mapPointer[12]);
    

    This code reads a char from offset 12 relatively to mapPointer, casts it to uint16_t and applies ntohs() to it.

    long anotherBlockSize = ntohl((uint32_t) mapPointer[11]);
    

    This code reads a char from offset 11 relatively to mapPointer, casts it to uint32_t and applies ntohl() to it.

    Obviously, you are reading non-overlapped data (different chars) from the mapped memory, so you should not expect blockSize and anotherBlockSize to be connected.

    If you are trying to read the same memory in different ways (as uint32_t and uint16_t), you must do some pointer casting:

    info.blockSize = ntohs( *((uint16_t*)&mapPointer[12]));
    

    Note that such code will generally be platform dependent. Such cast working perfectly on x86 may fail on ARM.