Open therealprof opened 5 years ago
Did Rust mangling scheme change already? I recall there being an RFC about this. Maybe upgrading rustc_demangle fixes this, but I'm not sure.
No, this is with stable: rustc 1.35.0 (3c235d560 2019-05-20)
There's now support for a new mangling scheme in nightly but it's also not turned on by default yet.
NB: It works in a lot of of cases and fails in a lot of other cases within the same crate with the same tooling.
e.g.:
# cargo asm --rust core::result::unwrap_failed
fn unwrap_failed<E: fmt::Debug>(msg: &str, error: E) -> ! {
sub sp, #56
movs r0, #16
str r0, [sp, #8]
ldr r0, .LCPI5_0
str r0, [sp, #4]
panic!("{}: {:?}", msg, error)
ldr r0, .LCPI5_1
str r0, [sp, #48]
add r0, sp, #52
str r0, [sp, #44]
ldr r0, .LCPI5_2
str r0, [sp, #40]
add r0, sp, #4
str r0, [sp, #36]
movs r0, #2
Arguments { (libcore/fmt/mod.rs:316)
str r0, [sp, #32]
add r1, sp, #36
str r1, [sp, #28]
movs r1, #0
str r1, [sp, #24]
str r1, [sp, #20]
str r0, [sp, #16]
ldr r0, .LCPI5_3
str r0, [sp, #12]
add r0, sp, #12
$crate::panicking::panic_fmt(format_args!($fmt, $($arg)*), (libcore/macros.rs:18)
ldr r1, .LCPI5_4
bl _ZN4core9panicking9panic_fmt17h0d6d5c8b201e3246E
.LCPI5_0:
.LCPI5_1:
.LCPI5_2:
.LCPI5_3:
.LCPI5_4:
You might want to inspect the real assembly generated by rustc (and not the one reported by cargo asm
), and see if something is fishy (I think rustc's assembly output is a "best effort").
Right now it is not possible to pass cargo asm
an assembly text file, and ask it to find some function in that file, but it shouldn't be hard to extend cargo asm
with a --from-file=foo.asm
option. That could allow better testing the tool, by providing assembly snippets via the CLI, and verifying the option. The tests we currently have are end-to-end integration tests, and because the toolchain changes all the time, they are quite brittle.
Ooh. I think we have something here... There're three core::ptr::real_drop_in_place
functions in the final linked binary:
080017b8 <core::ptr::real_drop_in_place>:
80017b8: 4770 bx lr
80017ba: d4d4 bmi.n 8001766 <<cdc_acm::SerialPort<B> as usb_device::class::UsbClass<B>>::get_configuration_descriptors+0x13a>
0800339a <core::ptr::real_drop_in_place>:
800339a: 4770 bx lr
08003fee <core::ptr::real_drop_in_place>:
8003fee: 4770 bx lr
The write_char
function from above looks completely different, though:
08003ff0 <<&mut W as core::fmt::Write>::write_char>:
8003ff0: b510 push {r4, lr}
8003ff2: b082 sub sp, #8
8003ff4: 6800 ldr r0, [r0, #0]
8003ff6: 2200 movs r2, #0
8003ff8: 9201 str r2, [sp, #4]
8003ffa: 297f cmp r1, #127 ; 0x7f
8003ffc: d807 bhi.n 800400e <<&mut W as core::fmt::Write>::write_char+0x1e>
8003ffe: aa01 add r2, sp, #4
8004000: 7011 strb r1, [r2, #0]
8004002: 2201 movs r2, #1
8004004: a901 add r1, sp, #4
8004006: f7ff ffa9 bl 8003f5c <<cortex_m_semihosting::hio::HStderr as core::fmt::Write>::write_str>
800400a: b002 add sp, #8
800400c: bd10 pop {r4, pc}
800400e: 0aca lsrs r2, r1, #11
8004010: d10e bne.n 8004030 <<&mut W as core::fmt::Write>::write_char+0x40>
...
Funny enough I've a few slightly differing unwrap_failed
functions, too but they seem close enough to the cargo-asm
output.
FWIW, cargo-asm
doesn't really inspect the final binary. rustc
generates LLVM-IR, which is then optimized, but we tell LLVM that instead of generating a binary from that LLVM-IR, they should translate it to "textual assembly" instead. I've always been a bit skeptic about how good that "textual assembly" matches the machine code that is actually generated.
The reason we do it this way is that LLVM actually has support for embedding comments in the textual assembly generated, and we wanted to show those too, but rustc doesn't really support that very good right now. The alternative here would be to compile the binary normally, and then disassemble it, and try to look for the function symbols there. Sometimes I wish I would have gone this way.
Not so sure disassembling the final binary is a good approach. Even with "debug info" in the final release binary the information level is really lacking which is one of the reasons I care so much about cargo-asm
. 😅
Yeah, i'm not sure either. More than about the quality of the assembly displayed, I thought it might be an interesting approach to explore to produce something more faithful to what's executed, and also for speed. Compiling and linking a binary, and then doing a disassembly, is for whatever reason much faster than dumping the textual assembly through LLVM.
This issue is constantly getting in my way. I just encountered a function in a library that I cannot seem to get the assembly for, even when wrapping it in a inline(never)
function in a binary. It either gives me code from the main
function or does not find it at all.
edit: I remembered to add pub
to the function and added inline(always)
directly on the function in the library, but trying to chain functions and permutating inline(never)
and inline(always)
is still giving me the wrong function output
even when wrapping it in a inline(never) function in a binary.
I suppose you are then searching for that inline(never)
function, that this function is pub
, and that this function is not generic, right?
I managed to get a reproduction for the stable channel. I am trying to get the assembly from my specialized-div-rem
crate of the u128_div_rem
function. It has #[inline]
applied to it because only one element of the tuple it returns is used sometimes, so I want to be able to see what the inlining does. Sometimes though, the function inlines further than I want, or cargo asm
is acting up and showing the wrong functions even if no inlining is occuring. Usually, I am able to hack around problems like this without having the clone the library and modify it, by doing this:
in Cargo.toml
,
...
[dependencies]
specialized-div-rem = "0.0.4"
rand = "0.6.5"
in main.rs
,
#[inline(never)]
pub fn fun(lkj0: u128, lkj1: u128) -> (u128, u128) {
specialized_div_rem::u128_div_rem(lkj0, lkj1)
}
fn main() {
let lkj0 = 7325176125387951268757865215152512u128;
let lkj1 = 112879451768156781546789154876156715u128;
dbg!(fun(lkj0, lkj1));
}
and running cargo asm [name of binary]::fun
. However, it results in assembly from the wrong function. I can fix it by cloning the library and changing the function to use inline(never)
on the library side, but this is far from convenient or practically impossible if I need to do it when using something that binds to a C++ library. Also, I should mention that I have never once seen --rust
do anything in all my time with cargo asm
.
Another problem I have is that cargo asm [incomplete path]
sometimes does not show a helpful list of potential functions. The master branch of the apint
crate currently has impl
s spread across several modules, so I need to run stuff like cargo asm "apint::apint::data::morestuff::<impl bunch of nontrivial mangling stuff that I cannot figure out without cargo asm mentioning it>::wrapping_add_assign"
. On some libraries like my specialized-div-rem
library, cargo asm
never shows the helpful list, so I may be unable to actually find the function. If it is possible, I would recommend that functions are addressed just how they would be addressed in Rust code, e.g. cargo asm apint::ApInt::wrapping_add_assign
.
It seems to me that cargo-asm
is something that belongs in the nightly Rust compiler itself. I understand that cargo-asm
relying on optimized compiler output is always going to need fragile hacks if it were able to be "ergonomic". Either way, I suppose we are all waiting on stabilized mangling and internal compiler refactors.
I haven't quite figured out what triggers the erratic behaviour but for some functions I'd get something like:
or
In both cases
cargo-asm
seems to have picked up the incorrect function and to demangle the symbols.