Skip to content

Commit 6608ace

Browse files
jsomedonedwin0cheng
authored andcommitted
Read version of rustc that compiled proc macro
With Jay Somedon <jay.somedon@outlook.com>
1 parent 7275750 commit 6608ace

File tree

3 files changed

+100
-12
lines changed

3 files changed

+100
-12
lines changed

Cargo.lock

Lines changed: 26 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/proc_macro_api/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ jod-thread = "0.1.1"
1919
tt = { path = "../tt", version = "0.0.0" }
2020
base_db = { path = "../base_db", version = "0.0.0" }
2121
stdx = { path = "../stdx", version = "0.0.0" }
22+
snap = "1"
23+
object = "0.22.0"

crates/proc_macro_api/src/lib.rs

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,11 @@
55
//! is used to provide basic infrastructure for communication between two
66
//! processes: Client (RA itself), Server (the external program)
77
8-
mod rpc;
9-
mod process;
108
pub mod msg;
9+
mod process;
10+
mod rpc;
1111

12-
use std::{
13-
ffi::OsStr,
14-
io,
15-
path::{Path, PathBuf},
16-
sync::Arc,
17-
};
12+
use std::{ffi::OsStr, fs::read as fsread, io::{self, Read}, path::{Path, PathBuf}, sync::Arc};
1813

1914
use base_db::{Env, ProcMacro};
2015
use tt::{SmolStr, Subtree};
@@ -23,6 +18,9 @@ use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread};
2318

2419
pub use rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind};
2520

21+
use object::read::{File as BinaryFile, Object, ObjectSection};
22+
use snap::read::FrameDecoder as SnapDecoder;
23+
2624
#[derive(Debug, Clone)]
2725
struct ProcMacroProcessExpander {
2826
process: Arc<ProcMacroProcessSrv>,
@@ -71,7 +69,10 @@ impl ProcMacroClient {
7169
args: impl IntoIterator<Item = impl AsRef<OsStr>>,
7270
) -> io::Result<ProcMacroClient> {
7371
let (thread, process) = ProcMacroProcessSrv::run(process_path, args)?;
74-
Ok(ProcMacroClient { process: Arc::new(process), thread })
72+
Ok(ProcMacroClient {
73+
process: Arc::new(process),
74+
thread,
75+
})
7576
}
7677

7778
pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> {
@@ -98,8 +99,69 @@ impl ProcMacroClient {
9899
dylib_path: dylib_path.into(),
99100
});
100101

101-
ProcMacro { name, kind, expander }
102+
ProcMacro {
103+
name,
104+
kind,
105+
expander,
106+
}
102107
})
103108
.collect()
104109
}
110+
111+
// This is used inside self.read_version() to locate the ".rustc" section
112+
// from a proc macro crate's binary file.
113+
fn read_section<'a>(&self, dylib_binary: &'a [u8], section_name: &str) -> &'a [u8] {
114+
BinaryFile::parse(dylib_binary)
115+
.unwrap()
116+
.section_by_name(section_name)
117+
.unwrap()
118+
.data()
119+
.unwrap()
120+
}
121+
122+
// Check the version of rustc that was used to compile a proc macro crate's
123+
// binary file.
124+
// A proc macro crate binary's ".rustc" section has following byte layout:
125+
// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes
126+
// * ff060000 734e6150 is followed, it's the snappy format magic bytes,
127+
// means bytes from here(including this sequence) are compressed in
128+
// snappy compression format. Version info is here inside, so decompress
129+
// this.
130+
// The bytes you get after decompressing the snappy format portion has
131+
// following layout:
132+
// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes(again)
133+
// * [crate root bytes] next 4 bytes is to store crate root position,
134+
// according to rustc's source code comment
135+
// * [length byte] next 1 byte tells us how many bytes we should read next
136+
// for the version string's utf8 bytes
137+
// * [version string bytes encoded in utf8] <- GET THIS BOI
138+
// * [some more bytes that we don really care but still there] :-)
139+
// Check this issue for more about the bytes layout:
140+
// https://github.com/rust-analyzer/rust-analyzer/issues/6174
141+
fn read_version(&self, dylib_path: &Path) -> String {
142+
let dylib_binary = fsread(dylib_path).unwrap();
143+
144+
let dot_rustc = self.read_section(&dylib_binary, ".rustc");
145+
146+
let snappy_portion = &dot_rustc[8..];
147+
148+
let mut snappy_decoder = SnapDecoder::new(snappy_portion);
149+
150+
// the bytes before version string bytes, so this basically is:
151+
// 8 bytes for [b'r',b'u',b's',b't',0,0,0,5]
152+
// 4 bytes for [crate root bytes]
153+
// 1 byte for length of version string
154+
// so 13 bytes in total, and we should check the 13th byte
155+
// to know the length
156+
let mut bytes_before_version = [0u8; 13];
157+
snappy_decoder
158+
.read_exact(&mut bytes_before_version)
159+
.unwrap();
160+
let length = bytes_before_version[12]; // what? can't use -1 indexing?
161+
162+
let mut version_string_utf8 = vec![0u8; length as usize];
163+
snappy_decoder.read_exact(&mut version_string_utf8).unwrap();
164+
let version_string = String::from_utf8(version_string_utf8).unwrap();
165+
version_string
166+
}
105167
}

0 commit comments

Comments
 (0)