From C, or the linker, or something outside Rust, I receive two items. Call them _data
and _edata
.
extern {
static mut _data: u32;
static mut _edata: u32;
}
The two symbols will be mapped to addresses by the linker, and those addresses may be the same. Not every build, but sometimes.
rustc
seems to perform optimizations under the assumption that the two items occupy different addresses, when they are defined as above. In particular, a loop like the following:
let mut src: *const u32 = &_data_load;
let mut dst: *mut u32 = &mut _data;
while dst != &mut _edata {
*dst = *src;
dst = dst.offset(1);
src = src.offset(1);
}
...gets compiled into:
8000058: f851 3b04 ldr.w r3, [r1], #4
800005c: f840 3b04 str.w r3, [r0], #4
8000060: 4282 cmp r2, r0
8000062: d1f9 bne.n 8000058
...meaning that the initial while loop test is assumed constant. If _data
and _edata
have the same address this code misbehaves. Good optimization in general, but incorrect for this case.
I can fix the compiler's output for this loop by switching !=
to <
, but that's just avoiding the issue.
I understand that having two objects that may or may not overlap makes a whole bunch of things potentially unsafe. I am doing something rather unsafe. So assuming I'm already in this situation, is there a different way I can declare the extern
s to tell the compiler that they are potentially aliased, to avoid this optimization biting me in a later build?
Note: I'm writing the equivalent of crt0.o
. Other people I've seen do this, e.g. in Zinc, put dummy data between _data
and _edata
to force them not to alias.