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
37 changes: 37 additions & 0 deletions compiler/rustc_codegen_llvm/src/back/command_line_args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#[cfg(test)]
mod tests;

/// Joins command-line arguments into a single space-separated string, quoting
/// and escaping individual arguments as necessary.
///
/// The result is intended to be informational, for embedding in debug metadata,
/// and might not be properly quoted/escaped for actual command-line use.
pub(crate) fn quote_command_line_args(args: &[String]) -> String {
// Start with a decent-sized buffer, since rustc invocations tend to be long.
let mut buf = String::with_capacity(128);

for arg in args {
if !buf.is_empty() {
buf.push(' ');
}

print_arg_quoted(&mut buf, arg);
}

buf
}

/// Equivalent to LLVM's `sys::printArg` with quoting always enabled
/// (see llvm/lib/Support/Program.cpp).
fn print_arg_quoted(buf: &mut String, arg: &str) {
buf.reserve(arg.len() + 2);

buf.push('"');
for ch in arg.chars() {
if matches!(ch, '"' | '\\' | '$') {
buf.push('\\');
}
buf.push(ch);
}
buf.push('"');
}
25 changes: 25 additions & 0 deletions compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#[test]
fn quote_command_line_args() {
use super::quote_command_line_args;

struct Case<'a> {
args: &'a [&'a str],
expected: &'a str,
}

let cases = &[
Case { args: &[], expected: "" },
Case { args: &["--hello", "world"], expected: r#""--hello" "world""# },
Case { args: &["--hello world"], expected: r#""--hello world""# },
Case {
args: &["plain", "$dollar", "spa ce", r"back\slash", r#""quote""#, "plain"],
expected: r#""plain" "\$dollar" "spa ce" "back\\slash" "\"quote\"" "plain""#,
},
];

for &Case { args, expected } in cases {
let args = args.iter().copied().map(str::to_owned).collect::<Vec<_>>();
let actual = quote_command_line_args(&args);
assert_eq!(actual, expected, "args {args:?}");
}
}
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/back/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub(crate) mod archive;
mod command_line_args;
pub(crate) mod lto;
pub(crate) mod owned_target_machine;
mod profiling;
Expand Down
14 changes: 6 additions & 8 deletions compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::assert_matches::assert_matches;
use std::ffi::CStr;
use std::marker::PhantomData;
use std::ptr::NonNull;
Expand Down Expand Up @@ -39,13 +38,10 @@ impl OwnedTargetMachine {
output_obj_file: &CStr,
debug_info_compression: &CStr,
use_emulated_tls: bool,
args_cstr_buff: &[u8],
argv0: &str,
command_line_args: &str,
use_wasm_eh: bool,
) -> Result<Self, LlvmError<'static>> {
// The argument list is passed as the concatenation of one or more C strings.
// This implies that there must be a last byte, and it must be 0.
assert_matches!(args_cstr_buff, [.., b'\0'], "the last byte must be a NUL terminator");

// SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data
let tm_ptr = unsafe {
llvm::LLVMRustCreateTargetMachine(
Expand All @@ -70,8 +66,10 @@ impl OwnedTargetMachine {
output_obj_file.as_ptr(),
debug_info_compression.as_ptr(),
use_emulated_tls,
args_cstr_buff.as_ptr(),
args_cstr_buff.len(),
argv0.as_ptr(),
argv0.len(),
command_line_args.as_ptr(),
command_line_args.len(),
use_wasm_eh,
)
};
Expand Down
30 changes: 12 additions & 18 deletions compiler/rustc_codegen_llvm/src/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext, sym};
use rustc_target::spec::{CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel};
use tracing::{debug, trace};

use crate::back::command_line_args::quote_command_line_args;
use crate::back::lto::ThinBuffer;
use crate::back::owned_target_machine::OwnedTargetMachine;
use crate::back::profiling::{
Expand Down Expand Up @@ -249,23 +250,15 @@ pub(crate) fn target_machine_factory(

let use_emulated_tls = matches!(sess.tls_model(), TlsModel::Emulated);

// copy the exe path, followed by path all into one buffer
// null terminating them so we can use them as null terminated strings
let args_cstr_buff = {
let mut args_cstr_buff: Vec<u8> = Vec::new();
let exe_path = std::env::current_exe().unwrap_or_default();
let exe_path_str = exe_path.into_os_string().into_string().unwrap_or_default();

args_cstr_buff.extend_from_slice(exe_path_str.as_bytes());
args_cstr_buff.push(0);

for arg in sess.expanded_args.iter() {
args_cstr_buff.extend_from_slice(arg.as_bytes());
args_cstr_buff.push(0);
}

args_cstr_buff
};
// Command-line information to be included in the target machine.
// This seems to only be used for embedding in PDB debuginfo files.
// FIXME(Zalathar): Maybe skip this for non-PDB targets?
let argv0 = std::env::current_exe()
.unwrap_or_default()
.into_os_string()
.into_string()
.unwrap_or_default();
let command_line_args = quote_command_line_args(&sess.expanded_args);

let debuginfo_compression = sess.opts.debuginfo_compression.to_string();
match sess.opts.debuginfo_compression {
Expand Down Expand Up @@ -323,7 +316,8 @@ pub(crate) fn target_machine_factory(
&output_obj_file,
&debuginfo_compression,
use_emulated_tls,
&args_cstr_buff,
&argv0,
&command_line_args,
use_wasm_eh,
)
})
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2488,8 +2488,10 @@ unsafe extern "C" {
OutputObjFile: *const c_char,
DebugInfoCompression: *const c_char,
UseEmulatedTls: bool,
ArgsCstrBuff: *const c_uchar, // See "PTR_LEN_STR".
ArgsCstrBuffLen: usize,
Argv0: *const c_uchar, // See "PTR_LEN_STR".
Argv0Len: size_t,
CommandLineArgs: *const c_uchar, // See "PTR_LEN_STR".
CommandLineArgsLen: size_t,
UseWasmEH: bool,
) -> *mut TargetMachine;

Expand Down
28 changes: 7 additions & 21 deletions compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
bool TrapUnreachable, bool Singlethread, bool VerboseAsm,
bool EmitStackSizeSection, bool RelaxELFRelocations, bool UseInitArray,
const char *SplitDwarfFile, const char *OutputObjFile,
const char *DebugInfoCompression, bool UseEmulatedTls,
const char *ArgsCstrBuff, size_t ArgsCstrBuffLen, bool UseWasmEH) {
const char *DebugInfoCompression, bool UseEmulatedTls, const char *Argv0,
size_t Argv0Len, const char *CommandLineArgs, size_t CommandLineArgsLen,
bool UseWasmEH) {

auto OptLevel = fromRust(RustOptLevel);
auto RM = fromRust(RustReloc);
Expand Down Expand Up @@ -343,25 +344,10 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(

Options.EmitStackSizeSection = EmitStackSizeSection;

if (ArgsCstrBuff != nullptr) {
size_t buffer_offset = 0;
assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0');
auto Arg0 = std::string(ArgsCstrBuff);
buffer_offset = Arg0.size() + 1;

std::string CommandlineArgs;
raw_string_ostream OS(CommandlineArgs);
ListSeparator LS(" ");
for (StringRef Arg : split(StringRef(ArgsCstrBuff + buffer_offset,
ArgsCstrBuffLen - buffer_offset),
'\0')) {
OS << LS;
sys::printArg(OS, Arg, /*Quote=*/true);
}
OS.flush();
Options.MCOptions.Argv0 = Arg0;
Options.MCOptions.CommandlineArgs = CommandlineArgs;
}
if (Argv0 != nullptr)
Options.MCOptions.Argv0 = {Argv0, Argv0Len};
if (CommandLineArgs != nullptr)
Options.MCOptions.CommandlineArgs = {CommandLineArgs, CommandLineArgsLen};

#if LLVM_VERSION_GE(21, 0)
TargetMachine *TM = TheTarget->createTargetMachine(Trip, CPU, Feature,
Expand Down
Loading