1use std::{
2 io::Write,
3 process::{
4 exit,
5 Command,
6 },
7};
8
9use crate::{
10 asm::separate_statements,
11 getopt::{
12 GetoptParser,
13 OptVal,
14 },
15 listing::EXTENSION,
16};
17
18const OPTION_BLACKLIST: [&str; 1] = ["-mrelax"];
19
20#[derive(Debug)]
21pub struct AsWrapper {
22 args: Vec<String>,
23 inputs: Vec<String>,
24 output: String,
25}
26
27impl AsWrapper {
28 pub fn from_cmdline(args: Vec<String>) -> Option<Self> {
29 let parser = GetoptParser::new()
30 .optstring("o:JKLMRWZa::Dg::I:vwXt:")
31 .optstring("O::g::G:")
32 .long("alternate", OptVal::None, None)
33 .long("compress-debug-sections", OptVal::Optional, None)
34 .long("nocompress-debug-sections", OptVal::None, None)
35 .long("debug-prefix-map", OptVal::Required, None)
36 .long("defsym", OptVal::Required, None)
37 .long("dump-config", OptVal::None, None)
38 .long("emulation", OptVal::Required, None)
39 .long("execstack", OptVal::None, None)
40 .long("noexecstack", OptVal::None, None)
41 .long("size-check", OptVal::Required, None)
42 .long("elf-stt-common", OptVal::Required, None)
43 .long("sectname-subst", OptVal::None, None)
44 .long("generate-missing-build-notes", OptVal::Required, None)
45 .long("fatal-warnings", OptVal::None, None)
46 .long("gdwarf-cie-version", OptVal::Required, None)
47 .long("gen-debug", OptVal::None, None)
48 .long("gstabs", OptVal::None, None)
49 .long("gstabs+", OptVal::None, None)
50 .long("gdwarf-2", OptVal::None, None)
51 .long("gdwarf-3", OptVal::None, None)
52 .long("gdwarf-4", OptVal::None, None)
53 .long("gdwarf-5", OptVal::None, None)
54 .long("gdwarf-sections", OptVal::None, None)
55 .long("hash-size", OptVal::Required, None)
56 .long("help", OptVal::None, None)
57 .long("itbl", OptVal::Required, None)
58 .long("keep-locals", OptVal::None, None)
59 .long("listing-lhs-width", OptVal::Required, None)
60 .long("listing-lhs-width2", OptVal::Required, None)
61 .long("listing-rhs-width", OptVal::Required, None)
62 .long("listing-cont-lines", OptVal::Required, None)
63 .long("MD", OptVal::Required, None)
64 .long("mri", OptVal::None, None)
65 .long("nocpp", OptVal::None, None)
66 .long("no-pad-sections", OptVal::None, None)
67 .long("no-warn", OptVal::None, None)
68 .long("reduce-memory-overheads", OptVal::None, None)
69 .long("statistics", OptVal::None, None)
70 .long("strip-local-absolute", OptVal::None, None)
71 .long("version", OptVal::None, None)
72 .long("verbose", OptVal::None, None)
73 .long("target-help", OptVal::None, None)
74 .long("traditional-format", OptVal::None, None)
75 .long("warn", OptVal::None, None)
76 .long("multibyte-handling", OptVal::Required, None)
77 .long("march", OptVal::Required, None)
78 .short('f', OptVal::Required, None)
79 .long("mabi", OptVal::Required, None)
80 .long("misa-spec", OptVal::Required, None)
81 .long("mpriv-spec", OptVal::Required, None)
82 .short('m', OptVal::Required, None);
83
84 let cmdline = parser.parse_long_only(&args).unwrap();
85 let output = if let Some(output) = cmdline.arg_value('o') {
86 output.to_string()
87 } else {
88 return None;
89 };
90 let mut inputs = Vec::new();
91
92 for input in cmdline.positionals() {
93 if *input != "-" {
94 inputs.push(input.to_string());
95 }
96 }
97
98 Some(Self {
99 args,
100 inputs,
101 output,
102 })
103 }
104
105 pub fn preprocess(&self) {
106 for filename in &self.inputs {
107 let input = std::fs::read(filename).unwrap();
108 let output = separate_statements(&input, filename);
109 let mut file = std::fs::OpenOptions::new().create(true).truncate(true).write(true).open(filename).unwrap();
110 file.write_all(&output).unwrap();
111 }
112 }
113
114 pub fn compile(&self) {
115 let listing = format!("{}.{}", self.output, EXTENSION);
116 let status = Command::new(&self.args[0])
117 .arg(format!("-almd={listing}"))
118 .args(self.args[1..].iter().filter(|x| !OPTION_BLACKLIST.contains(&x.as_str())))
119 .arg("-mno-relax")
120 .envs(std::env::vars())
121 .status()
122 .unwrap();
123
124 if let Some(code) = status.code() {
125 if code != 0 {
126 exit(code);
127 }
128 } else {
129 exit(-1);
130 }
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn test_assembly_preprocessing() {
140 let output = separate_statements(
141 b".file \":;\\\"\\\\\"# single-line comment\n/* multi-line\ncomment */.L0:.directive;inst;\n",
142 "<test>",
143 );
144 println!("{}", std::str::from_utf8(&output).unwrap());
145 }
146
147 #[test]
148 fn test_as_wrapper1() {
149 let wrapper = AsWrapper::from_cmdline(vec!["as".to_string(), "--help".to_string()]);
150 assert!(wrapper.is_none());
151 }
152
153 #[test]
154 fn test_as_wrapper2() {
155 let wrapper = AsWrapper::from_cmdline(vec![
156 "as".to_string(),
157 "-fpic".to_string(),
158 "--traditional-format".to_string(),
159 "-march=rv64imafd".to_string(),
160 "-mabi=lp64d".to_string(),
161 "-misa-spec=2.2".to_string(),
162 "-o".to_string(),
163 "/tmp/cc5hz51Q.o".to_string(),
164 "/tmp/ccNyPyiT.s".to_string(),
165 "-".to_string(),
166 ])
167 .unwrap();
168 assert_eq!(wrapper.inputs, &["/tmp/ccNyPyiT.s"]);
169 assert_eq!(wrapper.output, "/tmp/cc5hz51Q.o");
170 }
171
172 #[test]
173 fn test_glibc() {
174 let wrapper = AsWrapper::from_cmdline(vec![
175 "as".to_string(),
176 "-I".to_string(),
177 "../include".to_string(),
178 "--gdwarf-5".to_string(),
179 "--traditional-format".to_string(),
180 "-fpic".to_string(),
181 "-march=rv64imafd".to_string(),
182 "-march=rv64imafd".to_string(),
183 "-mabi=lp64d".to_string(),
184 "-misa-spec=2.2".to_string(),
185 "-o".to_string(),
186 "/io/test-data/glibc/build/libio/clearerr.os".to_string(),
187 "/tmp/ccJfUCIc.s".to_string(),
188 ])
189 .unwrap();
190 println!("{:?}", wrapper);
191 }
192}