Skip to content

Commit e739649

Browse files
committed
Add unstable option to nul-terminate location strings
1 parent fb546ee commit e739649

File tree

8 files changed

+61
-12
lines changed

8 files changed

+61
-12
lines changed

compiler/rustc_const_eval/src/util/caller_location.rs

+17-7
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,25 @@ fn alloc_caller_location<'tcx>(
1616
line: u32,
1717
col: u32,
1818
) -> MPlaceTy<'tcx> {
19+
// Ensure that the filename itself does not contain nul bytes.
20+
// This isn't possible via POSIX or Windows, but we should ensure no one
21+
// ever does such a thing.
22+
assert!(!filename.as_str().as_bytes().contains(&0));
23+
1924
let loc_details = ecx.tcx.sess.opts.unstable_opts.location_detail;
20-
// This can fail if rustc runs out of memory right here. Trying to emit an error would be
21-
// pointless, since that would require allocating more memory than these short strings.
22-
let file = if loc_details.file {
23-
ecx.allocate_str_dedup(filename.as_str()).unwrap()
25+
26+
let file = if loc_details.file { filename.as_str() } else { "<redacted>" };
27+
28+
// These allocations can fail if rustc runs out of memory right here. Trying to emit an error
29+
// would be pointless, since that would require allocating more memory than these short strings.
30+
let file_ptr = if loc_details.cstr {
31+
ecx.allocate_bytes_dedup(format!("{file}\0").as_bytes()).unwrap()
2432
} else {
25-
ecx.allocate_str_dedup("<redacted>").unwrap()
33+
ecx.allocate_bytes_dedup(file.as_bytes()).unwrap()
2634
};
27-
let file = file.map_provenance(CtfeProvenance::as_immutable);
35+
// We don't include the zero-terminator in the length here.
36+
let file_wide_ptr = Immediate::new_slice(file_ptr.into(), file.len().try_into().unwrap(), ecx);
37+
2838
let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) };
2939
let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) };
3040

@@ -37,7 +47,7 @@ fn alloc_caller_location<'tcx>(
3747
let location = ecx.allocate(loc_layout, MemoryKind::CallerLocation).unwrap();
3848

3949
// Initialize fields.
40-
ecx.write_immediate(file.to_ref(ecx), &ecx.project_field(&location, 0).unwrap())
50+
ecx.write_immediate(file_wide_ptr, &ecx.project_field(&location, 0).unwrap())
4151
.expect("writing to memory we just allocated cannot fail");
4252
ecx.write_scalar(line, &ecx.project_field(&location, 1).unwrap())
4353
.expect("writing to memory we just allocated cannot fail");

compiler/rustc_interface/src/tests.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,12 @@ fn test_unstable_options_tracking_hash() {
804804
tracked!(lint_llvm_ir, true);
805805
tracked!(llvm_module_flag, vec![("bar".to_string(), 123, "max".to_string())]);
806806
tracked!(llvm_plugins, vec![String::from("plugin_name")]);
807-
tracked!(location_detail, LocationDetail { file: true, line: false, column: false });
807+
tracked!(location_detail, LocationDetail {
808+
file: true,
809+
line: false,
810+
column: false,
811+
cstr: false
812+
});
808813
tracked!(maximal_hir_to_mir_coverage, true);
809814
tracked!(merge_functions, Some(MergeFunctions::Disabled));
810815
tracked!(mir_emit_retag, true);

compiler/rustc_session/src/config.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -398,11 +398,12 @@ pub struct LocationDetail {
398398
pub file: bool,
399399
pub line: bool,
400400
pub column: bool,
401+
pub cstr: bool,
401402
}
402403

403404
impl LocationDetail {
404405
pub(crate) fn all() -> Self {
405-
Self { file: true, line: true, column: true }
406+
Self { file: true, line: true, column: true, cstr: false }
406407
}
407408
}
408409

compiler/rustc_session/src/options.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ mod desc {
436436
"either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, `fat`, or omitted";
437437
pub(crate) const parse_linker_plugin_lto: &str =
438438
"either a boolean (`yes`, `no`, `on`, `off`, etc), or the path to the linker plugin";
439-
pub(crate) const parse_location_detail: &str = "either `none`, or a comma separated list of location details to track: `file`, `line`, or `column`";
439+
pub(crate) const parse_location_detail: &str = "either `none`, or a comma separated list of location details to track: `file`, `line`, `column` or `cstr`";
440440
pub(crate) const parse_fmt_debug: &str = "either `full`, `shallow`, or `none`";
441441
pub(crate) const parse_switch_with_opt_path: &str =
442442
"an optional path to the profiling data output directory";
@@ -647,6 +647,7 @@ pub mod parse {
647647
ld.line = false;
648648
ld.file = false;
649649
ld.column = false;
650+
ld.cstr = false;
650651
if v == "none" {
651652
return true;
652653
}
@@ -655,6 +656,7 @@ pub mod parse {
655656
"file" => ld.file = true,
656657
"line" => ld.line = true,
657658
"column" => ld.column = true,
659+
"cstr" => ld.cstr = true,
658660
_ => return false,
659661
}
660662
}
@@ -1904,8 +1906,8 @@ options! {
19041906
"generate JSON tracing data file from LLVM data (default: no)"),
19051907
location_detail: LocationDetail = (LocationDetail::all(), parse_location_detail, [TRACKED],
19061908
"what location details should be tracked when using caller_location, either \
1907-
`none`, or a comma separated list of location details, for which \
1908-
valid options are `file`, `line`, and `column` (default: `file,line,column`)"),
1909+
`none`, or a comma separated list of location details, for which valid \
1910+
options are `file`, `line`, `column`, and `cstr` (default: `file,line,column`)"),
19091911
ls: Vec<String> = (Vec::new(), parse_list, [UNTRACKED],
19101912
"decode and print various parts of the crate metadata for a library crate \
19111913
(space separated)"),

src/doc/unstable-book/src/compiler-flags/location-detail.md

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ within this list are:
1616
- `file` - the filename of the panic will be included in the panic output
1717
- `line` - the source line of the panic will be included in the panic output
1818
- `column` - the source column of the panic will be included in the panic output
19+
- `cstr` - whether the location strings should be nul-terminated
1920

2021
Any combination of these three options are supported. Alternatively, you can pass
2122
`none` to this option, which results in no location details being tracked.
@@ -40,5 +41,10 @@ and column information is identical for all panics, these branches can be fused,
4041
can lead to substantial code size savings, especially for small embedded binaries with
4142
many panics.
4243

44+
Although the `cstr` option makes location strings one byte longer, some linkers have
45+
functionality for deduplicating nul-terminated strings, so using this option may decrease
46+
binary size under some circumstances. Some applications may also use this option to pass
47+
location strings into C or C++ code that relies on nul-terminated strings.
48+
4349
The savings from this option are amplified when combined with the use of `-Zbuild-std`, as
4450
otherwise paths for panics within the standard library are still included in your binary.

tests/ui/panics/location-cstr.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//@ run-pass
2+
//@ compile-flags: -Zlocation-detail=line,file,cstr
3+
4+
use std::panic::Location;
5+
6+
fn main() {
7+
let location = Location::caller();
8+
let len = location.file().len();
9+
let ptr = location.file().as_ptr();
10+
11+
let zero_terminator = unsafe { core::ptr::read(ptr.add(len)) };
12+
assert_eq!(zero_terminator, 0u8);
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//@ run-fail
2+
//@ check-run-results
3+
//@ compile-flags: -Zlocation-detail=line,column,file,cstr
4+
//@ exec-env:RUST_BACKTRACE=0
5+
6+
fn main() {
7+
panic!("with-nul-terminator");
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
thread 'main' panicked at $DIR/location-detail-panic-cstr.rs:7:5:
3+
with-nul-terminator
4+
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

0 commit comments

Comments
 (0)