ldscript_parser/
sections.rs

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}