I am trying to use the #[link_sections] attribute to inject data into a program after it has been compiled.
This works nicely with programs compiled in debug mode but the section disappears from the executable when it is compiled with the cargo --release flag.
Sample:
use std::ffi::CStr;
#[link_section = ".test_section"]
static TEST_VAR: [u8; 1024] = [0; 1024];
fn main() {
let mut len = 0;
for (idx, value) in TEST_VAR.iter().enumerate() {
if *value == 0 {
len = idx;
break;
}
}
if len > 0 {
match CStr::from_bytes_with_nul(&TEST_VAR[0..=len]) {
Ok(cstr) => {
println!("Injected string: '{}'", &*cstr.to_string_lossy());
}
Err(why) => {
eprintln!("Error: Invalid string found, error: {}", why);
}
}
} else {
eprintln!("No String found");
}
}
Will work as designed in release or debug mode.
When compiled in debug mode I can find the segment with
@2e71828, thanks for your remarks, I have worked with include_bytes but in this case I want to inject stuff after compiling.
I have tried to add #[no_mangle] and #[used] but it makes no difference. I have also played with using #[export_name] with the same effect. The symbols appear only in the debug version.
At least for the #[export_name] variant it looks like the symbols are being stripped together with debug symbols.
It’s been a long time since I did this sort of thing, but you should be able to specify an extern global and use binutils’ objcopy to transform a raw binary file into an ELF object file with one symbol; that can be linked with the Rust application as late as you’d like.
Unfortunately, I don’t remember the objcopy incantation that does this.
It's because the compiler optimized out every access to the static variable since it's initialized with all zero and can nobody change its value at runtime. To prevent such optimization you can replace every access to it with std::ptr::read_volatile.
The used attribute can only be applied to static items. This attribute forces the compiler to keep the variable in the output object file (.o, .rlib, etc. excluding final binaries) even if the variable is not used, or referenced, by any other item in the crate.
This makes it sound like the symbol should survive optimization, but it then goes on to say:
However, the linker is still free to remove such an item.
So, it sounds like #[used] is supposed to keep the symbol alive through optimization until the final link stage, but you might need to pass linker options through rustc to ensure it is included in the final executable.