There are two reasons.
First, the value is an offset into the shared memory location, not an absolute physical address. So it will have to be within the range of the size of the shared memory. This is intentional because different hardware might use different addresses and the sdk is shielding you from it.
Second is due to where the linker script places the code and data of the executable. By default it reserves the first 16MB of the 32MB block for "shared" code, hence you use an offset of 16MB to avoid that (i.e. 0x1000000). But you can change the linker scripts to put this stuff elsewhere, or just define your executables in a way that they don't put any code or data there. Or use a completely different mechanism of just defining global variables and resolving their address properly.