forked from utdscheld/name
-
Notifications
You must be signed in to change notification settings - Fork 0
/
assembler.rs
280 lines (248 loc) · 11.1 KB
/
assembler.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
use std::collections::HashMap;
use std::path::PathBuf;
use name_core::constants::{MIPS_ADDRESS_ALIGNMENT, MIPS_DATA_START_ADDR, MIPS_TEXT_START_ADDR};
use name_core::elf_def::{STT_FUNC, STT_OBJECT};
use name_core::instruction::information::InstructionInformation;
use name_core::structs::{Section, Symbol, Visibility};
use crate::assembler::assemble_instruction::assemble_instruction;
use crate::assembler::assembly_helpers::{
generate_pseudo_instruction_hashmap, pretty_print_instruction,
};
use crate::definitions::constants::BACKPATCH_PLACEHOLDER;
use crate::definitions::structs::{Backpatch, BackpatchType, LineComponent, PseudoInstruction};
// This file contains the struct definition and extracted functions used in the assembler_logic file. There was far too much inlined, so I have extracted it.
#[derive(Debug)]
pub struct Assembler {
pub(crate) pseudo_instruction_table: HashMap<&'static str, &'static PseudoInstruction>,
pub section_dot_text: Vec<u8>,
pub section_dot_data: Vec<u8>,
pub section_dot_line: Vec<u8>,
pub symbol_table: Vec<Symbol>,
pub(crate) equivalences: HashMap<String, String>,
pub(crate) errors: Vec<String>,
pub(crate) backpatches: Vec<Backpatch>,
pub(crate) current_section: Section,
pub(crate) current_address: u32,
pub(crate) current_dir: PathBuf,
pub(crate) text_address: u32,
pub(crate) data_address: u32,
pub(crate) line_number: usize,
pub(crate) line_prefix: String,
pub(crate) most_recent_label: String,
}
impl Assembler {
// Initialize the assembler environment - default constructor.
pub(crate) fn new() -> Self {
Assembler {
pseudo_instruction_table: generate_pseudo_instruction_hashmap(),
section_dot_text: vec![],
section_dot_data: vec![],
section_dot_line: vec![],
symbol_table: vec![],
equivalences: HashMap::new(),
errors: vec![],
backpatches: vec![],
current_section: Section::Null,
current_address: 0,
current_dir: PathBuf::new(),
text_address: MIPS_TEXT_START_ADDR,
data_address: MIPS_DATA_START_ADDR,
line_number: 1,
line_prefix: String::from(""),
most_recent_label: String::from(""),
}
}
// Check if a target symbol exists in the symbol table.
// Returns a boolean representing if the symbol is present.
pub(crate) fn symbol_exists(&self, symbol_identifier: &String) -> bool {
let ident = symbol_identifier.clone();
self.symbol_table
.iter()
.find(|symbol| symbol.identifier == ident)
.is_some()
}
// Add a backpatch to the backpatches list. Used if a forward reference was detected to signal that it must be resolved later.
pub(crate) fn add_backpatch(
&mut self,
instruction_info: &'static InstructionInformation,
args: &Vec<LineComponent>,
ident: String,
backpatch_type: BackpatchType,
) {
match backpatch_type {
BackpatchType::Standard | BackpatchType::Upper => {
self.backpatches.push(Backpatch {
instruction_info: instruction_info,
backpatch_type: backpatch_type,
arguments: args.clone(),
undiscovered_identifier: ident,
backpatch_address: self.current_address,
byte_offset: self.section_dot_text.len(),
line_number: self.line_number,
});
}
BackpatchType::Lower => {
self.backpatches.push(Backpatch {
instruction_info: instruction_info,
backpatch_type: backpatch_type,
arguments: args.clone(),
undiscovered_identifier: ident,
backpatch_address: self.current_address + MIPS_ADDRESS_ALIGNMENT,
byte_offset: self.section_dot_text.len() + MIPS_ADDRESS_ALIGNMENT as usize,
line_number: self.line_number,
});
}
}
}
// Add a label to the symbol table.
pub(crate) fn add_label(&mut self, ident: &String) {
self.symbol_table.push(Symbol {
symbol_type: match self.current_section {
Section::Null => {
self.errors
.push(format!("[*] On line {}:", self.line_number));
self.errors
.push(" - Cannot declare label outside a section.".to_string());
0
}
Section::Text => STT_FUNC,
Section::Data => STT_OBJECT,
},
identifier: ident.to_owned(),
value: self.current_address,
size: 4,
visibility: Visibility::Local,
section: self.current_section.clone(),
});
println!("Inserted symbol {} at 0x{:x}", ident, self.current_address);
self.most_recent_label = ident.clone();
}
// Resolve all backpatches attached to a label. Used once a forward-reference label has been discovered and defined.
// FIXME: Needs to be updated to work properly with the Upper/Lower vairants of the backpatch struct.
pub(crate) fn resolve_backpatches(&mut self, ident: &String) {
let label: String = ident.clone();
loop {
let backpatch_found = self
.backpatches
.iter()
.find(|backpatch| backpatch.undiscovered_identifier == label);
let backpatch: &Backpatch;
if backpatch_found.is_none() {
break;
} else {
backpatch = backpatch_found.unwrap();
}
let backpatch_address: u32 = backpatch.backpatch_address;
let assembled_result: Result<Option<u32>, String>;
match backpatch.backpatch_type {
BackpatchType::Standard => {
assembled_result = assemble_instruction(
backpatch.instruction_info,
&backpatch.arguments,
&self.symbol_table,
&backpatch_address,
)
}
BackpatchType::Upper => {
assembled_result = Ok(Some(
// Get previously assembled instruction bytes from section .text
u32::from_be_bytes(self.section_dot_text[backpatch.byte_offset..backpatch.byte_offset+4].try_into().unwrap())
// OR in the newly found symbol's upper portion
| (self.symbol_table.iter().find(|symbol| symbol.identifier == label).unwrap().value >> 16),
))
}
BackpatchType::Lower => {
assembled_result = Ok(Some(
// Get previously assembled instruction bytes from section .text
u32::from_be_bytes(self.section_dot_text[backpatch.byte_offset..backpatch.byte_offset+4].try_into().unwrap())
// OR in the newly found symbol's lower portion
| (self.symbol_table.iter().find(|symbol| symbol.identifier == label).unwrap().value & 0xFFFF),
))
}
}
match assembled_result {
Ok(assembled_instruction) => match assembled_instruction {
Some(word) => {
let insert_offset = backpatch.byte_offset;
let bytes_to_insert = word.to_be_bytes();
self.section_dot_text.splice(
insert_offset..insert_offset + 4,
bytes_to_insert.iter().cloned(),
);
let label = backpatch.undiscovered_identifier.clone();
let line = backpatch.line_number.clone();
println!(" - Backpatch resolved for label {label} on line {line}:");
pretty_print_instruction(&backpatch.backpatch_address, &word);
let found_index = self
.backpatches
.iter()
.position(|bp| bp == backpatch)
.expect("Literally impossible to get this error.");
self.backpatches.remove(found_index);
}
None => {
unreachable!("Backpatch unable to be resolved. This indicates a stupidly difficult extension error that is likely not your fault unless you contribute to the source code.");
}
},
Err(e) => {
let found_index = self
.backpatches
.iter()
.position(|bp| bp == backpatch)
.expect("Literally impossible to get this error.");
self.errors.push(format!(
"[*] While resolving backpatch on line {}:",
backpatch.line_number
));
self.errors.push(e);
self.backpatches.remove(found_index);
}
}
}
}
// Expand a line. Try replacing all instances of equivalences.
pub fn expand_line(&self, line: &str) -> String {
let mut expanded_line = String::new();
// Replace equivalences
for token in line.split_whitespace() {
if let Some(expansion) = self.equivalences.get(token) {
expanded_line.push_str(expansion);
} else {
expanded_line.push_str(token);
}
expanded_line.push(' ');
}
expanded_line.trim_end().to_string()
}
// Attempt to assemble a parsed line. If successful, add bytes to section .text - else, extend errors and keep it pushing.
pub fn handle_assemble_instruction(
&mut self,
info: &InstructionInformation,
args: &Vec<LineComponent>,
) {
let assembled_instruction_result =
assemble_instruction(info, &args, &self.symbol_table, &self.current_address);
match assembled_instruction_result {
Ok(assembled_instruction) => match assembled_instruction {
Some(packed) => {
self.section_dot_text
.extend_from_slice(&packed.to_be_bytes());
pretty_print_instruction(&self.current_address, &packed);
}
None => {
self.section_dot_text
.extend_from_slice(&BACKPATCH_PLACEHOLDER.to_be_bytes());
println!(" - Placeholder bytes appended to section .text.\n");
}
},
Err(e) => {
self.errors.push(format!(
"[*] On line {}{}:",
self.line_prefix, self.line_number
));
self.errors.push(e);
}
}
self.current_address += MIPS_ADDRESS_ALIGNMENT;
}
}