1 //: Global variables.
  2 //:
  3 //: Global variables are just labels in the data segment.
  4 //: However, they can only be used in imm32 and not disp32 operands. And they
  5 //: can't be used with jump and call instructions.
  6 //:
  7 //: This layer much the same structure as rewriting labels.
  8 
  9 :(scenario global_variable)
 10 % Mem_offset = CODE_START;
 11 % Mem.resize(0x2000);
 12 == code
 13 b9/copy x/imm32  # copy to ECX
 14 == data
 15 x:
 16 00 00 00 00
 17 +transform: global variable 'x' is at address 0x08049079
 18 
 19 :(before "End Level-2 Transforms")
 20 Transform.push_back(rewrite_global_variables);
 21 :(code)
 22 void rewrite_global_variables(program& p) {
 23   trace(99, "transform") << "-- rewrite global variables" << end();
 24   map<string, uint32_t> address;
 25   compute_addresses_for_global_variables(p, address);
 26   if (trace_contains_errors()) return;
 27   drop_global_variables(p);
 28   replace_global_variables_with_addresses(p, address);
 29 }
 30 
 31 void compute_addresses_for_global_variables(const program& p, map<string, uint32_t>& address) {
 32   for (int i = /*skip code segment*/1;  i < SIZE(p.segments);  ++i)
 33     compute_addresses_for_global_variables(p.segments.at(i), address);
 34 }
 35 
 36 void compute_addresses_for_global_variables(const segment& s, map<string, uint32_t>& address) {
 37   int current_address = s.start;
 38   for (int i = 0;  i < SIZE(s.lines);  ++i) {
 39     const line& inst = s.lines.at(i);
 40     for (int j = 0;  j < SIZE(inst.words);  ++j) {
 41       const word& curr = inst.words.at(j);
 42       if (*curr.data.rbegin() != ':') {
 43         ++current_address;
 44       }
 45       else {
 46         string variable = drop_last(curr.data);
 47         // ensure variables look sufficiently different from raw hex
 48         check_valid_name(variable);
 49         if (trace_contains_errors()) return;
 50         if (j > 0)
 51           raise << "'" << to_string(inst) << "': global variable names can only be the first word in a line.\n" << end();
 52         put(address, variable, current_address);
 53         trace(99, "transform") << "global variable '" << variable << "' is at address 0x" << HEXWORD << current_address << end();
 54         // no modifying current_address; global variable definitions won't be in the final binary
 55       }
 56     }
 57   }
 58 }
 59 
 60 void drop_global_variables(program& p) {
 61   for (int i = /*skip code segment*/1;  i < SIZE(p.segments);  ++i)
 62     drop_labels(p.segments.at(i));
 63 }
 64 
 65 void replace_global_variables_with_addresses(program& p, const map<string, uint32_t>& address) {
 66   if (p.segments.empty()) return;
 67   segment& code = p.segments.at(0);
 68   for (int i = 0;  i < SIZE(code.lines);  ++i) {
 69     line& inst = code.lines.at(i);
 70     line new_inst;
 71     for (int j = 0;  j < SIZE(inst.words);  ++j) {
 72       const word& curr = inst.words.at(j);
 73       if (contains_key(address, curr.data)) {
 74         uint32_t value = get(address, curr.data);
 75         if (!has_metadata(curr, "imm32"))
 76           raise << "'" << to_string(inst) << "': data variables should always be in '/imm32' operands\n" << end();
 77         emit_hex_bytes(new_inst, value, 4);
 78       }
 79       else {
 80         new_inst.words.push_back(curr);
 81       }
 82     }
 83     inst.words.swap(new_inst.words);
 84     trace(99, "transform") << "instruction after transform: '" << data_to_string(inst) << "'" << end();
 85   }
 86 }
 87 
 88 :(scenario global_variable_disallowed_in_jump)
 89 % Mem_offset = CODE_START;
 90 % Hide_errors = true;
 91 == code
 92 eb/jump x/disp8
 93 == data
 94 x:
 95 00 00 00 00
 96 +error: 'eb/jump x/disp8': data variables should always be in '/imm32' operands
 97 # sub-optimal error message; should be
 98 #? +error: can't jump to data (variable 'x')
 99 
100 :(scenario global_variable_disallowed_in_call)
101 % Mem_offset = CODE_START;
102 % Hide_errors = true;
103 == code
104 e8/call x/disp32
105 == data
106 x:
107 00 00 00 00
108 +error: 'e8/call x/disp32': data variables should always be in '/imm32' operands
109 # sub-optimal error message; should be
110 #? +error: can't call a data variable ('x')
111 # also, what about function pointers?