A signed 32 bit "offset" value gives you 2GB in each direction, not 4GB in each direction.
Only if you use byte aligned relative pointers. For data structures you don't need better than 32-bit aligned pointers. So you get +- 8GB relative offsets. It's not all that unreasonable to use 64-bit or even 128-bit alignment, for +-16 GB and +-32 GB respectively.
A library that can do that in Rust would be great.