Why the Compiled Binary Files Are Inconsistent After the Directory of the Rust Package Is Changed?

Let's say I have two crate in my project, crate mangle2 depends on crate mangle1 and they are located in directory1.

root -> /usr1/directory1 $  tree 
.
├── mangle1
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
└── mangle2
    ├── Cargo.toml
    └── src
        └── lib.rs

in mangle2/Cargo.toml file, we see mangle2 depends on mangle1.

[package]
name = "mangle2"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
mangle1 = { path = "../mangle1" }

I define a simple function in the mangle1 directory and reference it in mangle2.

Then I went into the mangle2 directory and compiled the whole project.

root -> /usr1/directory1 $ cd mangle2
root -> /usr1/directory1/mangle2 $  cargo build
   Compiling mangle1 v0.1.0 (/usr1/s00659936/rust_program/mangle_disambiguator_test/test2/mangle1)
   Compiling mangle2 v0.1.0 (/usr1/s00659936/rust_program/mangle_disambiguator_test/test2/mangle2)
    Finished dev [unoptimized + debuginfo] target(s) in 0.19s

We use the readelf command to see the name of the symbol table.

root -> /usr1/directory1/mangle2 $  readelf -sW target/debug/libmangle2.rlib 
.....

Symbol table '.symtab' contains 19 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
......
    17: 0000000000000000    32 FUNC    GLOBAL DEFAULT    3 _ZN7mangle29super_add17h9e7d5740a4187474E
    18: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZN7mangle16sjhadd17h565c9c31ddc83d00E

Then I copied the directory1 directory to another location directory2.

root -> /usr1 $  cp directory1 directory2  -rf
root -> /usr1 $  cd directory2
root -> /usr1/directory2 $  tree 
.
├── mangle1
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
└── mangle2
    ├── Cargo.toml
    └── src
        └── lib.rs

As before, I went into the mangle2 directory and compiled the whole project.

root -> /usr1/directory2/mangle2 $  readelf -sW target/debug/libmangle2.rlib 
.....

Symbol table '.symtab' contains 19 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
......
    17: 0000000000000000    32 FUNC    GLOBAL DEFAULT    3 _ZN7mangle29super_add17hc8831168e2d1a643E
    18: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZN7mangle16sjhadd17h1484bdaf46e8f547E

As you can see, the name of the generating function has changed, and the last string of 17 hashes has changed.

_ZN7mangle29super_add17h9e7d5740a4187474E change to _ZN7mangle29super_add17hc8831168e2d1a643E

I didn't change a single line of code, and I cleaned up the build artifacts, including Cargo.lock, before compiling. Why is the resulting binary inconsistent?
If a single crate is copied to another directory for compilation, this problem does not occur. Binary inconsistency occurs when two crates depend on each other and both crates are copied to another directory.

What exactly is your issue? Nobody promised you that mangled names would be stable over compile runs. Even if you didn't copy any dirs, even if you didn't change a line of code, technically nothing prevents the compiler from generating new functions with new names (it likely won't just to avoid useless recompilation, but nothing guarantees that).

So, what problem are you trying to solve? Rust doesn't have a stable defined ABI, you can't link to those compiled libraries anyway. If you need stable function names, you need to use #[no_mangle].

It would be VERY nice to have reproducible builds. For example, you could check out a certain Git tag of your project and know that you'll always get the same exact binary.

This provides security (you can prove no one has tampered with the binary you already had).

Reproducible builds, independent of absolute path names of source files, would support massively distributed builds. The sccache tool is unfortunately crippled with Rust so that it's not practically very useful. We need something better.

1 Like

Reproducible builds is a goal of the project. (No idea what the current situation is.)

1 Like

One source of changes is that you are doing a debug build rather than a release build, so the source file paths are embedded in the binary through debuginfo.

As for the changing symbol names, can you check if the -C metadata argument passed by cargo to rustc differs between both locations? You can use cargo clean && cargo build -v to get all arguments passed by cargo.

Oh, the -C metadata argument passed by cargo to rustc is indeed different between both locations, Same problem in the release version. It is not reproducible.
How do I do reproducible builds for this rust project?

My problem is how do reproducible builds for this rust project?

Please take a look at the link from @quinedot above. You'll see that there are lots of unresolved problems with reproducible builds on Rust. This implies that it may not be currently possible.

-Cmetadata shouldn't be different even with rustc bugs.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.