It has to do with how the sections are loaded into memory versus how they are laid out in the file. For locations within the same section, the in-memory distance versus the in-file distance will be the same. For locations in different sections, the file format might save space by not representing bytes at the end of the section that align it to a multiple of the page size. Generally speaking, the simplest way to deal with issues like these is to use tools that are aware of such things. For example, try loading the .so in IDA or Ghidra.
In the case of a Windows PE file, the sections have two different "alignment values": the "file alignment", and the "section alignment". The size of every section in the raw .EXE must be a multiple of the file alignment, which is commonly 0x200, or the size of one disk sector. That is to say, if the actual size of the contents of the section are 0x3FF, then there are 0x1FF bytes in the last chunk, and we need 0x001 extra byte to round it up to the file alignment. If the actual size of the section is 0x402 bytes, then there are 0x002 bytes in the last chunk, and we need 0x1FE more bytes to round it up to be a multiple of 0x200.
On the other hand, the section alignment dictates how big sections are when they are loaded into memory. This is commonly 0x1000, or the size of one memory page. So if the disk size of a section is 0xFFE, we need 0x002 bytes to round it up to a multiple of 0x1000. If the disk size of a section is 0x1003, we need 0xFFD extra bytes to round it up to a multiple of 0x1000.
For a complete example:
- Let's say section #1 is 0x200 bytes long, and begins at address 0x600 in the file. Supposing the imagebase is 0x400000, the section will be loaded at address 0x401000 in memory.
- Let's say that section #2 is 0x200 bytes long, begins at address 0x800 in the file, and is loaded at address 0x402000 in memory.
- Let's say that byte 0x0 in section #1 contains a reference to data at byte 0x0 in section #2. In terms of distance within the file, the distance is 0x800-0x600, or 0x200. In terms of distance in memory, the distance will be 0x402000-0x401000, or 0x1000.
How do automated tools deal with this? By being aware of how the sections in the file will be loaded into memory. In the case of a Windows PE file, an automated tool would need to parse the section headers to produce a memory map of where the sections would be loaded in memory. Then, to resolve a RIP-relative displacement
- Add 0xABCD to the in-memory address of the source of the reference
- Determine in which section the target address lies
- Determine the relative offset of the target address within its containing section (by subtracting the target address by the section begin address)
- Map this back to a file offset by adding the relative offset to the beginning of the targeted section within the file.