https://github.com/akkartik/mu/blob/master/subx/036global_variables.cc
1
2
3
4
5
6
7
8
9 :(code)
10 void test_global_variable() {
11 run(
12 "== code\n"
13 "b9 x/imm32\n"
14 "== data\n"
15 "x:\n"
16 " 00 00 00 00\n"
17 );
18 CHECK_TRACE_CONTENTS(
19 "transform: global variable 'x' is at address 0x0a000079\n"
20 );
21 }
22
23 :(before "End Level-2 Transforms")
24 Transform.push_back(rewrite_global_variables);
25 :(code)
26 void rewrite_global_variables(program& p) {
27 trace(3, "transform") << "-- rewrite global variables" << end();
28
29 map<string, uint32_t> address;
30 compute_addresses_for_global_variables(p, address);
31 if (trace_contains_errors()) return;
32 drop_global_variables(p);
33 replace_global_variables_with_addresses(p, address);
34 }
35
36 void compute_addresses_for_global_variables(const program& p, map<string, uint32_t>& address) {
37 for (int i = 1; i < SIZE(p.segments); ++i)
38 compute_addresses_for_global_variables(p.segments.at(i), address);
39 }
40
41 void compute_addresses_for_global_variables(const segment& s, map<string, uint32_t>& address) {
42 int current_address = s.start;
43 for (int i = 0; i < SIZE(s.lines); ++i) {
44 const line& inst = s.lines.at(i);
45 for (int j = 0; j < SIZE(inst.words); ++j) {
46 const word& curr = inst.words.at(j);
47 if (*curr.data.rbegin() != ':') {
48 current_address += size_of(curr);
49 }
50 else {
51 string variable = drop_last(curr.data);
52
53 check_valid_name(variable);
54 if (trace_contains_errors()) return;
55 if (j > 0)
56 raise << "'" << to_string(inst) << "': global variable names can only be the first word in a line.\n" << end();
57 if (Map_file.is_open())
58 Map_file << "0x" << HEXWORD << current_address << ' ' << variable << '\n';
59 if (contains_key(address, variable)) {
60 raise << "duplicate global '" << variable << "'\n" << end();
61 return;
62 }
63 put(address, variable, current_address);
64 trace(99, "transform") << "global variable '" << variable << "' is at address 0x" << HEXWORD << current_address << end();
65
66 }
67 }
68 }
69 }
70
71 void drop_global_variables(program& p) {
72 for (int i = 1; i < SIZE(p.segments); ++i)
73 drop_labels(p.segments.at(i));
74 }
75
76 void replace_global_variables_with_addresses(program& p, const map<string, uint32_t>& address) {
77 if (p.segments.empty()) return;
78 replace_global_variables_in_code_segment(p.segments.at(0), address);
79 for (int i = 1; i < SIZE(p.segments); ++i)
80 replace_global_variables_in_data_segment(p.segments.at(i), address);
81 }
82
83 void replace_global_variables_in_code_segment(segment& code, const map<string, uint32_t>& address) {
84 for (int i = 0; i < SIZE(code.lines); ++i) {
85 line& inst = code.lines.at(i);
86 line new_inst;
87 for (int j = 0; j < SIZE(inst.words); ++j) {
88 const word& curr = inst.words.at(j);
89 if (!contains_key(address, curr.data)) {
90 if (!looks_like_hex_int(curr.data))
91 raise << "missing reference to global '" << curr.data << "'\n" << end();
92 new_inst.words.push_back(curr);
93 continue;
94 }
95 if (!valid_use_of_global_variable(curr)) {
96 raise << "'" << to_string(inst) << "': can't refer to global variable '" << curr.data << "'\n" << end();
97 return;
98 }
99 emit_hex_bytes(new_inst, get(address, curr.data), 4);
100 }
101 inst.words.swap(new_inst.words);
102 trace(99, "transform") << "instruction after transform: '" << data_to_string(inst) << "'" << end();
103 }
104 }
105
106 void replace_global_variables_in_data_segment(segment& data, const map<string, uint32_t>& address) {
107 for (int i = 0; i < SIZE(data.lines); ++i) {
108 line& l = data.lines.at(i);
109 line new_l;
110 for (int j = 0; j < SIZE(l.words); ++j) {
111 const word& curr = l.words.at(j);
112 if (!contains_key(address, curr.data)) {
113 if (looks_like_hex_int(curr.data)) {
114 if (has_operand_metadata(curr, "imm32"))
115 emit_hex_bytes(new_l, curr, 4);
116 else if (has_operand_metadata(curr, "imm16"))
117 emit_hex_bytes(new_l, curr, 2);
118 else if (has_operand_metadata(curr, "imm8"))
119 emit_hex_bytes(new_l, curr, 1);
120 else if (has_operand_metadata(curr, "disp8"))
121 raise << "can't use /disp8 in a non-code segment\n" << end();
122 else if (has_operand_metadata(curr, "disp16"))
123 raise << "can't use /disp16 in a non-code segment\n" << end();
124 else if (has_operand_metadata(curr, "disp32"))
125 raise << "can't use /disp32 in a non-code segment\n" << end();
126 else
127 new_l.words.push_back(curr);
128 }
129 else {
130 raise << "missing reference to global '" << curr.data << "'\n" << end();
131 new_l.words.push_back(curr);
132 }
133 continue;
134 }
135 trace(99, "transform") << curr.data << " maps to " << HEXWORD << get(address, curr.data) << end();
136 emit_hex_bytes(new_l, get(address, curr.data), 4);
137 }
138 l.words.swap(new_l.words);
139 trace(99, "transform") << "after transform: '" << data_to_string(l) << "'" << end();
140 }
141 }
142
143 bool valid_use_of_global_variable(const word& curr) {
144 if (has_operand_metadata(curr, "imm32")) return true;
145
146 return false;
147 }
148
149
150
151
152 :(after "Begin Level-2 Transforms")
153 Transform.push_back(correlate_disp32_with_mod);
154 :(code)
155 void correlate_disp32_with_mod(program& p) {
156 if (p.segments.empty()) return;
157 segment& code = p.segments.at(0);
158 for (int i = 0; i < SIZE(code.lines); ++i) {
159 line& inst = code.lines.at(i);
160 for (int j = 0; j < SIZE(inst.words); ++j) {
161 word& curr = inst.words.at(j);
162 if (has_operand_metadata(curr, "disp32")
163 && has_operand_metadata(inst, "mod"))
164 curr.metadata.push_back("has_mod");
165 }
166 }
167 }
168
169 :(before "End Valid Uses Of Global Variable(curr)")
170 if (has_operand_metadata(curr, "disp32"))
171 return has_metadata(curr, "has_mod");
172
173
174
175 :(code)
176 bool has_metadata(const word& w, const string& m) {
177 for (int i = 0; i < SIZE(w.metadata); ++i)
178 if (w.metadata.at(i) == m) return true;
179 return false;
180 }
181
182 void test_global_variable_disallowed_in_jump() {
183 Hide_errors = true;
184 run(
185 "== code\n"
186 "eb/jump x/disp8\n"
187 "== data\n"
188 "x:\n"
189 " 00 00 00 00\n"
190 );
191 CHECK_TRACE_CONTENTS(
192 "error: 'eb/jump x/disp8': can't refer to global variable 'x'\n"
193
194
195 );
196 }
197
198 void test_global_variable_disallowed_in_call() {
199 Hide_errors = true;
200 run(
201 "== code\n"
202 "e8/call x/disp32\n"
203 "== data\n"
204 "x:\n"
205 " 00 00 00 00\n"
206 );
207 CHECK_TRACE_CONTENTS(
208 "error: 'e8/call x/disp32': can't refer to global variable 'x'\n"
209
210
211 );
212 }
213
214 void test_global_variable_in_data_segment() {
215 run(
216 "== 0x1\n"
217 "b9 x/imm32\n"
218 "== 0x0a000000\n"
219 "x:\n"
220 " y/imm32\n"
221 "y:\n"
222 " 00 00 00 00\n"
223 );
224
225 CHECK_TRACE_CONTENTS(
226 "load: 0x0a000000 -> 04\n"
227 "load: 0x0a000001 -> 00\n"
228 "load: 0x0a000002 -> 00\n"
229 "load: 0x0a000003 -> 0a\n"
230 );
231 CHECK_TRACE_COUNT("error", 0);
232 }
233
234 void test_raw_number_with_imm32_in_data_segment() {
235 run(
236 "== 0x1\n"
237 "b9 x/imm32\n"
238 "== 0x0a000000\n"
239 "x:\n"
240 " 1/imm32\n"
241 );
242
243 CHECK_TRACE_CONTENTS(
244 "load: 0x0a000000 -> 01\n"
245 "load: 0x0a000001 -> 00\n"
246 "load: 0x0a000002 -> 00\n"
247 "load: 0x0a000003 -> 00\n"
248 );
249 CHECK_TRACE_COUNT("error", 0);
250 }
251
252 void test_duplicate_global_variable() {
253 Hide_errors = true;
254 run(
255 "== 0x1\n"
256 "40/increment-EAX\n"
257 "== 0x0a000000\n"
258 "x:\n"
259 "x:\n"
260 " 00\n"
261 );
262 CHECK_TRACE_CONTENTS(
263 "error: duplicate global 'x'\n"
264 );
265 }
266
267 void test_global_variable_disp32_with_modrm() {
268 run(
269 "== code\n"
270 "8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX x/disp32\n"
271 "== data\n"
272 "x:\n"
273 " 00 00 00 00\n"
274 );
275 CHECK_TRACE_COUNT("error", 0);
276 }
277
278 void test_global_variable_disp32_with_call() {
279 transform(
280 "== code\n"
281 "foo:\n"
282 " e8/call bar/disp32\n"
283 "bar:\n"
284 );
285 CHECK_TRACE_COUNT("error", 0);
286 }
287
288 string to_full_string(const line& in) {
289 ostringstream out;
290 for (int i = 0; i < SIZE(in.words); ++i) {
291 if (i > 0) out << ' ';
292 out << in.words.at(i).data;
293 for (int j = 0; j < SIZE(in.words.at(i).metadata); ++j)
294 out << '/' << in.words.at(i).metadata.at(j);
295 }
296 return out.str();
297 }