Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ alloy-json-abi = { version = "1.3", features = ["serde_json"] }
alloy-primitives = { version = "1.3", features = ["serde", "rand"] }
cfg-if = "1.0"
dunce = "1.0"
memchr = "2.7"
memmap2 = "0.9"
path-slash = "0.2"
rayon = "1.11"
Expand Down
1 change: 1 addition & 0 deletions crates/artifacts/solc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ foundry-compilers-core.workspace = true

alloy-json-abi.workspace = true
alloy-primitives.workspace = true
memchr.workspace = true
semver.workspace = true
serde_json.workspace = true
serde.workspace = true
Expand Down
49 changes: 38 additions & 11 deletions crates/artifacts/solc/src/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,17 +304,7 @@ impl BytecodeObject {
/// See also: <https://docs.soliditylang.org/en/develop/using-the-compiler.html#library-linking>
pub fn link_fully_qualified(&mut self, name: &str, addr: Address) -> &mut Self {
if let Self::Unlinked(unlinked) = self {
let place_holder = utils::library_hash_placeholder(name);
// the address as hex without prefix
let hex_addr = hex::encode(addr);

// the library placeholder used to be the fully qualified name of the library instead of
// the hash. This is also still supported by `solc` so we handle this as well
let fully_qualified_placeholder = utils::library_fully_qualified_placeholder(name);

*unlinked = unlinked
.replace(&format!("__{fully_qualified_placeholder}__"), &hex_addr)
.replace(&format!("__{place_holder}__"), &hex_addr)
link(unlinked, name, addr);
}
self
}
Expand Down Expand Up @@ -386,6 +376,43 @@ impl AsRef<[u8]> for BytecodeObject {
}
}

/// Reference: <https://github.com/argotorg/solidity/blob/965166317bbc2b02067eb87f222a2dce9d24e289/libevmasm/LinkerObject.cpp#L38>
fn link(unlinked: &mut String, name: &str, addr: Address) {
const LEN: usize = 40;

let mut refs = vec![];
let mut find = |needle: &str| {
assert_eq!(needle.len(), LEN, "{needle:?}");
refs.extend(memchr::memmem::find_iter(unlinked.as_bytes(), needle));
};

let placeholder = utils::library_hash_placeholder(name);
find(&format!("__{placeholder}__"));

// The library placeholder used to be the fully qualified name of the library instead of
// the hash. This is also still supported by `solc` so we handle this as well.
let fully_qualified_placeholder = utils::library_fully_qualified_placeholder(name);
find(&format!("__{fully_qualified_placeholder}__"));

if refs.is_empty() {
debug!("no references found while linking {name} -> {addr}");
return;
}

// The address as hex without prefix.
let mut buffer = hex::Buffer::<20, false>::new();
let hex_addr = &*buffer.format(&addr);
assert_eq!(hex_addr.len(), LEN, "{hex_addr:?}");

// The ranges are non-overlapping, so we don't need to sort, and can iterate in whatever order
// because of equal lengths.
// SAFETY: We're replacing LEN bytes at a time, and all the indexes come from the same string.
let unlinked = unsafe { unlinked.as_bytes_mut() };
for &idx in &refs {
unlinked[idx..idx + LEN].copy_from_slice(hex_addr.as_bytes());
}
}

/// This will serialize the bytecode data without a `0x` prefix, which the `ethers::types::Bytes`
/// adds by default.
///
Expand Down
Loading