1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 :(before "End looks_like_hex_int(s) Detectors")
23 if (SIZE(s) == 2) return true;
24
25 :(scenarios transform)
26 :(scenario pack_immediate_ignores_single_byte_nondigit_operand)
27 % Hide_errors = true;
28 == 0x1
29 b9/copy a/imm32
30 +transform: packing instruction 'b9/copy a/imm32'
31
32 +transform: instruction after packing: 'b9 a'
33
34 :(scenario pack_immediate_ignores_3_hex_digit_operand)
35 % Hide_errors = true;
36 == 0x1
37 b9/copy aaa/imm32
38 +transform: packing instruction 'b9/copy aaa/imm32'
39
40 +transform: instruction after packing: 'b9 aaa'
41
42 :(scenario pack_immediate_ignores_non_hex_operand)
43 % Hide_errors = true;
44 == 0x1
45 b9/copy xxx/imm32
46 +transform: packing instruction 'b9/copy xxx/imm32'
47
48 +transform: instruction after packing: 'b9 xxx'
49
50
51 :(code)
52 void check_valid_name(const string& s) {
53 if (s.empty()) {
54 raise << "empty name!\n" << end();
55 return;
56 }
57 if (s.at(0) == '-')
58 raise << "'" << s << "' starts with '-', which can be confused with a negative number; use a different name\n" << end();
59 if (s.substr(0, 2) == "0x") {
60 raise << "'" << s << "' looks like a hex number; use a different name\n" << end();
61 return;
62 }
63 if (isdigit(s.at(0)))
64 raise << "'" << s << "' starts with a digit, and so can be confused with a negative number; use a different name.\n" << end();
65 if (SIZE(s) == 2)
66 raise << "'" << s << "' is two characters long which can look like raw hex bytes at a glance; use a different name\n" << end();
67 }
68
69
70
71 :(scenario map_label)
72 == 0x1
73 loop:
74 05 0x0d0c0b0a/imm32
75 +transform: label 'loop' is at address 1
76
77 :(before "End Level-2 Transforms")
78 Transform.push_back(rewrite_labels);
79 :(code)
80 void rewrite_labels(program& p) {
81 trace(99, "transform") << "-- rewrite labels" << end();
82 if (p.segments.empty()) return;
83 segment& code = p.segments.at(0);
84 map<string, int32_t> byte_index;
85 compute_byte_indices_for_labels(code, byte_index);
86 if (trace_contains_errors()) return;
87 drop_labels(code);
88 if (trace_contains_errors()) return;
89 replace_labels_with_displacements(code, byte_index);
90 }
91
92 void compute_byte_indices_for_labels(const segment& code, map<string, int32_t>& byte_index) {
93 int current_byte = 0;
94 for (int i = 0; i < SIZE(code.lines); ++i) {
95 const line& inst = code.lines.at(i);
96 for (int j = 0; j < SIZE(inst.words); ++j) {
97 const word& curr = inst.words.at(j);
98
99
100
101
102
103 if (has_operand_metadata(curr, "disp32") || has_operand_metadata(curr, "imm32")) {
104 if (*curr.data.rbegin() == ':')
105 raise << "'" << to_string(inst) << "': don't use ':' when jumping to labels\n" << end();
106 current_byte += 4;
107 }
108
109 else if (*curr.data.rbegin() != ':') {
110 ++current_byte;
111 }
112 else {
113 string label = drop_last(curr.data);
114
115 check_valid_name(label);
116 if (trace_contains_errors()) return;
117 if (contains_any_operand_metadata(curr))
118 raise << "'" << to_string(inst) << "': label definition (':') not allowed in operand\n" << end();
119 if (j > 0)
120 raise << "'" << to_string(inst) << "': labels can only be the first word in a line.\n" << end();
121 if (Map_file.is_open())
122 Map_file << "0x" << HEXWORD << (code.start + current_byte) << ' ' << label << '\n';
123 put(byte_index, label, current_byte);
124 trace(99, "transform") << "label '" << label << "' is at address " << (current_byte+code.start) << end();
125
126 }
127 }
128 }
129 }
130
131 :(before "End Globals")
132 bool Dump_map = false;
133 ofstream Map_file;
134 :(before "End Commandline Options")
135 else if (is_equal(*arg, "--map")) {
136 Dump_map = true;
137
138 }
139
140 :(after "Begin subx translate")
141 if (Dump_map)
142 Map_file.open("map");
143 :(before "End subx translate")
144 if (Dump_map)
145 Map_file.close();
146
147 :(code)
148 void drop_labels(segment& code) {
149 for (int i = 0; i < SIZE(code.lines); ++i) {
150 line& inst = code.lines.at(i);
151 vector<word>::iterator new_end = remove_if(inst.words.begin(), inst.words.end(), is_label);
152 inst.words.erase(new_end, inst.words.end());
153 }
154 }
155
156 bool is_label(const word& w) {
157 return *w.data.rbegin() == ':';
158 }
159
160 void replace_labels_with_displacements(segment& code, const map<string, int32_t>& byte_index) {
161 int32_t byte_index_next_instruction_starts_at = 0;
162 for (int i = 0; i < SIZE(code.lines); ++i) {
163 line& inst = code.lines.at(i);
164 byte_index_next_instruction_starts_at += num_bytes(inst);
165 line new_inst;
166 for (int j = 0; j < SIZE(inst.words); ++j) {
167 const word& curr = inst.words.at(j);
168 if (contains_key(byte_index, curr.data)) {
169 int32_t displacement = static_cast<int32_t>(get(byte_index, curr.data)) - byte_index_next_instruction_starts_at;
170 if (has_operand_metadata(curr, "disp8")) {
171 if (displacement > 0xff || displacement < -0x7f)
172 raise << "'" << to_string(inst) << "': label too far away for displacement " << std::hex << displacement << " to fit in 8 bits\n" << end();
173 else
174 emit_hex_bytes(new_inst, displacement, 1);
175 }
176 else if (has_operand_metadata(curr, "disp16")) {
177 if (displacement > 0xffff || displacement < -0x7fff)
178 raise << "'" << to_string(inst) << "': label too far away for displacement " << std::hex << displacement << " to fit in 16 bits\n" << end();
179 else
180 emit_hex_bytes(new_inst, displacement, 2);
181 }
182 else if (has_operand_metadata(curr, "disp32")) {
183 emit_hex_bytes(new_inst, displacement, 4);
184 }
185 }
186 else {
187 new_inst.words.push_back(curr);
188 }
189 }
190 inst.words.swap(new_inst.words);
191 trace(99, "transform") << "instruction after transform: '" << data_to_string(inst) << "'" << end();
192 }
193 }
194
195 string data_to_string(const line& inst) {
196 ostringstream out;
197 for (int i = 0; i < SIZE(inst.words); ++i) {
198 if (i > 0) out << ' ';
199 out << inst.words.at(i).data;
200 }
201 return out.str();
202 }
203
204 string drop_last(const string& s) {
205 return string(s.begin(), --s.end());
206 }
207
208
209
210
211
212
213
214 :(scenario multiple_labels_at)
215 == 0x1
216
217 loop:
218 $loop2:
219
220 05 0x0d0c0b0a/imm32
221
222 eb $loop2/disp8
223
224 eb $loop3/disp8
225
226 $loop3:
227 +transform: label 'loop' is at address 1
228 +transform: label '$loop2' is at address 1
229 +transform: label '$loop3' is at address a
230
231 +transform: instruction after transform: 'eb f9'
232
233 +transform: instruction after transform: 'eb 00'
234
235 :(scenario label_too_short)
236 % Hide_errors = true;
237 == 0x1
238 xz:
239 05 0x0d0c0b0a/imm32
240 +error: 'xz' is two characters long which can look like raw hex bytes at a glance; use a different name
241
242 :(scenario label_hex)
243 % Hide_errors = true;
244 == 0x1
245 0xab:
246 05 0x0d0c0b0a/imm32
247 +error: '0xab' looks like a hex number; use a different name
248
249 :(scenario label_negative_hex)
250 % Hide_errors = true;
251 == 0x1
252 -a:
253 05 0x0d0c0b0a/imm32
254 +error: '-a' starts with '-', which can be confused with a negative number; use a different name
255
256
257
258
259 :(scenario segment_size_ignores_labels)
260 == code
261 05/add 0x0d0c0b0a/imm32
262 foo:
263 == data
264 bar:
265 00
266 +transform: segment 1 begins at address 0x0a000079
267
268 :(before "End num_bytes(curr) Special-cases")
269 else if (is_label(curr))
270 ;