Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Box

Source

Initialize a new workspace with cargo init --lib.

#![allow(unused)]
fn main() {
#[unsafe(no_mangle)]
pub fn create_boxed_value(n: i32) -> Box<i32> {
    Box::new(n * 2)
}

#[unsafe(no_mangle)]
pub fn process_box(boxed: Box<i32>) -> i32 {
    *boxed + 10
}
}

The Box type is described in the official docs in detail. Further information can be found here. We are using no_mangle to simplify things, the reason can be found here.

Build

$ cargo rustc --release -- --emit obj

llvm-objdump

For this example we will use llvm-objdump instead of Ghidra, as Ghidra does not support the relocation type R_AARCH64_ADR_GOT_PAGE.

Alternatively, the compiler can generate assembly output as well, although it is a bit less readable because of all the .cfi_* directives:

$ cargo rustc --release -- --emit asm
$ cat target/aarch64-unknown-linux-musl/release/deps/rust_lab-15e16dbcf37b825b.s
...
create_boxed_value:
	.cfi_startproc
	stp	x29, x30, [sp, #-32]!
	.cfi_def_cfa_offset 32
	str	x19, [sp, #16]
	mov	x29, sp
	.cfi_def_cfa w29, 32
	.cfi_offset w19, -16
	.cfi_offset w30, -24
	.cfi_offset w29, -32
	.cfi_remember_state
	adrp	x8, :got:__rust_no_alloc_shim_is_unstable
	mov	w19, w0
	mov	w0, #4
	ldr	x8, [x8, :got_lo12:__rust_no_alloc_shim_is_unstable]
	mov	w1, #4
	ldrb	wzr, [x8]
	bl	_RNvCshIQntqZdYTC_7___rustc12___rust_alloc
	cbz	x0, .LBB0_2
	lsl	w8, w19, #1
	str	w8, [x0]
	.cfi_def_cfa wsp, 32
	ldr	x19, [sp, #16]
	ldp	x29, x30, [sp], #32
	.cfi_def_cfa_offset 0
	.cfi_restore w19
	.cfi_restore w30
	.cfi_restore w29
	ret
.LBB0_2:
	.cfi_restore_state
	mov	w0, #4
	mov	w1, #4
	bl	_ZN5alloc5alloc18handle_alloc_error17h0e34a69f1cc3072eE
...

create_boxed_value

#![allow(unused)]
fn main() {
#[unsafe(no_mangle)]
pub fn create_boxed_value(n: i32) -> Box<i32> {
    Box::new(n * 2)
}
}

Disassembly (the output is piped through rustfilt to demangle symbols):

$ llvm-objdump -r --disassemble-symbols=create_boxed_value target/aarch64-unknown-linux-musl/release/deps/rust_lab-15e16dbcf37b825b.o | rustfilt 

target/aarch64-unknown-linux-musl/release/deps/rust_lab-15e16dbcf37b825b.o:	file format elf64-littleaarch64

Disassembly of section .text.create_boxed_value:

0000000000000000 <create_boxed_value>:
       0: a9be7bfd     	stp	x29, x30, [sp, #-0x20]!
       4: f9000bf3     	str	x19, [sp, #0x10]
       8: 910003fd     	mov	x29, sp
       c: 90000008     	adrp	x8, 0x0 <create_boxed_value>
		000000000000000c:  R_AARCH64_ADR_GOT_PAGE	__rust_no_alloc_shim_is_unstable
      10: 2a0003f3     	mov	w19, w0
      14: 52800080     	mov	w0, #0x4                // =4
      18: f9400108     	ldr	x8, [x8]
		0000000000000018:  R_AARCH64_LD64_GOT_LO12_NC	__rust_no_alloc_shim_is_unstable
      1c: 52800081     	mov	w1, #0x4                // =4
      20: 3940011f     	ldrb	wzr, [x8]
      24: 94000000     	bl	0x24 <create_boxed_value+0x24>
		0000000000000024:  R_AARCH64_CALL26	__rustc::__rust_alloc
      28: b40000c0     	cbz	x0, 0x40 <create_boxed_value+0x40>
      2c: 531f7a68     	lsl	w8, w19, #1
      30: b9000008     	str	w8, [x0]
      34: f9400bf3     	ldr	x19, [sp, #0x10]
      38: a8c27bfd     	ldp	x29, x30, [sp], #0x20
      3c: d65f03c0     	ret
      40: 52800080     	mov	w0, #0x4                // =4
      44: 52800081     	mov	w1, #0x4                // =4
      48: 94000000     	bl	0x48 <create_boxed_value+0x48>
		0000000000000048:  R_AARCH64_CALL26	alloc::alloc::handle_alloc_error

There is one piece of information we need to be familiar with to fully understand all of the instructions. __rust_no_alloc_shim_is_unstable is an internal variable, ldrb wzr,[x8] forces the linker to include the alloc shim. The tracking issue is here for anyone who might be interested.

Since our input is i32, __rust_alloc is called with the following arguments (size and alignment):

      14: 52800080     	mov	w0, #0x4                // =4
      1c: 52800081     	mov	w1, #0x4                // =4

If the allocation fails and returns null, handle_alloc_error is called:

      28: b40000c0     	cbz	x0, 0x40 <create_boxed_value+0x40>

After successful allocation, the input value is multiplied by 2 and stored in the allocated section:

      10: 2a0003f3     	mov	w19, w0
...
      2c: 531f7a68     	lsl	w8, w19, #1
      30: b9000008     	str	w8, [x0]

process_box

#![allow(unused)]
fn main() {
#[unsafe(no_mangle)]
pub fn process_box(boxed: Box<i32>) -> i32 {
    *boxed + 10
}
}

Disassembly:

$ llvm-objdump -r --disassemble-symbols=process_box target/aarch64-unknown-linux-musl/release/deps/rust_lab-15e16dbcf37b825b.o | rustfilt

target/aarch64-unknown-linux-musl/release/deps/rust_lab-15e16dbcf37b825b.o:	file format elf64-littleaarch64

Disassembly of section .text.process_box:

0000000000000000 <process_box>:
       0: a9be7bfd     	stp	x29, x30, [sp, #-0x20]!
       4: f9000bf3     	str	x19, [sp, #0x10]
       8: 910003fd     	mov	x29, sp
       c: b9400013     	ldr	w19, [x0]
      10: 52800081     	mov	w1, #0x4                // =4
      14: 52800082     	mov	w2, #0x4                // =4
      18: 94000000     	bl	0x18 <process_box+0x18>
		0000000000000018:  R_AARCH64_CALL26	__rustc::__rust_dealloc
      1c: 11002a60     	add	w0, w19, #0xa
      20: f9400bf3     	ldr	x19, [sp, #0x10]
      24: a8c27bfd     	ldp	x29, x30, [sp], #0x20
      28: d65f03c0     	ret

The function takes ownership of the Box and then boxed goes out of scope at the end of the function, so the memory is automatically deallocated. Since the data stored in the Box is i32, __rust_dealloc is called with the following arguments (pointer, size and alignment):

      10: 52800081     	mov	w1, #0x4                // =4
      14: 52800082     	mov	w2, #0x4                // =4

x0 already holds the pointer to the allocated memory, since the Box is passed to the function.

10 is added to the value and the result is returned:

       c: b9400013     	ldr	w19, [x0]
...
      1c: 11002a60     	add	w0, w19, #0xa