“str”值分配在哪里?是在堆里吗?

Den*_*ive 3 gdb heap-memory rust

我是 Rust 新手,我试图弄清楚东西分配在哪里。

1.64.0我在 Ubuntu 上使用 Rust 22.04.0( jammy):

$ rustc --version
rustc 1.64.0 (a55dd71d5 2022-09-19)
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:    22.04
Codename:   jammy
Run Code Online (Sandbox Code Playgroud)

我尝试通过编译这个非常基本的 Rust 程序来探索可执行文件:

fn tester() {
    let borrowed_string: &str = "world";
    println!("{}", borrowed_string);
}

fn main() { tester(); }
Run Code Online (Sandbox Code Playgroud)

编译(好吧,这有点矫枉过正了......但我想确保我拥有使用 GDB 所需的一切):

$ cargo build --config "profile.dev.debug=true" --config "profile.dev.opt-level=0" --profile=dev
  Compiling variables v0.1.0 (/home/denis/Documents/github/rust-playground/variables)
   Finished dev [unoptimized + debuginfo] target(s) in 0.71s
Run Code Online (Sandbox Code Playgroud)

Run rust-gdb,设置断点并运行程序:

$ rust-gdb target/debug/variables 
GNU gdb (Ubuntu 12.0.90-0ubuntu1) 12.0.90
...
Reading symbols from target/debug/variables...
(gdb) b 3
Breakpoint 1 at 0x7959: file src/main.rs, line 3.
(gdb) r
Starting program: /home/denis/Documents/github/rust-playground/variables/target/debug/variables 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, variables::tester () at src/main.rs:3
3       println!("{}", borrowed_string);
(gdb) 
s
Run Code Online (Sandbox Code Playgroud)

打印内存映射;

(gdb) info proc map
process 6985
Mapped address spaces:

          Start Addr           End Addr       Size     Offset  Perms  objfile
      0x555555554000     0x55555555a000     0x6000        0x0  r--p   /home/denis/Documents/github/rust-playground/variables/target/debug/variables
      0x55555555a000     0x555555591000    0x37000     0x6000  r-xp   /home/denis/Documents/github/rust-playground/variables/target/debug/variables
      0x555555591000     0x55555559f000     0xe000    0x3d000  r--p   /home/denis/Documents/github/rust-playground/variables/target/debug/variables
      0x5555555a0000     0x5555555a3000     0x3000    0x4b000  r--p   /home/denis/Documents/github/rust-playground/variables/target/debug/variables
      0x5555555a3000     0x5555555a4000     0x1000    0x4e000  rw-p   /home/denis/Documents/github/rust-playground/variables/target/debug/variables
      0x5555555a4000     0x5555555c5000    0x21000        0x0  rw-p   [heap]
      0x7ffff7d5c000     0x7ffff7d5f000     0x3000        0x0  rw-p   
      0x7ffff7d5f000     0x7ffff7d87000    0x28000        0x0  r--p   /usr/lib/x86_64-linux-gnu/libc.so.6
      0x7ffff7d87000     0x7ffff7f1c000   0x195000    0x28000  r-xp   /usr/lib/x86_64-linux-gnu/libc.so.6
      0x7ffff7f1c000     0x7ffff7f74000    0x58000   0x1bd000  r--p   /usr/lib/x86_64-linux-gnu/libc.so.6
      0x7ffff7f74000     0x7ffff7f78000     0x4000   0x214000  r--p   /usr/lib/x86_64-linux-gnu/libc.so.6
      0x7ffff7f78000     0x7ffff7f7a000     0x2000   0x218000  rw-p   /usr/lib/x86_64-linux-gnu/libc.so.6
      0x7ffff7f7a000     0x7ffff7f87000     0xd000        0x0  rw-p   
      0x7ffff7f87000     0x7ffff7f8a000     0x3000        0x0  r--p   /usr/lib/x86_64-linux-gnu/libgcc_s.so.1
      0x7ffff7f8a000     0x7ffff7fa1000    0x17000     0x3000  r-xp   /usr/lib/x86_64-linux-gnu/libgcc_s.so.1
      0x7ffff7fa1000     0x7ffff7fa5000     0x4000    0x1a000  r--p   /usr/lib/x86_64-linux-gnu/libgcc_s.so.1
      0x7ffff7fa5000     0x7ffff7fa6000     0x1000    0x1d000  r--p   /usr/lib/x86_64-linux-gnu/libgcc_s.so.1
      0x7ffff7fa6000     0x7ffff7fa7000     0x1000    0x1e000  rw-p   /usr/lib/x86_64-linux-gnu/libgcc_s.so.1
      0x7ffff7fb8000     0x7ffff7fb9000     0x1000        0x0  ---p   
      0x7ffff7fb9000     0x7ffff7fbd000     0x4000        0x0  rw-p   
      0x7ffff7fbd000     0x7ffff7fc1000     0x4000        0x0  r--p   [vvar]
      0x7ffff7fc1000     0x7ffff7fc3000     0x2000        0x0  r-xp   [vdso]
      0x7ffff7fc3000     0x7ffff7fc5000     0x2000        0x0  r--p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      0x7ffff7fc5000     0x7ffff7fef000    0x2a000     0x2000  r-xp   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      0x7ffff7fef000     0x7ffff7ffa000     0xb000    0x2c000  r--p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      0x7ffff7ffb000     0x7ffff7ffd000     0x2000    0x37000  r--p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      0x7ffff7ffd000     0x7ffff7fff000     0x2000    0x39000  rw-p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0  rw-p   [stack]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0  --xp   [vsyscall]
(gdb) 
Run Code Online (Sandbox Code Playgroud)

我们有:

HEAP:  [0x5555555A4000, 0x5555555C4FFF]
STACK: [0x7FFFFFFDE000, 0x7FFFFFFFEFFF]
Run Code Online (Sandbox Code Playgroud)

我假设数据“世界”是在堆中分配的。因此,让我们找出答案......但看看我得到了什么!

(gdb) find 0x5555555A4000, 0x5555555C4FFF, "world"
0x5555555a4ba0
1 pattern found.
(gdb) find 0x5555555A4000, 0x5555555C4FFF, "world"
0x5555555a4be0
1 pattern found.
(gdb) find 0x5555555A4000, 0x5555555C4FFF, "world"
0x5555555a4c20
1 pattern found.
(gdb) find 0x5555555A4000, 0x5555555C4FFF, "world"
0x5555555a4c60
1 pattern found.
(gdb) find 0x5555555A4000, 0x5555555C4FFF, "world"
0x5555555a4ca0
1 pattern found.
(gdb) 
Run Code Online (Sandbox Code Playgroud)

请注意:

0x5555555A4BA0 -> 0x5555555A4BE0 => 64
0x5555555A4BE0 -> 0x5555555A4C20 => 64
0x5555555A4C20 -> 0x5555555A4C60 => 64
Run Code Online (Sandbox Code Playgroud)

问题:为什么数据的位置从一次扫描到下一次扫描会发生变化?

(这个问题可能与 Rust 无关。)

(gdb) p borrowed_string
$1 = "world"
(gdb) ptype borrowed_string
type = struct &str {
  data_ptr: *mut u8,
  length: usize,
}
(gdb) p borrowed_string.data_ptr
$2 = (*mut u8) 0x555555591000
(gdb) x/5c 0x555555591000
0x555555591000: 119 'w' 111 'o' 114 'r' 108 'l' 100 'd'
Run Code Online (Sandbox Code Playgroud)

我们有:

  • 0x5555555A4000:堆的开始(包含)
  • 0x5555555C5000:堆的末尾(不包括),以及“未知”内存区域的开始
  • 0x555555591000:数据“world”的地址
  • 0x7FFFF7D5F000:“未知”内存领域的终结

问题:0x5555555C5000(包含)和 0x7FFFF7D5F000(排除)之间的“未知”内存区域是什么?

编辑

正如下面的评论中提到的,我犯了一个错误。数据“世界”位于堆“之前”......这些评论是有道理的。谢谢。

Mas*_*inn 7

问题:0x5555555C5000(包含)和 0x7FFFF7D5F000(排除)之间的“未知”内存区域是什么?

没什么。这是未映射的内存。

“str”值分配在哪里?是在堆里吗?

你的内存映射回答了这个问题:

0x555555591000     0x55555559f000     0xe000    0x3d000  r--p   /home/denis/Documents/github/rust-playground/variables/target/debug/variables
Run Code Online (Sandbox Code Playgroud)

这是二进制文件本身被映射的区域之一。最有可能的是可执行文件的“rodata”段。字符串文字通常在二进制文件本身中“分配”,然后加载静态引用。

更一般地说str,值不会被分配,它们是对其他地方的数据的引用,这些数据可能位于堆上(如果引用是从一个StringBox<str>多个指针获得的),在二进制文件中(文字,静态包含),甚至是在堆栈(例如,您可以在堆栈上创建一个数组,然后将其传递std::str::from_utf8&str从中取出)

我们有:堆:[0x5555555A4000,0x55​​55555C4FFF]

坦率地说,这并不是真正有帮助、有用或真实的:现代系统已经很大程度上从线性堆转向内存映射堆。虽然 Linux 软件仍然对 (s)brk 进行一些有限的使用,但 BSD 将它们称为“历史珍品”。如果您假设堆包含在这些范围内,您将面临很多意外。

问题:为什么每次扫描数据的位置会发生变化?

因为调试器正在程序的上下文中分配东西,我想?所以您可能会发现 gdb 本身正在存储的数据。