1use commands::{command, Command};
2use expressions::expression;
3use expressions::Expression;
4use idents::pattern;
5use idents::symbol;
6use nom::branch::alt;
7use nom::bytes::complete::tag;
8use nom::combinator::cut;
9use nom::combinator::map;
10use nom::combinator::opt;
11use nom::multi::many0;
12use nom::multi::many1;
13use nom::sequence::delimited;
14use nom::sequence::preceded;
15use nom::sequence::tuple;
16use nom::IResult;
17use statements::{statement, Statement};
18use whitespace::opt_space;
19
20#[derive(Debug, PartialEq)]
21pub enum SectionCommand {
22 Statement(Statement),
23 Command(Command),
24 OutputSection {
25 name: String,
26 vma_address: Option<Box<Expression>>,
27 s_type: Option<OutputSectionType>,
28 lma_address: Option<Box<Expression>>,
29 section_align: Option<Box<Expression>>,
30 align_with_input: bool,
31 subsection_align: Option<Box<Expression>>,
32 constraint: Option<OutputSectionConstraint>,
33 content: Vec<OutputSectionCommand>,
34 region: Option<String>,
35 lma_region: Option<String>,
36 fillexp: Option<Box<Expression>>,
37 },
38}
39
40#[derive(Debug, PartialEq)]
41pub enum OutputSectionCommand {
42 Statement(Statement),
43 Fill {
44 expr: Box<Expression>,
45 },
46 Data {
47 d_type: DataType,
48 value: Box<Expression>,
49 },
50 InputSection {
51 file: SectionPattern,
52 sections: Vec<SectionPattern>,
53 },
54 KeepInputSection {
55 file: SectionPattern,
56 sections: Vec<SectionPattern>,
57 },
58}
59
60#[derive(Debug, PartialEq)]
61pub enum DataType {
62 Byte,
63 Short,
64 Long,
65 Quad,
66}
67
68#[derive(Debug, PartialEq)]
69pub enum SectionPattern {
70 Simple(String),
71 SortByName(String),
72 SortByAlignment(String),
73 SortByInitPriority(String),
74 SortNone(String),
75 ExcludeFile {
76 files: Vec<String>,
77 pattern: Box<SectionPattern>,
78 },
79}
80
81#[derive(Debug, PartialEq)]
82pub enum OutputSectionType {
83 NoLoad,
84 DSect,
85 Copy,
86 Info,
87 Overlay,
88}
89
90#[derive(Debug, PartialEq)]
91pub enum OutputSectionConstraint {
92 OnlyIfRo,
93 OnlyIfRw,
94}
95
96fn output_section_type(input: &str) -> IResult<&str, OutputSectionType> {
97 alt((
98 map(tag("(NOLOAD)"), |_| OutputSectionType::NoLoad),
99 map(tag("(DSECT)"), |_| OutputSectionType::DSect),
100 map(tag("(COPY)"), |_| OutputSectionType::Copy),
101 map(tag("(INFO)"), |_| OutputSectionType::Info),
102 map(tag("(OVERLAY)"), |_| OutputSectionType::Overlay),
103 ))(input)
104}
105
106fn output_section_constraint(input: &str) -> IResult<&str, OutputSectionConstraint> {
107 alt((
108 map(tag("ONLY_IF_RO"), |_| OutputSectionConstraint::OnlyIfRo),
109 map(tag("ONLY_IF_RW"), |_| OutputSectionConstraint::OnlyIfRw),
110 ))(input)
111}
112
113fn sorted_sp(input: &str) -> IResult<&str, SectionPattern> {
114 let (input, keyword) = alt((
115 tag("SORT_BY_NAME"),
116 tag("SORT_BY_ALIGNMENT"),
117 tag("SORT_BY_INIT_PRIORITY"),
118 tag("SORT_NONE"),
119 tag("SORT"),
120 ))(input)?;
121 let (input, _) = cut(wsc!(tag("(")))(input)?;
122 let (input, inner) = cut(pattern)(input)?;
123 let (input, _) = cut(opt_space)(input)?;
124 let (input, _) = cut(tag(")"))(input)?;
125 Ok((
126 input,
127 match keyword {
128 "SORT" | "SORT_BY_NAME" => SectionPattern::SortByName(inner.into()),
129 "SORT_BY_ALIGNMENT" => SectionPattern::SortByAlignment(inner.into()),
130 "SORT_BY_INIT_PRIORITY" => SectionPattern::SortByInitPriority(inner.into()),
131 "SORT_NONE" => SectionPattern::SortNone(inner.into()),
132 _ => panic!("wrong sort keyword"),
133 },
134 ))
135}
136
137fn exclude_file_sp(input: &str) -> IResult<&str, SectionPattern> {
138 let (input, _) = tuple((tag("EXCLUDE_FILE"), opt_space, tag("(")))(input)?;
139 let (input, files) = cut(many1(wsc!(map(pattern, String::from))))(input)?;
140 let (input, _) = cut(tuple((tag(")"), opt_space)))(input)?;
141 let (input, inner) = cut(section_pattern)(input)?;
142 Ok((
143 input,
144 SectionPattern::ExcludeFile {
145 files: files,
146 pattern: Box::new(inner),
147 },
148 ))
149}
150
151fn simple_sp(input: &str) -> IResult<&str, SectionPattern> {
152 map(pattern, |x: &str| SectionPattern::Simple(x.into()))(input)
153}
154
155fn section_pattern(input: &str) -> IResult<&str, SectionPattern> {
156 alt((exclude_file_sp, sorted_sp, simple_sp))(input)
157}
158
159fn data_osc(input: &str) -> IResult<&str, OutputSectionCommand> {
160 let (input, d_type) = alt((tag("BYTE"), tag("SHORT"), tag("LONG"), tag("QUAD")))(input)?;
161 let (input, _) = wsc!(tag("("))(input)?;
162 let (input, value) = expression(input)?;
163 let (input, _) = tuple((wsc!(tag(")")), opt(tag(";"))))(input)?;
164 Ok((
165 input,
166 OutputSectionCommand::Data {
167 d_type: match d_type {
168 "BYTE" => DataType::Byte,
169 "SHORT" => DataType::Short,
170 "LONG" => DataType::Long,
171 "QUAD" => DataType::Quad,
172 _ => panic!("invalid data type"),
173 },
174 value: Box::new(value),
175 },
176 ))
177}
178
179fn fill_osc(input: &str) -> IResult<&str, OutputSectionCommand> {
180 let (input, _) = tuple((tag("FILL"), wsc!(tag("("))))(input)?;
181 let (input, expr) = expression(input)?;
182 let (input, _) = tuple((wsc!(tag(")")), opt(tag(";"))))(input)?;
183 Ok((
184 input,
185 OutputSectionCommand::Fill {
186 expr: Box::new(expr),
187 },
188 ))
189}
190
191fn statement_osc(input: &str) -> IResult<&str, OutputSectionCommand> {
192 map(statement, |stmt| OutputSectionCommand::Statement(stmt))(input)
193}
194
195fn input_osc(input: &str) -> IResult<&str, OutputSectionCommand> {
196 let (input, file) = section_pattern(input)?;
197 let (input, _) = opt_space(input)?;
198 let (input, sections) = opt(delimited(
199 wsc!(tag("(")),
200 many1(wsc!(section_pattern)),
201 wsc!(tag(")")),
202 ))(input)?;
203 Ok((
204 input,
205 OutputSectionCommand::InputSection {
206 file: file,
207 sections: match sections {
208 Some(s) => s,
209 None => Vec::new(),
210 },
211 },
212 ))
213}
214
215fn keep_osc(input: &str) -> IResult<&str, OutputSectionCommand> {
216 let (input, _) = tuple((tag("KEEP"), wsc!(tag("("))))(input)?;
217 let (input, inner) = input_osc(input)?;
218 let (input, _) = wsc!(tag(")"))(input)?;
219 Ok((
220 input,
221 match inner {
222 OutputSectionCommand::InputSection { file, sections } => {
223 OutputSectionCommand::KeepInputSection {
224 file: file,
225 sections: sections,
226 }
227 }
228 _ => panic!("wrong output section command"),
229 },
230 ))
231}
232
233fn output_section_command(input: &str) -> IResult<&str, OutputSectionCommand> {
234 alt((statement_osc, keep_osc, data_osc, fill_osc, input_osc))(input)
235}
236
237fn statement_sc(input: &str) -> IResult<&str, SectionCommand> {
238 map(statement, |stmt| SectionCommand::Statement(stmt))(input)
239}
240
241fn command_sc(input: &str) -> IResult<&str, SectionCommand> {
242 map(command, |cmd| SectionCommand::Command(cmd))(input)
243}
244
245fn output_sc(input: &str) -> IResult<&str, SectionCommand> {
246 let (input, name) = alt((tag("/DISCARD/"), symbol))(input)?;
247 let (input, _) = opt_space(input)?;
248 let (input, s_type1) = opt(output_section_type)(input)?;
249 let (input, vma) = wsc!(opt(expression))(input)?;
250 let (input, s_type2) = opt(output_section_type)(input)?;
251 let (input, _) = wsc!(tag(":"))(input)?;
252 let (input, lma) = opt(delimited(tag("AT("), wsc!(expression), tag(")")))(input)?;
253 let (input, _) = opt_space(input)?;
254 let (input, section_align) = opt(delimited(tag("ALIGN("), wsc!(expression), tag(")")))(input)?;
255 let (input, align_with_input) = wsc!(opt(tag("ALIGN_WITH_INPUT")))(input)?;
256 let (input, subsection_align) =
257 opt(delimited(tag("SUBALIGN("), wsc!(expression), tag(")")))(input)?;
258 let (input, constraint) = wsc!(opt(output_section_constraint))(input)?;
259 let (input, _) = wsc!(tag("{"))(input)?;
260 let (input, content) = many0(wsc!(output_section_command))(input)?;
261 let (input, _) = wsc!(tag("}"))(input)?;
262 let (input, region) = opt(preceded(tag(">"), wsc!(symbol)))(input)?;
263 let (input, lma_region) = opt(preceded(tag("AT>"), wsc!(symbol)))(input)?;
264 let (input, fillexp) = opt(preceded(tag("="), wsc!(expression)))(input)?;
265 let (input, _) = opt(tag(","))(input)?;
266 Ok((
267 input,
268 SectionCommand::OutputSection {
269 name: name.into(),
270 vma_address: vma.map(Box::new),
271 s_type: if s_type1.is_some() { s_type1 } else { s_type2 },
272 lma_address: lma.map(Box::new),
273 section_align: section_align.map(Box::new),
274 align_with_input: align_with_input.is_some(),
275 subsection_align: subsection_align.map(Box::new),
276 constraint: constraint,
277 content: content,
278 region: region.map(String::from),
279 lma_region: lma_region.map(String::from),
280 fillexp: fillexp.map(Box::new),
281 },
282 ))
283}
284
285pub fn section_command(input: &str) -> IResult<&str, SectionCommand> {
286 alt((statement_sc, output_sc, command_sc))(input)
287}
288
289#[cfg(test)]
290mod tests {
291 use sections::*;
292
293 #[test]
294 fn test_section_command() {
295 assert_fail!(section_pattern("EXCLUDE_FILE (*a)"));
296 assert_fail!(input_osc("EXCLUDE_FILE (*a)"));
297 assert_done!(section_pattern("EXCLUDE_FILE ( *a *b ) .c"));
298 assert_done!(input_osc("EXCLUDE_FILE ( *a *b ) *c"));
299
300 assert_fail!(input_osc("EXCLUDE_FILE ( EXCLUDE_FILE ( *a *b ) *c ) .d"));
301 assert_done!(input_osc("EXCLUDE_FILE ( *a ) *b ( .c )"));
302 assert_done!(input_osc("EXCLUDE_FILE ( *a ) *b ( .c .d )"));
303 assert_done!(input_osc(
304 "EXCLUDE_FILE ( *a ) *b ( .c EXCLUDE_FILE ( *a ) .d )",
305 ));
306
307 assert_done!(output_section_command("[A-Z]*(.data)"));
308 assert_done!(output_section_command(
309 "LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)",
310 ));
311 assert_done!(output_section_command(
312 "EXCLUDE_FILE (*crtend.o *otherfile.o) *(.ctors)",
313 ));
314 assert_done!(output_section_command(
315 "*(EXCLUDE_FILE (*crtend.o *otherfile.o) .ctors)",
316 ));
317 assert_done!(output_section_command(
318 "*(EXCLUDE_FILE (*a) .text EXCLUDE_FILE (*b) .c)",
319 ));
320 assert_done!(output_section_command("KEEP(SORT_BY_NAME(*)(.ctors))"));
321 assert_done!(output_section_command("PROVIDE (__init_array_end = .);"));
322 assert_done!(output_section_command("LONG(0);"));
323 assert_done!(output_section_command("SORT(CONSTRUCTORS)"));
324 assert_done!(output_section_command("*"));
325
326 assert_done!(statement_osc("ASSERT(SIZEOF(.upper)==0,\"Test\");"));
327 assert_done!(output_section_command(
328 "ASSERT(SIZEOF(.upper)==0,\"Test\");",
329 ));
330 assert_done!(output_section_command("FILL(0xff);"));
331
332 assert_done!(output_sc("/DISCARD/ : { *(.note.GNU-stack) }"));
333 assert_done!(output_sc(".DATA : { [A-Z]*(.data) }"));
334 assert_done!(output_sc(".infoD : {} > INFOD"));
335
336 assert_done!(output_sc(".a:{*(.b .c)*(.d .e)}"));
337 }
338}