Skip to content

Commit aca0c42

Browse files
authored
Add support for DW_LNCT_LLVM_source (gimli-rs#728)
1 parent 11c6544 commit aca0c42

File tree

3 files changed

+118
-5
lines changed

3 files changed

+118
-5
lines changed

src/constants.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,10 @@ DwLnct(u16) {
10521052
DW_LNCT_timestamp = 0x3,
10531053
DW_LNCT_size = 0x4,
10541054
DW_LNCT_MD5 = 0x5,
1055+
// DW_LNCT_source = 0x6,
10551056
DW_LNCT_lo_user = 0x2000,
1057+
// We currently only implement the LLVM embedded source code extension for DWARF v5.
1058+
DW_LNCT_LLVM_source = 0x2001,
10561059
DW_LNCT_hi_user = 0x3fff,
10571060
});
10581061

src/read/line.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1224,6 +1224,13 @@ where
12241224
.any(|x| x.content_type == constants::DW_LNCT_MD5)
12251225
}
12261226

1227+
/// Return true if the file name entry format contains a source field.
1228+
pub fn file_has_source(&self) -> bool {
1229+
self.file_name_entry_format
1230+
.iter()
1231+
.any(|x| x.content_type == constants::DW_LNCT_LLVM_source)
1232+
}
1233+
12271234
/// Get the list of source files that appear in this header's line program.
12281235
pub fn file_names(&self) -> &[FileEntry<R, Offset>] {
12291236
&self.file_names[..]
@@ -1380,6 +1387,7 @@ where
13801387
timestamp: 0,
13811388
size: 0,
13821389
md5: [0; 16],
1390+
source: None,
13831391
});
13841392

13851393
file_name_entry_format = Vec::new();
@@ -1579,6 +1587,7 @@ where
15791587
timestamp: u64,
15801588
size: u64,
15811589
md5: [u8; 16],
1590+
source: Option<AttributeValue<R, Offset>>,
15821591
}
15831592

15841593
impl<R, Offset> FileEntry<R, Offset>
@@ -1598,6 +1607,7 @@ where
15981607
timestamp,
15991608
size,
16001609
md5: [0; 16],
1610+
source: None,
16011611
};
16021612

16031613
Ok(entry)
@@ -1667,6 +1677,16 @@ where
16671677
pub fn md5(&self) -> &[u8; 16] {
16681678
&self.md5
16691679
}
1680+
1681+
/// The source code of this file. (UTF-8 source text string with "\n" line
1682+
/// endings).
1683+
///
1684+
/// Note: For DWARF v5 files this may return an empty attribute that
1685+
/// indicates that no source code is available, which this function
1686+
/// represents as Some(<zero-length attr>).
1687+
pub fn source(&self) -> Option<AttributeValue<R, Offset>> {
1688+
self.source.clone()
1689+
}
16701690
}
16711691

16721692
/// The format of a component of an include directory or file name entry.
@@ -1733,6 +1753,7 @@ fn parse_file_v5<R: Reader>(
17331753
let mut timestamp = 0;
17341754
let mut size = 0;
17351755
let mut md5 = [0; 16];
1756+
let mut source = None;
17361757

17371758
for format in formats {
17381759
let value = parse_attribute(input, encoding, format.form)?;
@@ -1760,6 +1781,9 @@ fn parse_file_v5<R: Reader>(
17601781
}
17611782
}
17621783
}
1784+
constants::DW_LNCT_LLVM_source => {
1785+
source = Some(value);
1786+
}
17631787
// Ignore unknown content types.
17641788
_ => {}
17651789
}
@@ -1771,6 +1795,7 @@ fn parse_file_v5<R: Reader>(
17711795
timestamp,
17721796
size,
17731797
md5,
1798+
source,
17741799
})
17751800
}
17761801

@@ -1984,13 +2009,15 @@ mod tests {
19842009
timestamp: 0,
19852010
size: 0,
19862011
md5: [0; 16],
2012+
source: None,
19872013
},
19882014
FileEntry {
19892015
path_name: AttributeValue::String(EndianSlice::new(b"bar.h", LittleEndian)),
19902016
directory_index: 1,
19912017
timestamp: 0,
19922018
size: 0,
19932019
md5: [0; 16],
2020+
source: None,
19942021
},
19952022
];
19962023
assert_eq!(header.file_names(), &expected_file_names);
@@ -2149,13 +2176,15 @@ mod tests {
21492176
timestamp: 0,
21502177
size: 0,
21512178
md5: [0; 16],
2179+
source: None,
21522180
},
21532181
FileEntry {
21542182
path_name: AttributeValue::String(EndianSlice::new(b"bar.rs", LittleEndian)),
21552183
directory_index: 0,
21562184
timestamp: 0,
21572185
size: 0,
21582186
md5: [0; 16],
2187+
source: None,
21592188
},
21602189
],
21612190
include_directories: vec![],
@@ -2402,6 +2431,7 @@ mod tests {
24022431
timestamp: 1,
24032432
size: 2,
24042433
md5: [0; 16],
2434+
source: None,
24052435
}),
24062436
);
24072437

@@ -2425,6 +2455,7 @@ mod tests {
24252455
timestamp: 0,
24262456
size: 0,
24272457
md5: [0; 16],
2458+
source: None,
24282459
};
24292460

24302461
let mut header = make_test_header(EndianSlice::new(&[], LittleEndian));
@@ -2853,6 +2884,7 @@ mod tests {
28532884
timestamp: 0,
28542885
size: 0,
28552886
md5: [0; 16],
2887+
source: None,
28562888
};
28572889

28582890
let opcode = LineInstruction::DefineFile(file);
@@ -2914,6 +2946,10 @@ mod tests {
29142946
timestamp: 0,
29152947
size: 0,
29162948
md5: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
2949+
source: Some(AttributeValue::String(EndianSlice::new(
2950+
b"foobar",
2951+
LittleEndian,
2952+
))),
29172953
},
29182954
FileEntry {
29192955
path_name: AttributeValue::String(EndianSlice::new(b"file2", LittleEndian)),
@@ -2923,6 +2959,10 @@ mod tests {
29232959
md5: [
29242960
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
29252961
],
2962+
source: Some(AttributeValue::String(EndianSlice::new(
2963+
b"quux",
2964+
LittleEndian,
2965+
))),
29262966
},
29272967
];
29282968

@@ -2965,21 +3005,25 @@ mod tests {
29653005
.append_bytes(b"dir1\0")
29663006
.append_bytes(b"dir2\0")
29673007
// File entry format count.
2968-
.D8(3)
3008+
.D8(4)
29693009
.uleb(constants::DW_LNCT_path.0 as u64)
29703010
.uleb(constants::DW_FORM_string.0 as u64)
29713011
.uleb(constants::DW_LNCT_directory_index.0 as u64)
29723012
.uleb(constants::DW_FORM_data1.0 as u64)
29733013
.uleb(constants::DW_LNCT_MD5.0 as u64)
29743014
.uleb(constants::DW_FORM_data16.0 as u64)
3015+
.uleb(constants::DW_LNCT_LLVM_source.0 as u64)
3016+
.uleb(constants::DW_FORM_string.0 as u64)
29753017
// File count.
29763018
.D8(2)
29773019
.append_bytes(b"file1\0")
29783020
.D8(0)
29793021
.append_bytes(&expected_file_names[0].md5)
3022+
.append_bytes(b"foobar\0")
29803023
.append_bytes(b"file2\0")
29813024
.D8(1)
29823025
.append_bytes(&expected_file_names[1].md5)
3026+
.append_bytes(b"quux\0")
29833027
.mark(&header_end)
29843028
// Dummy line program data.
29853029
.append_bytes(expected_program)
@@ -3031,6 +3075,10 @@ mod tests {
30313075
FileEntryFormat {
30323076
content_type: constants::DW_LNCT_MD5,
30333077
form: constants::DW_FORM_data16,
3078+
},
3079+
FileEntryFormat {
3080+
content_type: constants::DW_LNCT_LLVM_source,
3081+
form: constants::DW_FORM_string,
30343082
}
30353083
]
30363084
);

src/write/line.rs

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ pub struct LineProgram {
6767
/// For version 5, this controls whether to emit `DW_LNCT_MD5`.
6868
pub file_has_md5: bool,
6969

70+
/// True if the file entries have embedded source code.
71+
///
72+
/// For version <= 4, this is ignored.
73+
/// For version 5, this controls whether to emit `DW_LNCT_LLVM_source`.
74+
pub file_has_source: bool,
75+
7076
prev_row: LineRow,
7177
row: LineRow,
7278
// TODO: this probably should be either rows or sequences instead
@@ -119,6 +125,7 @@ impl LineProgram {
119125
file_has_timestamp: false,
120126
file_has_size: false,
121127
file_has_md5: false,
128+
file_has_source: false,
122129
};
123130
// For all DWARF versions, directory index 0 is comp_dir.
124131
// For version <= 4, the entry is implicit. We still add
@@ -153,6 +160,7 @@ impl LineProgram {
153160
file_has_timestamp: false,
154161
file_has_size: false,
155162
file_has_md5: false,
163+
file_has_source: false,
156164
}
157165
}
158166

@@ -592,7 +600,8 @@ impl LineProgram {
592600
let count = 2
593601
+ if self.file_has_timestamp { 1 } else { 0 }
594602
+ if self.file_has_size { 1 } else { 0 }
595-
+ if self.file_has_md5 { 1 } else { 0 };
603+
+ if self.file_has_md5 { 1 } else { 0 }
604+
+ if self.file_has_source { 1 } else { 0 };
596605
w.write_u8(count)?;
597606
w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?;
598607
let file_form = self.comp_file.0.form();
@@ -611,6 +620,10 @@ impl LineProgram {
611620
w.write_uleb128(u64::from(constants::DW_LNCT_MD5.0))?;
612621
w.write_uleb128(constants::DW_FORM_data16.0.into())?;
613622
}
623+
if self.file_has_source {
624+
w.write_uleb128(u64::from(constants::DW_LNCT_LLVM_source.0))?;
625+
w.write_uleb128(constants::DW_FORM_string.0.into())?;
626+
}
614627

615628
// File name entries.
616629
w.write_uleb128(self.files.len() as u64 + 1)?;
@@ -632,6 +645,20 @@ impl LineProgram {
632645
if self.file_has_md5 {
633646
w.write(&info.md5)?;
634647
}
648+
if self.file_has_source {
649+
// Note: An empty DW_LNCT_LLVM_source is interpreted as missing
650+
// source code. Included source code should always be
651+
// terminated by a "\n" line ending.
652+
let empty_str = LineString::String(Vec::new());
653+
let source = info.source.as_ref().unwrap_or(&empty_str);
654+
source.write(
655+
w,
656+
constants::DW_FORM_string,
657+
self.encoding,
658+
debug_line_str_offsets,
659+
debug_str_offsets,
660+
)?;
661+
}
635662
Ok(())
636663
};
637664
write_file(&self.comp_file.0, DirectoryId(0), &self.comp_file.1)?;
@@ -937,7 +964,7 @@ mod id {
937964
pub use self::id::*;
938965

939966
/// Extra information for file in a `LineProgram`.
940-
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
967+
#[derive(Debug, Default, Clone, PartialEq, Eq)]
941968
pub struct FileInfo {
942969
/// The implementation defined timestamp of the last modification of the file,
943970
/// or 0 if not available.
@@ -950,6 +977,15 @@ pub struct FileInfo {
950977
///
951978
/// Only used if version >= 5 and `LineProgram::file_has_md5` is `true`.
952979
pub md5: [u8; 16],
980+
981+
/// Optionally some embedded sourcecode.
982+
///
983+
/// Only used if version >= 5 and `LineProgram::file_has_source` is `true`.
984+
///
985+
/// NOTE: This currently only supports the `LineString::String` variant,
986+
/// since we're encoding the string with `DW_FORM_string`.
987+
/// Other variants will result in an `LineStringFormMismatch` error.
988+
pub source: Option<LineString>,
953989
}
954990

955991
define_section!(
@@ -999,6 +1035,15 @@ mod convert {
9991035
timestamp: comp_file.timestamp(),
10001036
size: comp_file.size(),
10011037
md5: *comp_file.md5(),
1038+
source: match comp_file.source() {
1039+
Some(source) => Some(LineString::from(
1040+
source,
1041+
dwarf,
1042+
line_strings,
1043+
strings,
1044+
)?),
1045+
None => None,
1046+
},
10021047
}),
10031048
)
10041049
}
@@ -1040,6 +1085,7 @@ mod convert {
10401085
program.file_has_timestamp = from_header.file_has_timestamp();
10411086
program.file_has_size = from_header.file_has_size();
10421087
program.file_has_md5 = from_header.file_has_md5();
1088+
program.file_has_source = from_header.file_has_source();
10431089
for from_file in from_header.file_names().iter().skip(file_skip) {
10441090
let from_name =
10451091
LineString::from(from_file.path_name(), dwarf, line_strings, strings)?;
@@ -1052,6 +1098,12 @@ mod convert {
10521098
timestamp: from_file.timestamp(),
10531099
size: from_file.size(),
10541100
md5: *from_file.md5(),
1101+
source: match from_file.source() {
1102+
Some(source) => {
1103+
Some(LineString::from(source, dwarf, line_strings, strings)?)
1104+
}
1105+
None => None,
1106+
},
10551107
});
10561108
files.push(program.add_file(from_name, from_dir, from_info));
10571109
}
@@ -1190,6 +1242,13 @@ mod tests {
11901242
program.file_has_md5 = true;
11911243
}
11921244

1245+
// Note: Embedded source code is an accepted extension
1246+
// that will become part of DWARF v6. We're using the LLVM extension
1247+
// here for v5.
1248+
if encoding.version >= 5 {
1249+
program.file_has_source = true;
1250+
}
1251+
11931252
let dir_id = program.add_directory(dir2.clone());
11941253
assert_eq!(&dir2, program.get_directory(dir_id));
11951254
assert_eq!(dir_id, program.add_directory(dir2.clone()));
@@ -1202,8 +1261,11 @@ mod tests {
12021261
} else {
12031262
[0; 16]
12041263
},
1264+
source: (encoding.version >= 5)
1265+
.then(|| LineString::String(b"the source code\n".to_vec())),
12051266
};
1206-
let file_id = program.add_file(file2.clone(), dir_id, Some(file_info));
1267+
let file_id =
1268+
program.add_file(file2.clone(), dir_id, Some(file_info.clone()));
12071269
assert_eq!((&file2, dir_id), program.get_file(file_id));
12081270
assert_eq!(file_info, *program.get_file_info(file_id));
12091271

@@ -1213,7 +1275,7 @@ mod tests {
12131275
assert_ne!(file_info, *program.get_file_info(file_id));
12141276
assert_eq!(
12151277
file_id,
1216-
program.add_file(file2.clone(), dir_id, Some(file_info))
1278+
program.add_file(file2.clone(), dir_id, Some(file_info.clone()))
12171279
);
12181280
assert_eq!(file_info, *program.get_file_info(file_id));
12191281

0 commit comments

Comments
 (0)