Skip to content

Commit a6c21c4

Browse files
authored
Add support to cdylib, dylib and staticlib (rust-lang#2039)
Refactor how we were creating the rlib files in the link stage. We cannot invoke rustc's linker directly for the native types since they will call the native linker that will fail. Instead, we just manually create an rlib file that includes only a metadata file. I cleaned up our archive builder since we no longer need to conform to rustc's interface.
1 parent 5b6e99a commit a6c21c4

File tree

16 files changed

+120
-188
lines changed

16 files changed

+120
-188
lines changed

kani-compiler/src/codegen_cprover_gotoc/archive.rs

Lines changed: 31 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -2,114 +2,45 @@
22
//
33
// Modifications Copyright Kani Contributors
44
// See GitHub history for details.
5-
// This file is a lightly modified version of upstream rustc:
5+
// This file is a heavily modified version of upstream rustc:
66
// compiler/rustc_codegen_cranelift/src/archive.rs
77

8-
// Along with lifting the deps:
9-
// object = { version = "0.27.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
10-
// ar = "0.8.0"
11-
12-
// Also: I removed all mentions of 'ranlib' which caused issues on macos.
13-
// We don't actually need these to be passed to a real linker anyway.
14-
// 'ranlib' is about building a global symbol table out of all the object
15-
// files in the archive, and we just don't put object files in our archives.
16-
178
//! Creation of ar archives like for the lib and staticlib crate type
9+
//! We now call the ArchiveBuilder directly, so we don't bother trying to fit into the rustc's
10+
//! `ArchiveBuilder`.
11+
//! Note that once we update to a version newer than 2022-12-04 we should be able to remove the
12+
//! logic here and use the compiler ArArchiveBuilder introduced here:
13+
//! <https://github.com/rust-lang/rust/pull/97485>
1814
15+
use rustc_session::Session;
1916
use std::fs::File;
20-
use std::io::{self, Read, Seek};
2117
use std::path::{Path, PathBuf};
2218

23-
use rustc_codegen_ssa::back::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
24-
use rustc_session::Session;
25-
26-
use object::read::archive::ArchiveFile;
27-
use object::ReadCache;
28-
29-
#[derive(Debug)]
30-
enum ArchiveEntry {
31-
FromArchive { archive_index: usize, file_range: (u64, u64) },
32-
File(PathBuf),
33-
}
34-
35-
pub(crate) struct ArArchiveBuilder<'a> {
19+
pub(crate) struct ArchiveBuilder<'a> {
3620
sess: &'a Session,
3721
use_gnu_style_archive: bool,
3822

39-
src_archives: Vec<File>,
4023
// Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at
4124
// the end of an archive for linkers to not get confused.
42-
entries: Vec<(Vec<u8>, ArchiveEntry)>,
25+
entries: Vec<(Vec<u8>, PathBuf)>,
4326
}
4427

45-
impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
46-
fn add_file(&mut self, file: &Path) {
28+
impl<'a> ArchiveBuilder<'a> {
29+
pub fn add_file(&mut self, file: &Path) {
4730
self.entries.push((
4831
file.file_name().unwrap().to_str().unwrap().to_string().into_bytes(),
49-
ArchiveEntry::File(file.to_owned()),
32+
file.to_owned(),
5033
));
5134
}
5235

53-
fn add_archive(
54-
&mut self,
55-
archive_path: &Path,
56-
mut skip: Box<dyn FnMut(&str) -> bool + 'static>,
57-
) -> std::io::Result<()> {
58-
let read_cache = ReadCache::new(std::fs::File::open(&archive_path)?);
59-
let archive = ArchiveFile::parse(&read_cache).unwrap();
60-
let archive_index = self.src_archives.len();
61-
62-
for entry in archive.members() {
63-
let entry = entry.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
64-
let file_name = String::from_utf8(entry.name().to_vec())
65-
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
66-
if !skip(&file_name) {
67-
self.entries.push((
68-
file_name.into_bytes(),
69-
ArchiveEntry::FromArchive { archive_index, file_range: entry.file_range() },
70-
));
71-
}
72-
}
73-
74-
self.src_archives.push(read_cache.into_inner());
75-
Ok(())
76-
}
77-
78-
fn build(mut self: Box<Self>, output: &Path) -> bool {
36+
pub fn build(&self, output: &Path) -> bool {
7937
enum BuilderKind {
8038
Bsd(ar::Builder<File>),
8139
Gnu(ar::GnuBuilder<File>),
8240
}
8341

8442
let sess = self.sess;
8543

86-
let mut entries = Vec::new();
87-
88-
for (entry_name, entry) in self.entries {
89-
// FIXME only read the symbol table of the object files to avoid having to keep all
90-
// object files in memory at once, or read them twice.
91-
let data = match entry {
92-
ArchiveEntry::FromArchive { archive_index, file_range } => {
93-
// FIXME read symbols from symtab
94-
let src_read_cache = &mut self.src_archives[archive_index];
95-
96-
src_read_cache.seek(io::SeekFrom::Start(file_range.0)).unwrap();
97-
let mut data = std::vec::from_elem(0, usize::try_from(file_range.1).unwrap());
98-
src_read_cache.read_exact(&mut data).unwrap();
99-
100-
data
101-
}
102-
ArchiveEntry::File(file) => std::fs::read(file).unwrap_or_else(|err| {
103-
sess.fatal(&format!(
104-
"error while reading object file during archive building: {}",
105-
err
106-
));
107-
}),
108-
};
109-
110-
entries.push((entry_name, data));
111-
}
112-
11344
let mut builder = if self.use_gnu_style_archive {
11445
BuilderKind::Gnu(ar::GnuBuilder::new(
11546
File::create(&output).unwrap_or_else(|err| {
@@ -118,50 +49,45 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
11849
err
11950
));
12051
}),
121-
entries.iter().map(|(name, _)| name.clone()).collect(),
52+
self.entries.iter().map(|(name, _)| name.clone()).collect(),
12253
))
12354
} else {
12455
BuilderKind::Bsd(ar::Builder::new(File::create(&output).unwrap_or_else(|err| {
12556
sess.fatal(&format!("error opening destination during archive building: {err}"));
12657
})))
12758
};
12859

60+
let entries = self.entries.iter().map(|(entry_name, file)| {
61+
let data = std::fs::read(file).unwrap_or_else(|err| {
62+
sess.fatal(&format!(
63+
"error while reading object file during archive building: {}",
64+
err
65+
));
66+
});
67+
(entry_name, data)
68+
});
69+
12970
// Add all files
130-
let any_members = !entries.is_empty();
131-
for (entry_name, data) in entries.into_iter() {
132-
let header = ar::Header::new(entry_name, data.len() as u64);
71+
let mut any_members = false;
72+
for (entry_name, data) in entries {
73+
let header = ar::Header::new(entry_name.clone(), data.len() as u64);
13374
match builder {
13475
BuilderKind::Bsd(ref mut builder) => builder.append(&header, &mut &*data).unwrap(),
13576
BuilderKind::Gnu(ref mut builder) => builder.append(&header, &mut &*data).unwrap(),
13677
}
78+
any_members = true;
13779
}
13880

13981
// Finalize archive
14082
std::mem::drop(builder);
14183
any_members
14284
}
143-
}
14485

145-
pub(crate) struct ArArchiveBuilderBuilder;
146-
147-
impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
148-
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder<'a> + 'a> {
149-
Box::new(ArArchiveBuilder {
86+
pub fn new(sess: &'a Session) -> Self {
87+
ArchiveBuilder {
15088
sess,
15189
use_gnu_style_archive: sess.target.archive_format == "gnu",
152-
src_archives: vec![],
15390
entries: vec![],
154-
})
155-
}
156-
157-
fn create_dll_import_lib(
158-
&self,
159-
_sess: &Session,
160-
_lib_name: &str,
161-
_dll_imports: &[rustc_session::cstore::DllImport],
162-
_tmpdir: &Path,
163-
_is_direct_dependency: bool,
164-
) -> PathBuf {
165-
unimplemented!("injecting dll imports is not supported");
91+
}
16692
}
16793
}

kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
//! This file contains the code necessary to interface with the compiler backend
55
6+
use crate::codegen_cprover_gotoc::archive::ArchiveBuilder;
67
use crate::codegen_cprover_gotoc::GotocCtx;
78
use crate::kani_middle::provide;
89
use crate::kani_middle::reachability::{
@@ -13,20 +14,24 @@ use cbmc::goto_program::Location;
1314
use cbmc::{InternedString, MachineModel};
1415
use kani_metadata::{ArtifactType, HarnessMetadata, KaniMetadata};
1516
use kani_queries::{QueryDb, ReachabilityType, UserInput};
17+
use rustc_codegen_ssa::back::metadata::create_wrapper_file;
1618
use rustc_codegen_ssa::traits::CodegenBackend;
1719
use rustc_codegen_ssa::{CodegenResults, CrateInfo};
1820
use rustc_data_structures::fx::FxHashMap;
21+
use rustc_data_structures::temp_dir::MaybeTempDir;
1922
use rustc_errors::ErrorGuaranteed;
2023
use rustc_hir::def::DefKind;
2124
use rustc_hir::def_id::LOCAL_CRATE;
25+
use rustc_metadata::fs::{emit_wrapper_file, METADATA_FILENAME};
2226
use rustc_metadata::EncodedMetadata;
2327
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
2428
use rustc_middle::mir::mono::{CodegenUnit, MonoItem};
2529
use rustc_middle::mir::write_mir_pretty;
2630
use rustc_middle::ty::query::Providers;
2731
use rustc_middle::ty::{self, InstanceDef, TyCtxt};
28-
use rustc_session::config::{OutputFilenames, OutputType};
32+
use rustc_session::config::{CrateType, OutputFilenames, OutputType};
2933
use rustc_session::cstore::MetadataLoaderDyn;
34+
use rustc_session::output::out_filename;
3035
use rustc_session::Session;
3136
use rustc_span::def_id::DefId;
3237
use rustc_target::abi::Endian;
@@ -42,6 +47,7 @@ use std::path::{Path, PathBuf};
4247
use std::process::Command;
4348
use std::rc::Rc;
4449
use std::time::Instant;
50+
use tempfile::Builder as TempFileBuilder;
4551
use tracing::{debug, error, info, warn};
4652

4753
#[derive(Clone)]
@@ -72,15 +78,15 @@ impl CodegenBackend for GotocCodegenBackend {
7278
&self,
7379
tcx: TyCtxt,
7480
rustc_metadata: EncodedMetadata,
75-
need_metadata_module: bool,
81+
_need_metadata_module: bool,
7682
) -> Box<dyn Any> {
7783
super::utils::init();
7884

7985
// Follow rustc naming convention (cx is abbrev for context).
8086
// https://rustc-dev-guide.rust-lang.org/conventions.html#naming-conventions
8187
let mut gcx = GotocCtx::new(tcx, self.queries.clone());
8288
check_target(tcx.sess);
83-
check_options(tcx.sess, need_metadata_module);
89+
check_options(tcx.sess);
8490
check_crate_items(&gcx);
8591

8692
let items = with_timer(|| collect_codegen_items(&gcx), "codegen reachability analysis");
@@ -188,38 +194,45 @@ impl CodegenBackend for GotocCodegenBackend {
188194
.unwrap())
189195
}
190196

197+
/// Emit `rlib` files during the link stage if it was requested.
198+
///
199+
/// We need to emit `rlib` files normally if requested. Cargo expects these in some
200+
/// circumstances and sends them to subsequent builds with `-L`.
201+
///
202+
/// We CAN NOT invoke the native linker, because that will fail. We don't have real objects.
203+
/// What determines whether the native linker is invoked or not is the set of `crate_types`.
204+
/// Types such as `bin`, `cdylib`, `dylib` will trigger the native linker.
205+
///
206+
/// Thus, we manually build the rlib file including only the `rmeta` file.
191207
fn link(
192208
&self,
193209
sess: &Session,
194210
codegen_results: CodegenResults,
195211
outputs: &OutputFilenames,
196212
) -> Result<(), ErrorGuaranteed> {
197-
// In `link`, we need to do two things:
198-
// 1. We need to emit `rlib` files normally. Cargo expects these in some circumstances and sends
199-
// them to subsequent builds with `-L`.
200-
// 2. We MUST NOT try to invoke the native linker, because that will fail. We don't have real objects.
201-
// This is normally not a problem: usually we only get one requested `crate-type`.
202-
// But let's be careful and fail loudly if we get conflicting requests:
203213
let requested_crate_types = sess.crate_types();
204-
// Quit successfully if we don't need an `rlib`:
205-
if !requested_crate_types.contains(&rustc_session::config::CrateType::Rlib) {
214+
if !requested_crate_types.contains(&CrateType::Rlib) {
215+
// Quit successfully if we don't need an `rlib`:
206216
return Ok(());
207217
}
208-
// Fail loudly if we need an `rlib` (above!) and *also* an executable, which
209-
// we can't produce, and can't easily suppress in `link_binary`:
210-
if requested_crate_types.contains(&rustc_session::config::CrateType::Executable) {
211-
sess.err("Build crate-type requested both rlib and executable, and Kani cannot handle this situation");
212-
sess.abort_if_errors();
213-
}
214218

215-
// All this ultimately boils down to is emitting an `rlib` that contains just one file: `lib.rmeta`
216-
use rustc_codegen_ssa::back::link::link_binary;
217-
link_binary(
219+
// Emit the `rlib` that contains just one file: `<crate>.rmeta`
220+
let mut builder = ArchiveBuilder::new(sess);
221+
let tmp_dir = TempFileBuilder::new().prefix("kani").tempdir().unwrap();
222+
let path = MaybeTempDir::new(tmp_dir, sess.opts.cg.save_temps);
223+
let (metadata, _metadata_position) =
224+
create_wrapper_file(sess, b".rmeta".to_vec(), codegen_results.metadata.raw_data());
225+
let metadata = emit_wrapper_file(sess, &metadata, &path, METADATA_FILENAME);
226+
builder.add_file(&metadata);
227+
228+
let rlib = out_filename(
218229
sess,
219-
&crate::codegen_cprover_gotoc::archive::ArArchiveBuilderBuilder,
220-
&codegen_results,
230+
CrateType::Rlib,
221231
outputs,
222-
)
232+
codegen_results.crate_info.local_crate_name.as_str(),
233+
);
234+
builder.build(&rlib);
235+
Ok(())
223236
}
224237
}
225238

@@ -246,7 +259,7 @@ fn check_target(session: &Session) {
246259
session.abort_if_errors();
247260
}
248261

249-
fn check_options(session: &Session, need_metadata_module: bool) {
262+
fn check_options(session: &Session) {
250263
// The requirements for `min_global_align` and `endian` are needed to build
251264
// a valid CBMC machine model in function `machine_model_from_session` from
252265
// src/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs
@@ -277,10 +290,6 @@ fn check_options(session: &Session, need_metadata_module: bool) {
277290
);
278291
}
279292

280-
if need_metadata_module {
281-
session.err("Kani cannot generate metadata module.");
282-
}
283-
284293
session.abort_if_errors();
285294
}
286295

kani-compiler/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ extern crate rustc_middle;
2626
extern crate rustc_session;
2727
extern crate rustc_span;
2828
extern crate rustc_target;
29+
// We can't add this directly as a dependency because we need the version to match rustc
30+
extern crate tempfile;
2931

3032
#[cfg(feature = "cprover")]
3133
mod codegen_cprover_gotoc;

0 commit comments

Comments
 (0)