Skip to content

Commit 6c0fd4c

Browse files
committed
WARP format 1.0
- Update to flatbuffers `25.2.10` - Add fuzzing targets for type and function `from_bytes` - Update examples - Simplify type spec - Make constraints generic and remove specialized constraint lists - Space optimizations for type and functions specs - More tests with greater coverage - Introduce the concept of a WARP `File` and `Chunk`s - Make chunk compression configurable - Make `Type` objects class field unboxed (decreases memory pressure) - Use standard directory structure for Rust API - Move tests to `tests` directory for more easy discovery - Remove almost all uses of `unwrap` (needed for server-side parsing) - Refactor `TypeMetadata` - Add `mock` module for easy mocking in tests and examples - Make `Symbol` space optimized - Switch to using `.warp` extension to represent general analysis data instead of just signatures - Add format version to `File` and `Chunk` (allow for breaking changes later) - Make analysis data (signatures and types) copy on write (See `ChunkHandler` impl's) This work is being done to allow for networked WARP information and generally to make the WARP format more usable in a wider set of scenarios. After this commit any breaking changes to the format will be held off for 2.0, if that ever becomes a thing.
1 parent aa84f00 commit 6c0fd4c

137 files changed

Lines changed: 5157 additions & 4055 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,10 @@ generated/
5656
**/out
5757

5858
*.vsix
59-
*.deb
59+
*.deb
60+
61+
# cargo-mutants
62+
mutants*
63+
64+
*.warp
65+
*.sbin

Cargo.toml

Lines changed: 9 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,13 @@
1-
[package]
2-
name = "warp"
3-
version = "0.1.0"
4-
edition = "2021"
5-
license = "Apache-2.0"
1+
[workspace]
2+
resolver = "2"
3+
members = [
4+
"rust",
5+
"rust/fuzz",
6+
"warp_cli"
7+
]
68

7-
[lib]
8-
path = "rust/lib.rs"
9-
10-
[dependencies]
11-
flatbuffers = "24.3.25"
12-
bon = "2.3.0"
13-
uuid = { version = "1.11.0", features = ["v5"]}
14-
rand = "0.8.5"
15-
flate2 = "1.0.34"
16-
17-
[features]
18-
default = []
19-
gen_flatbuffers = ["dep:flatbuffers-build"]
20-
21-
[dev-dependencies]
22-
criterion = "0.5.1"
23-
24-
[build-dependencies]
25-
flatbuffers-build = { git = "https://github.com/emesare/flatbuffers-build", features = ["vendored"], optional = true }
9+
[workspace.dependencies]
10+
warp = { path = "rust" }
2611

2712
[profile.release]
2813
panic = "abort"
@@ -31,16 +16,3 @@ debug = "full"
3116

3217
[profile.bench]
3318
lto = true
34-
35-
[[example]]
36-
name = "simple"
37-
path = "rust/examples/simple.rs"
38-
39-
[[example]]
40-
name = "random"
41-
path = "rust/examples/random.rs"
42-
43-
[[bench]]
44-
name = "void"
45-
path = "rust/benches/void.rs"
46-
harness = false

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright 2020-2024 Vector 35 Inc.
1+
Copyright 2020-2025 Vector 35 Inc.
22

33
Licensed under the Apache License, Version 2.0 (the "License");
44
you may not use this file except in compliance with the License.

README.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ The basic block GUID is the UUIDv5 of the byte sequence of the instructions (sor
6868

6969
#### When are instructions that set a register to itself removed?
7070

71-
To support hot-patching we must remove them as they can be injected by the compiler at the start of a function (see: [1] and [2]).
71+
To support hot-patching, we must remove them as they can be injected by the compiler at the start of a function (see: [1] and [2]).
7272
This does not affect the accuracy of the function GUID as they are only removed when the instruction is a NOP:
7373

7474
- Register groups with no implicit extension will be removed (see: [3] (under 3.4.1.1))
@@ -85,15 +85,33 @@ For the `x86` architecture the instruction `e8b55b0100` (or `call 0x15bba`) woul
8585

8686
The namespace for Basic Block GUID's is `0192a178-7a5f-7936-8653-3cbaa7d6afe7`.
8787

88-
### Function Constraints
88+
### Constraints
8989

90-
Function constraints allow us to further disambiguate between functions with the same GUID, when creating the functions we store information about the following:
90+
Constraints allow us to further disambiguate between functions with the same GUID; when creating the functions, we retrieve extra information
91+
that is consistent between versions of the same function, some examples are:
9192

9293
- Called functions
9394
- Caller functions
9495
- Adjacent functions
9596

96-
Each entry in the lists above is referred to as a "constraint" that can be used to further reduce the number of matches for a given function GUID.
97+
Each extra piece of information is referred to as a "constraint" that can be used to further reduce the number of matches for a given function GUID.
98+
99+
#### Creating a Constraint
100+
101+
Constraints are made up of a GUID and optionally, a matching offset. Adding a matching offset is preferred to give locality to the constraints,
102+
for example, if you have a function `A` which calls into function `B` that is one constraint, but if the function `B` is also adjacent to function `A`
103+
without a matching offset the two constraints may be merged into a single one, reducing the number of matching constraints.
104+
105+
- The adjacent function `B` as a constraint: `(9F188A12-3EA1-477D-B368-361936EEA213, -30)`
106+
- The call to function `B` as a constraint: `(9F188A12-3EA1-477D-B368-361936EEA213, 48)`
107+
108+
#### Creating a Constraint GUID
109+
110+
The constraint GUID is the UUIDv5 of the relevant bytes that would be computable at creation time and lookup time.
111+
112+
##### What is the UUIDv5 namespace?
113+
114+
The namespace for Constraint GUID's is `019701f3-e89c-7afa-9181-371a5e98a576`.
97115

98116
##### Why don't we require matching on constraints for trivial functions?
99117

about.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ accepted = [
33
"Apache-2.0",
44
"MIT",
55
"Unicode-DFS-2016",
6+
"Unicode-3.0",
67
"OFL-1.1",
78
"BSL-1.0",
89
"BSD-3-Clause",
@@ -11,5 +12,6 @@ accepted = [
1112
"NOASSERTION",
1213
"ISC",
1314
"Zlib",
14-
"OpenSSL"
15+
"OpenSSL",
16+
"NCSA"
1517
]

rust/Cargo.toml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[package]
2+
name = "warp"
3+
version = "1.0.0"
4+
edition = "2021"
5+
license = "Apache-2.0"
6+
7+
[dependencies]
8+
flatbuffers = "25.2.10"
9+
bon = "3.6.3"
10+
uuid = { version = "1.11.0", features = ["v5"]}
11+
flate2 = "1.0.34"
12+
itertools = "0.14"
13+
14+
[features]
15+
default = []
16+
gen_flatbuffers = ["dep:flatbuffers-build"]
17+
18+
[dev-dependencies]
19+
criterion = "0.6.0"
20+
21+
[build-dependencies]
22+
flatbuffers-build = { git = "https://github.com/emesare/flatbuffers-build", rev = "44410b9", features = ["vendored"], optional = true }
23+
24+
[[example]]
25+
name = "type_builder"
26+
27+
[[example]]
28+
name = "dumper"
29+
30+
[[bench]]
31+
name = "type"
32+
harness = false
33+
34+
[[bench]]
35+
name = "chunk"
36+
harness = false

rust/benches/chunk.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use criterion::{criterion_group, criterion_main, Criterion};
2+
use warp::chunk::{Chunk, ChunkKind, CompressionType};
3+
use warp::mock::{mock_function, mock_function_type_class, mock_type};
4+
use warp::r#type::chunk::TypeChunk;
5+
use warp::signature::chunk::SignatureChunk;
6+
use warp::{WarpFile, WarpFileHeader};
7+
8+
pub fn chunk_benchmark(c: &mut Criterion) {
9+
let count = 10000;
10+
// Fill out a signature chunk with functions.
11+
let mut functions = Vec::new();
12+
for i in 0..count {
13+
functions.push(mock_function(&format!("function_{}", i)));
14+
}
15+
let _signature_chunk = SignatureChunk::new(&functions).expect("Failed to create chunk");
16+
let signature_chunk = Chunk::new(
17+
ChunkKind::Signature(_signature_chunk),
18+
CompressionType::None,
19+
);
20+
21+
// Fill out a type chunk with types.
22+
let mut types = Vec::new();
23+
for i in 0..count {
24+
types.push(mock_type(
25+
&format!("type_{}", i),
26+
mock_function_type_class(),
27+
));
28+
}
29+
let _type_chunk = TypeChunk::new(&types).expect("Failed to create chunk");
30+
let type_chunk = Chunk::new(ChunkKind::Type(_type_chunk), CompressionType::Zstd);
31+
let file = WarpFile::new(WarpFileHeader::new(), vec![signature_chunk, type_chunk]);
32+
c.bench_function("file to bytes", |b| {
33+
b.iter(|| {
34+
file.to_bytes();
35+
})
36+
});
37+
}
38+
39+
criterion_group!(benches, chunk_benchmark);
40+
criterion_main!(benches);

rust/benches/type.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use criterion::{criterion_group, criterion_main, Criterion};
2+
use warp::mock::{mock_int_type_class, mock_type};
3+
use warp::r#type::class::{StructureClass, StructureMember, TypeClass};
4+
use warp::r#type::guid::TypeGUID;
5+
use warp::r#type::Type;
6+
7+
pub fn void_benchmark(c: &mut Criterion) {
8+
let void_type = Type::builder()
9+
.name("my_void".to_owned())
10+
.class(TypeClass::Void)
11+
.build();
12+
13+
c.bench_function("uuid void", |b| {
14+
b.iter(|| {
15+
let _ = TypeGUID::from(&void_type);
16+
})
17+
});
18+
19+
c.bench_function("computed void", |b| b.iter(|| void_type.to_bytes()));
20+
}
21+
22+
pub fn struct_benchmark(c: &mut Criterion) {
23+
let int_type = mock_type("my_int", mock_int_type_class(None, false));
24+
let structure_member = StructureMember::builder()
25+
.name("member")
26+
.ty(int_type)
27+
.offset(0)
28+
.build();
29+
let struct_class = StructureClass::new(vec![structure_member]);
30+
let struct_type = Type::builder()
31+
.name("my_struct".to_owned())
32+
.class(TypeClass::Structure(struct_class))
33+
.build();
34+
35+
c.bench_function("uuid struct", |b| {
36+
b.iter(|| {
37+
let _ = TypeGUID::from(&struct_type);
38+
})
39+
});
40+
41+
c.bench_function("computed struct", |b| b.iter(|| struct_type.to_bytes()));
42+
}
43+
44+
criterion_group!(benches, void_benchmark, struct_benchmark);
45+
criterion_main!(benches);

rust/benches/void.rs

Lines changed: 0 additions & 22 deletions
This file was deleted.

build.rs renamed to rust/build.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ pub fn main() {
1010
let _ = std::fs::remove_dir_all("rust/gen_flatbuffers");
1111
let workspace_dir: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into();
1212
BuilderOptions::new_with_files([
13-
workspace_dir.join("type.fbs"),
14-
workspace_dir.join("symbol.fbs"),
15-
workspace_dir.join("signature.fbs"),
13+
workspace_dir.join("../type.fbs"),
14+
workspace_dir.join("../symbol.fbs"),
15+
workspace_dir.join("../signature.fbs"),
16+
workspace_dir.join("../target.fbs"),
17+
workspace_dir.join("../warp.fbs"),
1618
])
17-
.set_output_path("rust/gen_flatbuffers")
19+
.set_output_path("src/gen_flatbuffers")
1820
.compile()
1921
.expect("flatbuffer compilation failed");
2022
}

0 commit comments

Comments
 (0)