调试下rust 的执行流程
参考:
认识 Cargo - Rust语言圣经(Rust Course)
新建一个hello world 程序:
fn main() {
println!("Hello, world!");
}
用IDA 打开exe,并加载符号:
根据字符串找到主程序入口:
双击该符号,然后按x,快捷键,查看所有的符号引用:
之后跳转到对应的程序位置:
PE 起始地址为140000000
读取"hello,world"字符的指令地址:140001040:
使用windbg ,加载该程序,并在lea 指令的位置下断点:
0:000> kp
# Child-SP RetAddr Call Site
00 000000e6`c38ff9a8 00007ff7`b2571006 hello_world!__ImageBase
01 000000e6`c38ff9b0 00007ff7`b257101c hello_world!__ImageBase
02 000000e6`c38ff9e0 00007ff7`b25736a8 hello_world!__ImageBase
03 (Inline Function) --------`-------- hello_world!std::rt::lang_start_internal::closure$2(void)+0xb [/rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\rt.rs @ 148]
04 (Inline Function) --------`-------- hello_world!std::panicking::try::do_call(void)+0xb [/rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs @ 502]
05 (Inline Function) --------`-------- hello_world!std::panicking::try(void)+0xb [/rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs @ 466]
06 (Inline Function) --------`-------- hello_world!std::panic::catch_unwind(void)+0xb [/rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panic.rs @ 142]
07 000000e6`c38ffa10 00007ff7`b25710ac hello_world!std::rt::lang_start_internal(void)+0xb8 [/rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\rt.rs @ 148]
08 000000e6`c38ffb10 00007ff7`b258a510 hello_world!main+0x2c
09 (Inline Function) --------`-------- hello_world!invoke_main(void)+0x22 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
0a 000000e6`c38ffb50 00007ffc`c2a0257d hello_world!__scrt_common_main_seh(void)+0x10c [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
0b 000000e6`c38ffb90 00007ffc`c39caa78 KERNEL32!BaseThreadInitThunk+0x1d
0c 000000e6`c38ffbc0 00000000`00000000 ntdll!RtlUserThreadStart+0x28
//! Runtime services
//!
//! The `rt` module provides a narrow set of runtime services,
//! including the global heap (exported in `heap`) and unwinding and
//! backtrace support. The APIs in this module are highly unstable,
//! and should be considered as private implementation details for the
//! time being.
#![unstable(
feature = "rt",
reason = "this public module should not exist and is highly likely \
to disappear",
issue = "none"
)]
#![doc(hidden)]
#![deny(unsafe_op_in_unsafe_fn)]
#![allow(unused_macros)]
use crate::ffi::CString;
// Re-export some of our utilities which are expected by other crates.
pub use crate::panicking::{begin_panic, panic_count};
pub use core::panicking::{panic_display, panic_fmt};
use crate::sync::Once;
use crate::sys;
use crate::sys_common::thread_info;
use crate::thread::Thread;
// Prints to the "panic output", depending on the platform this may be:
// - the standard error output
// - some dedicated platform specific output
// - nothing (so this macro is a no-op)
macro_rules! rtprintpanic {
($($t:tt)*) => {
if let Some(mut out) = crate::sys::stdio::panic_output() {
let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*));
}
}
}
macro_rules! rtabort {
($($t:tt)*) => {
{
rtprintpanic!("fatal runtime error: {}\n", format_args!($($t)*));
crate::sys::abort_internal();
}
}
}
macro_rules! rtassert {
($e:expr) => {
if !$e {
rtabort!(concat!("assertion failed: ", stringify!($e)));
}
};
}
macro_rules! rtunwrap {
($ok:ident, $e:expr) => {
match $e {
$ok(v) => v,
ref err => {
let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug
rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
}
}
};
}
// One-time runtime initialization.
// Runs before `main`.
// SAFETY: must be called only once during runtime initialization.
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
//
// # The `sigpipe` parameter
//
// Since 2014, the Rust runtime on Unix has set the `SIGPIPE` handler to
// `SIG_IGN`. Applications have good reasons to want a different behavior
// though, so there is a `#[unix_sigpipe = "..."]` attribute on `fn main()` that
// can be used to select how `SIGPIPE` shall be setup (if changed at all) before
// `fn main()` is called. See <https://github.com/rust-lang/rust/issues/97889>
// for more info.
//
// The `sigpipe` parameter to this function gets its value via the code that
// rustc generates to invoke `fn lang_start()`. The reason we have `sigpipe` for
// all platforms and not only Unix, is because std is not allowed to have `cfg`
// directives as this high level. See the module docs in
// `src/tools/tidy/src/pal.rs` for more info. On all other platforms, `sigpipe`
// has a value, but its value is ignored.
//
// Even though it is an `u8`, it only ever has 4 values. These are documented in
// `compiler/rustc_session/src/config/sigpipe.rs`.
#[cfg_attr(test, allow(dead_code))]
unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
unsafe {
sys::init(argc, argv, sigpipe);
let main_guard = sys::thread::guard::init();
// Next, set up the current Thread with the guard information we just
// created. Note that this isn't necessary in general for new threads,
// but we just do this to name the main thread and to give it correct
// info about the stack bounds.
let thread = Thread::new(Some(rtunwrap!(Ok, CString::new("main"))));
thread_info::set(main_guard, thread);
}
}
// One-time runtime cleanup.
// Runs after `main` or at program exit.
// NOTE: this is not guaranteed to run, for example when the program aborts.
pub(crate) fn cleanup() {
static CLEANUP: Once = Once::new();
CLEANUP.call_once(|| unsafe {
// Flush stdout and disable buffering.
crate::io::cleanup();
// SAFETY: Only called once during runtime cleanup.
sys::cleanup();
});
}
// To reduce the generated code of the new `lang_start`, this function is doing
// the real work.
#[cfg(not(test))]
fn lang_start_internal(
main: &(dyn Fn() -> i32 + Sync + crate::panic::RefUnwindSafe),
argc: isize,
argv: *const *const u8,
sigpipe: u8,
) -> Result<isize, !> {
use crate::{mem, panic};
let rt_abort = move |e| {
mem::forget(e);
rtabort!("initialization or cleanup bug");
};
// Guard against the code called by this function from unwinding outside of the Rust-controlled
// code, which is UB. This is a requirement imposed by a combination of how the
// `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking
// mechanism itself.
//
// There are a couple of instances where unwinding can begin. First is inside of the
// `rt::init`, `rt::cleanup` and similar functions controlled by bstd. In those instances a
// panic is a std implementation bug. A quite likely one too, as there isn't any way to
// prevent std from accidentally introducing a panic to these functions. Another is from
// user code from `main` or, more nefariously, as described in e.g. issue #86030.
// SAFETY: Only called once during runtime initialization.
panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_err(rt_abort)?;
let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
.map_err(move |e| {
mem::forget(e);
rtabort!("drop of the panic payload panicked");
});
panic::catch_unwind(cleanup).map_err(rt_abort)?;
ret_code
}
#[cfg(not(test))]
#[lang = "start"]
fn lang_start<T: crate::process::Termination + 'static>(
main: fn() -> T,
argc: isize,
argv: *const *const u8,
sigpipe: u8,
) -> isize {
let Ok(v) = lang_start_internal(
&move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report().to_i32(),
argc,
argv,
sigpipe,
);
v
}
其核心运行时如上
panic
看起来是利用panic 库进行一些基本的异常捕获与异常处理。
panic! 深入剖析 - Rust语言圣经(Rust Course)
实验:
主动 异常
fn main() {
panic!("crash and burn");
}
PS E:\learn\rust\panic_test> $env:RUST_BACKTRACE="full" ; cargo run release
Compiling panic_test v0.1.0 (E:\learn\rust\panic_test)
Finished dev [unoptimized + debuginfo] target(s) in 0.18s
Running `target\debug\panic_test.exe release`
thread 'main' panicked at src\main.rs:2:5:
crash and burn
stack backtrace:
0: 0x7ff7a439709a - std::sys_common::backtrace::_print::impl$0::fmt
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\sys_common\backtrace.rs:44
1: 0x7ff7a43a52db - core::fmt::rt::Argument::fmt
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\core\src\fmt\rt.rs:138
2: 0x7ff7a43a52db - core::fmt::write
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\core\src\fmt\mod.rs:1094
3: 0x7ff7a43953d1 - std::io::Write::write_fmt<std::sys::windows::stdio::Stderr>
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\io\mod.rs:1714
4: 0x7ff7a4396e1a - std::sys_common::backtrace::_print
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\sys_common\backtrace.rs:47
5: 0x7ff7a4396e1a - std::sys_common::backtrace::print
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\sys_common\backtrace.rs:34
6: 0x7ff7a4398e4a - std::panicking::default_hook::closure$1
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs:270
7: 0x7ff7a4398ab8 - std::panicking::default_hook
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs:290
8: 0x7ff7a43994fe - std::panicking::rust_panic_with_hook
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs:707
9: 0x7ff7a43993aa - std::panicking::begin_panic_handler::closure$0
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs:597
10: 0x7ff7a4397a89 - std::sys_common::backtrace::__rust_end_short_backtrace<std::panicking::begin_panic_handler::closure_env$0,never$>
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\sys_common\backtrace.rs:170
11: 0x7ff7a43990f0 - std::panicking::begin_panic_handler
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs:595
12: 0x7ff7a43aa235 - core::panicking::panic_fmt
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\core\src\panicking.rs:67
13: 0x7ff7a43910a1 - panic_test::main
at E:\learn\rust\panic_test\src\main.rs:2
14: 0x7ff7a439123b - core::ops::function::FnOnce::call_once<void (*)(),tuple$<> >
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33\library\core\src\ops\function.rs:250
15: 0x7ff7a439119e - std::sys_common::backtrace::__rust_begin_short_backtrace<void (*)(),tuple$<> >
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33\library\std\src\sys_common\backtrace.rs:154
16: 0x7ff7a439119e - std::sys_common::backtrace::__rust_begin_short_backtrace<void (*)(),tuple$<> >
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33\library\std\src\sys_common\backtrace.rs:154
17: 0x7ff7a4391061 - std::rt::lang_start::closure$0<tuple$<> >
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33\library\std\src\rt.rs:166
18: 0x7ff7a4393558 - std::rt::lang_start_internal::closure$2
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\rt.rs:148
19: 0x7ff7a4393558 - std::panicking::try::do_call
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs:502
20: 0x7ff7a4393558 - std::panicking::try
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs:466
21: 0x7ff7a4393558 - std::panic::catch_unwind
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panic.rs:142
22: 0x7ff7a4393558 - std::rt::lang_start_internal
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\rt.rs:148
23: 0x7ff7a439103a - std::rt::lang_start<tuple$<> >
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33\library\std\src\rt.rs:165
24: 0x7ff7a43910c9 - main
25: 0x7ff7a43a8c80 - invoke_main
at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
26: 0x7ff7a43a8c80 - __scrt_common_main_seh
at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
27: 0x7ffcc2a0257d - BaseThreadInitThunk
28: 0x7ffcc39caa78 - RtlUserThreadStart
error: process didn't exit successfully: `target\debug\panic_test.exe release` (exit code: 101)
被动 异常
fn main() {
let v = vec![1, 2, 3];
v[99];
}
PS E:\learn\rust\panic_test> $env:RUST_BACKTRACE="full" ; cargo run release
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target\debug\panic_test.exe release`
thread 'main' panicked at src\main.rs:4:6:
index out of bounds: the len is 3 but the index is 99
stack backtrace:
0: 0x7ff75aca794a - std::sys_common::backtrace::_print::impl$0::fmt
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\sys_common\backtrace.rs:44
1: 0x7ff75acb5c1b - core::fmt::rt::Argument::fmt
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\core\src\fmt\rt.rs:138
2: 0x7ff75acb5c1b - core::fmt::write
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\core\src\fmt\mod.rs:1094
3: 0x7ff75aca5c81 - std::io::Write::write_fmt<std::sys::windows::stdio::Stderr>
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\io\mod.rs:1714
4: 0x7ff75aca76ca - std::sys_common::backtrace::_print
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\sys_common\backtrace.rs:47
5: 0x7ff75aca76ca - std::sys_common::backtrace::print
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\sys_common\backtrace.rs:34
6: 0x7ff75aca978a - std::panicking::default_hook::closure$1
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs:270
7: 0x7ff75aca93f8 - std::panicking::default_hook
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs:290
8: 0x7ff75aca9e3e - std::panicking::rust_panic_with_hook
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs:707
9: 0x7ff75aca9d2d - std::panicking::begin_panic_handler::closure$0
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs:599
10: 0x7ff75aca8339 - std::sys_common::backtrace::__rust_end_short_backtrace<std::panicking::begin_panic_handler::closure_env$0,never$>
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\sys_common\backtrace.rs:170
11: 0x7ff75aca9a30 - std::panicking::begin_panic_handler
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs:595
12: 0x7ff75acbab75 - core::panicking::panic_fmt
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\core\src\panicking.rs:67
13: 0x7ff75acbacee - core::panicking::panic_bounds_check
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\core\src\panicking.rs:162
14: 0x7ff75aca1afd - core::slice::index::impl$2::index<i32>
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33\library\core\src\slice\index.rs:261
15: 0x7ff75aca1076 - alloc::vec::impl$12::index<i32,usize,alloc::alloc::Global>
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33\library\alloc\src\vec\mod.rs:2675
16: 0x7ff75aca1366 - panic_test::main
at E:\learn\rust\panic_test\src\main.rs:4
17: 0x7ff75aca14ab - core::ops::function::FnOnce::call_once<void (*)(),tuple$<> >
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33\library\core\src\ops\function.rs:250
18: 0x7ff75aca13de - std::sys_common::backtrace::__rust_begin_short_backtrace<void (*)(),tuple$<> >
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33\library\std\src\sys_common\backtrace.rs:154
19: 0x7ff75aca13de - std::sys_common::backtrace::__rust_begin_short_backtrace<void (*)(),tuple$<> >
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33\library\std\src\sys_common\backtrace.rs:154
20: 0x7ff75aca12e1 - std::rt::lang_start::closure$0<tuple$<> >
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33\library\std\src\rt.rs:166
21: 0x7ff75aca3e08 - std::rt::lang_start_internal::closure$2
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\rt.rs:148
22: 0x7ff75aca3e08 - std::panicking::try::do_call
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs:502
23: 0x7ff75aca3e08 - std::panicking::try
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panicking.rs:466
24: 0x7ff75aca3e08 - std::panic::catch_unwind
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\panic.rs:142
25: 0x7ff75aca3e08 - std::rt::lang_start_internal
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library\std\src\rt.rs:148
26: 0x7ff75aca12ba - std::rt::lang_start<tuple$<> >
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33\library\std\src\rt.rs:165
27: 0x7ff75aca13c9 - main
28: 0x7ff75acb95c0 - invoke_main
at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
29: 0x7ff75acb95c0 - __scrt_common_main_seh
at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
30: 0x7ffcc2a0257d - BaseThreadInitThunk
31: 0x7ffcc39caa78 - RtlUserThreadStart
error: process didn't exit successfully: `target\debug\panic_test.exe release` (exit code: 101)
可以看到,包括我们用windbg 看到的,比较完整的js 运行时的入口都看到了
rust 程序main 入口前,就已经安装了一个默认的panic handler ,用来打印一些全局的错误信息,和堆栈列表。
rt.rs 详解
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [rsp+20h] [rbp-18h]
__int64 (__fastcall *v5)(); // [rsp+30h] [rbp-8h] BYREF
v5 = sub_140001040;
v4 = 0;
return std::rt::lang_start_internal::h8a2184178aa988dc(&v5, &off_14001D360, argc, argv, v4);
}
其中,sub_140001040 即为main 函数:
__int64 sub_140001040()
{
__int64 v1[3]; // [rsp+28h] [rbp-30h] BYREF
__int128 v2; // [rsp+40h] [rbp-18h]
v1[0] = (__int64)&off_14001D3A0;
v1[1] = 1i64;
v1[2] = (__int64)"called `Option::unwrap()` on a `None` value";
v2 = 0i64;
return std::io::stdio::_print::h445fdab5382e0576(v1);
}