The --emit obj option causes binary inconsistency in the output

After the same code is added with the compilation option --emit obj and not added, the function assembly instructions in the generated binary are inconsistent. For example:
Create a crate and write the following code:

cargo new --lib emitobj_test

#[inline(never)]
fn rust_function_base032_local(dat: &mut i32) -> i32 {
    println!("\t[bin] test_function_base032_local");
    *dat + 1
}

pub fn rust_function_base032() -> i32 {
    println!("\t[same] test_function_base032");
    let mut dat = 9;
    rust_function_base032_local(&mut dat)
}

Then I compile with cargo build --release. View the symbol table of the output binary libxxx.rlib.

readelf -sW target/release/libemitobj_test.rlib
.......
Symbol table '.symtab' contains 20 entries:
Num: Value Size Type Bind Vis Ndx Name
3: 0000000000000000 60 FUNC LOCAL DEFAULT 3 ...rust_function_base032_local...
.....
19: 0000000000000000 68 FUNC GLOBAL DEFAULT 5 ...rust_function_base032...

From the symbol table, you can see that the size of the two functions is 60 bytes and 68 bytes, respectively.

But when I compile with cargo build --release and specify the --emit=obj compilation option in the config file. View the symbol table of the output binary libxxx.rlib.

readelf -sW target/release/libemitobj_test.rlib
.......
Symbol table '.symtab' contains 20 entries:
Num: Value Size Type Bind Vis Ndx Name
3: 0000000000000000 68 FUNC LOCAL DEFAULT 3 ...rust_function_base032_local...
.....
19: 0000000000000000 64 FUNC GLOBAL DEFAULT 5 ...rust_function_base032...

From the symbol table, you can see that the size of the two functions becomes 68 bytes and 64 bytes.
The same phenomenon occurs with the --emit=llvm-ir option.

I wanted to output the intermediate files llvm-ir and object for some analysis, but found that the binary changed after adding two options. Is this a bug?

By the way, look at the assembly of the two functions, there are some differences.

With compilation option --emit=obj or --emit=llvm-ir, see assemble:

Disassembly of section .text._ZN12emitobj_test27rust_function_base032_local17hcd8ff730eed1f947E:

0000000000000000 <_ZN12emitobj_test27rust_function_base032_local17hcd8ff730eed1f947E>:
   0:	d10103ff 	sub	sp, sp, #0x40
   4:	a9034ffe 	stp	x30, x19, [sp, #48]
   8:	2a0003f3 	mov	w19, w0
   c:	52800028 	mov	w8, #0x1                   	// #1
  10:	90000009 	adrp	x9, 0 <_ZN12emitobj_test27rust_function_base032_local17hcd8ff730eed1f947E>
			10: R_AARCH64_ADR_PREL_PG_HI21	.data.rel.ro..L__unnamed_1
  14:	91000129 	add	x9, x9, #0x0
			14: R_AARCH64_ADD_ABS_LO12_NC	.data.rel.ro..L__unnamed_1
  18:	9000000a 	adrp	x10, 0 <_ZN12emitobj_test27rust_function_base032_local17hcd8ff730eed1f947E>
			18: R_AARCH64_ADR_PREL_PG_HI21	.rodata..L__unnamed_2
  1c:	9100014a 	add	x10, x10, #0x0
			1c: R_AARCH64_ADD_ABS_LO12_NC	.rodata..L__unnamed_2
  20:	910003e0 	mov	x0, sp
  24:	a901ffff 	stp	xzr, xzr, [sp, #24]
  28:	a90023e9 	stp	x9, x8, [sp]
  2c:	f9000bea 	str	x10, [sp, #16]
  30:	94000000 	bl	0 <_ZN3std2io5stdio6_print17h861a5137f394eca5E>
			30: R_AARCH64_CALL26	_ZN3std2io5stdio6_print17h861a5137f394eca5E
  34:	11000660 	add	w0, w19, #0x1
  38:	a9434ffe 	ldp	x30, x19, [sp, #48]
  3c:	910103ff 	add	sp, sp, #0x40
  40:	d65f03c0 	ret

Disassembly of section .text._ZN12emitobj_test21rust_function_base03217h538a03c81fdcb424E:

0000000000000000 <_ZN12emitobj_test21rust_function_base03217h538a03c81fdcb424E>:
   0:	d10103ff 	sub	sp, sp, #0x40
   4:	f9001bfe 	str	x30, [sp, #48]
   8:	90000008 	adrp	x8, 0 <_ZN12emitobj_test21rust_function_base03217h538a03c81fdcb424E>
			8: R_AARCH64_ADR_PREL_PG_HI21	.data.rel.ro..L__unnamed_3
   c:	91000108 	add	x8, x8, #0x0
			c: R_AARCH64_ADD_ABS_LO12_NC	.data.rel.ro..L__unnamed_3
  10:	52800029 	mov	w9, #0x1                   	// #1
  14:	9000000a 	adrp	x10, 0 <_ZN12emitobj_test21rust_function_base03217h538a03c81fdcb424E>
			14: R_AARCH64_ADR_PREL_PG_HI21	.rodata..L__unnamed_2
  18:	9100014a 	add	x10, x10, #0x0
			18: R_AARCH64_ADD_ABS_LO12_NC	.rodata..L__unnamed_2
  1c:	910003e0 	mov	x0, sp
  20:	a901ffff 	stp	xzr, xzr, [sp, #24]
  24:	a90027e8 	stp	x8, x9, [sp]
  28:	f9000bea 	str	x10, [sp, #16]
  2c:	94000000 	bl	0 <_ZN3std2io5stdio6_print17h861a5137f394eca5E>
			2c: R_AARCH64_CALL26	_ZN3std2io5stdio6_print17h861a5137f394eca5E
  30:	f9401bfe 	ldr	x30, [sp, #48]
  34:	52800120 	mov	w0, #0x9                   	// #9
  38:	910103ff 	add	sp, sp, #0x40
  3c:	14000000 	b	0 <_ZN12emitobj_test21rust_function_base03217h538a03c81fdcb424E>
			3c: R_AARCH64_JUMP26	.text._ZN12emitobj_test27rust_function_base032_local17hcd8ff730eed1f947E

Without compilation option --emit=obj or --emit=llvm-ir, see assemble:

Disassembly of section .text._ZN12emitobj_test27rust_function_base032_local17hcd8ff730eed1f947E:

0000000000000000 <_ZN12emitobj_test27rust_function_base032_local17hcd8ff730eed1f947E>:
   0:	d10103ff 	sub	sp, sp, #0x40
   4:	f9001bfe 	str	x30, [sp, #48]
   8:	90000008 	adrp	x8, 0 <_ZN12emitobj_test27rust_function_base032_local17hcd8ff730eed1f947E>
			8: R_AARCH64_ADR_PREL_PG_HI21	.data.rel.ro..Lanon.2a7dbb5b68853135ea7f782c2c20ff64.1
   c:	91000108 	add	x8, x8, #0x0
			c: R_AARCH64_ADD_ABS_LO12_NC	.data.rel.ro..Lanon.2a7dbb5b68853135ea7f782c2c20ff64.1
  10:	52800029 	mov	w9, #0x1                   	// #1
  14:	9000000a 	adrp	x10, 0 <_ZN12emitobj_test27rust_function_base032_local17hcd8ff730eed1f947E>
			14: R_AARCH64_ADR_PREL_PG_HI21	.rodata..Lanon.2a7dbb5b68853135ea7f782c2c20ff64.2
  18:	9100014a 	add	x10, x10, #0x0
			18: R_AARCH64_ADD_ABS_LO12_NC	.rodata..Lanon.2a7dbb5b68853135ea7f782c2c20ff64.2
  1c:	910003e0 	mov	x0, sp
  20:	a901ffff 	stp	xzr, xzr, [sp, #24]
  24:	a90027e8 	stp	x8, x9, [sp]
  28:	f9000bea 	str	x10, [sp, #16]
  2c:	94000000 	bl	0 <_ZN3std2io5stdio6_print17h861a5137f394eca5E>
			2c: R_AARCH64_CALL26	_ZN3std2io5stdio6_print17h861a5137f394eca5E
  30:	f9401bfe 	ldr	x30, [sp, #48]
  34:	910103ff 	add	sp, sp, #0x40
  38:	d65f03c0 	ret

Disassembly of section .text._ZN12emitobj_test21rust_function_base03217h538a03c81fdcb424E:

0000000000000000 <_ZN12emitobj_test21rust_function_base03217h538a03c81fdcb424E>:
   0:	d10103ff 	sub	sp, sp, #0x40
   4:	f9001bfe 	str	x30, [sp, #48]
   8:	90000008 	adrp	x8, 0 <_ZN12emitobj_test21rust_function_base03217h538a03c81fdcb424E>
			8: R_AARCH64_ADR_PREL_PG_HI21	.data.rel.ro..Lanon.2a7dbb5b68853135ea7f782c2c20ff64.4
   c:	91000108 	add	x8, x8, #0x0
			c: R_AARCH64_ADD_ABS_LO12_NC	.data.rel.ro..Lanon.2a7dbb5b68853135ea7f782c2c20ff64.4
  10:	52800029 	mov	w9, #0x1                   	// #1
  14:	9000000a 	adrp	x10, 0 <_ZN12emitobj_test21rust_function_base03217h538a03c81fdcb424E>
			14: R_AARCH64_ADR_PREL_PG_HI21	.rodata..Lanon.2a7dbb5b68853135ea7f782c2c20ff64.2
  18:	9100014a 	add	x10, x10, #0x0
			18: R_AARCH64_ADD_ABS_LO12_NC	.rodata..Lanon.2a7dbb5b68853135ea7f782c2c20ff64.2
  1c:	910003e0 	mov	x0, sp
  20:	a901ffff 	stp	xzr, xzr, [sp, #24]
  24:	a90027e8 	stp	x8, x9, [sp]
  28:	f9000bea 	str	x10, [sp, #16]
  2c:	94000000 	bl	0 <_ZN3std2io5stdio6_print17h861a5137f394eca5E>
			2c: R_AARCH64_CALL26	_ZN3std2io5stdio6_print17h861a5137f394eca5E
  30:	94000000 	bl	0 <_ZN12emitobj_test21rust_function_base03217h538a03c81fdcb424E>
			30: R_AARCH64_CALL26	.text._ZN12emitobj_test27rust_function_base032_local17hcd8ff730eed1f947E
  34:	f9401bfe 	ldr	x30, [sp, #48]
  38:	52800140 	mov	w0, #0xa                   	// #10
  3c:	910103ff 	add	sp, sp, #0x40
  40:	d65f03c0 	ret

--emit asm, --emit obj, --emit llvm-ir and --emit llvm-bc all force a single codegen unit to be used, which can affect optimizations. This is for back compat reasons. At first rustc only supported a single codegen unit. When support for multiple codegen units was added to improve performance, --emit asm, --emit obj, --emit llvm-ir and --emit llvm-bc had to keep producing a single file to avoid tools breaking, which can only be done by forcing a single codegen unit to be used. You can use -Csave-temps to keep the original object files that will be linked or put in an rlib. They are stored next to the compilation output and their names contain the crate name and a hash of unspecified input.

Thank you for your reply. -Csave-temps are indeed useful. They generate the obj files I need, as well as many bc files. There are so many LLVM bc files here, Is there a document that explains what these bc files represent.

root@ubuntu:/home/emitobj_test/target/release/deps# ls
emitobj_test-3d272638f9b2fb32.d                                                                       emitobj_test-3d272638f9b2fb32.emitobj_test.5de40cd777e0fa1a-cgu.0.rcgu.thin-lto-after-rename.bc
emitobj_test-3d272638f9b2fb32.emitobj_test.5de40cd777e0fa1a-cgu.0.rcgu.bc                             emitobj_test-3d272638f9b2fb32.emitobj_test.5de40cd777e0fa1a-cgu.0.rcgu.thin-lto-after-resolve.bc
emitobj_test-3d272638f9b2fb32.emitobj_test.5de40cd777e0fa1a-cgu.0.rcgu.no-opt.bc                      emitobj_test-3d272638f9b2fb32.emitobj_test.5de40cd777e0fa1a-cgu.0.rcgu.thin-lto-input.bc
emitobj_test-3d272638f9b2fb32.emitobj_test.5de40cd777e0fa1a-cgu.0.rcgu.o                              libemitobj_test-3d272638f9b2fb32.rlib
emitobj_test-3d272638f9b2fb32.emitobj_test.5de40cd777e0fa1a-cgu.0.rcgu.thin-lto-after-import.bc       libemitobj_test-3d272638f9b2fb32.rmeta
emitobj_test-3d272638f9b2fb32.emitobj_test.5de40cd777e0fa1a-cgu.0.rcgu.thin-lto-after-internalize.bc  rmetaTJuWht
emitobj_test-3d272638f9b2fb32.emitobj_test.5de40cd777e0fa1a-cgu.0.rcgu.thin-lto-after-pm.bc

The ones with thin-lto in the name are intermediate artifacts produced by ThinLTO at various optimization stages. Even if you don't enable any LTO, when optimizations are enabled, rustc will use "thin local lto", which is basically ThinLTO except just between the codegen units of the current crate instead of between all crates. This is intended to reduce the runtime impact of using multiple codegen units while still providing most of the compile time reduction.