# Mu - subx/036global_variables.cc

      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 has much the same structure as rewriting labels.
      8
      9 :(scenario global_variable)
     10 == code
     11 b9  x/imm32
     12 == data
     13 x:
     14 00 00 00 00
     15 +transform: global variable 'x' is at address 0x0a000079
     16
     17 :(before "End Level-2 Transforms")
     18 Transform.push_back(rewrite_global_variables);
     19 :(code)
     20 void rewrite_global_variables(program& p) {
     21   trace(99, "transform") << "-- rewrite global variables" << end();
     22   map<string, uint32_t> address;
     23   compute_addresses_for_global_variables(p, address);
     24   if (trace_contains_errors()) return;
     25   drop_global_variables(p);
     26   replace_global_variables_with_addresses(p, address);
     27 }
     28
     29 void compute_addresses_for_global_variables(const program& p, map<string, uint32_t>& address) {
     30   for (int i = /*skip code segment*/1;  i < SIZE(p.segments);  ++i)
     31     compute_addresses_for_global_variables(p.segments.at(i), address);
     32 }
     33
     34 void compute_addresses_for_global_variables(const segment& s, map<string, uint32_t>& address) {
     35   int current_address = s.start;
     36   for (int i = 0;  i < SIZE(s.lines);  ++i) {
     37     const line& inst = s.lines.at(i);
     38     for (int j = 0;  j < SIZE(inst.words);  ++j) {
     39       const word& curr = inst.words.at(j);
     40       if (*curr.data.rbegin() != ':') {
     41         ++current_address;
     42       }
     43       else {
     44         string variable = drop_last(curr.data);
     45         // ensure variables look sufficiently different from raw hex
     46         check_valid_name(variable);
     47         if (trace_contains_errors()) return;
     48         if (j > 0)
     49           raise << "'" << to_string(inst) << "': global variable names can only be the first word in a line.\n" << end();
     50         if (Map_file.is_open())
     51           Map_file << "0x" << HEXWORD << current_address << ' ' << variable << '\n';
     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         if (!looks_like_hex_int(curr.data))
     75           raise << "missing reference to global '" << curr.data << "'\n" << end();
     76         new_inst.words.push_back(curr);
     77         continue;
     78       }
     79       if (!valid_use_of_global_variable(curr)) {
     80         raise << "'" << to_string(inst) << "': can't refer to global variable '" << curr.data << "'\n" << end();
     81         return;
     82       }
     83       emit_hex_bytes(new_inst, get(address, curr.data), 4);
     84     }
     85     inst.words.swap(new_inst.words);
     86     trace(99, "transform") << "instruction after transform: '" << data_to_string(inst) << "'" << end();
     87   }
     88 }
     89
     90 bool valid_use_of_global_variable(const word& curr) {
     91   if (has_operand_metadata(curr, "imm32")) return true;
     92   // End Valid Uses Of Global Variable(curr)
     93   return false;
     94 }
     95
     96 //:: a more complex sanity check for how we use global variables
     97 //: requires first saving some data early before we pack operands
     98
     99 :(after "Begin Level-2 Transforms")
    100 Transform.push_back(correlate_disp32_with_mod);
    101 :(code)
    102 void correlate_disp32_with_mod(program& p) {
    103   if (p.segments.empty()) return;
    104   segment& code = p.segments.at(0);
    105   for (int i = 0;  i < SIZE(code.lines);  ++i) {
    106     line& inst = code.lines.at(i);
    107     for (int j = 0;  j < SIZE(inst.words);  ++j) {
    108       word& curr = inst.words.at(j);
    109       if (has_operand_metadata(curr, "disp32")
    110           && has_operand_metadata(inst, "mod"))
    111         curr.metadata.push_back("has_mod");
    112     }
    113   }
    114 }
    115
    116 :(before "End Valid Uses Of Global Variable(curr)")
    117 if (has_operand_metadata(curr, "disp32"))
    118   return has_metadata(curr, "has_mod");
    119 // todo: more sophisticated check, to ensure we don't use global variable
    120 // addresses as a real displacement added to other operands.
    121
    122 :(code)
    123 bool has_metadata(const word& w, const string& m) {
    124   for (int i = 0;  i < SIZE(w.metadata);  ++i)
    125     if (w.metadata.at(i) == m) return true;
    126   return false;
    127 }
    128
    129 :(scenario global_variable_disallowed_in_jump)
    130 % Hide_errors = true;
    131 == code
    132 eb/jump  x/disp8
    133 == data
    134 x:
    135 00 00 00 00
    136 +error: 'eb/jump x/disp8': can't refer to global variable 'x'
    137 # sub-optimal error message; should be
    138 #? +error: can't jump to data (variable 'x')
    139
    140 :(scenario global_variable_disallowed_in_call)
    141 % Hide_errors = true;
    142 == code
    143 e8/call  x/disp32
    144 == data
    145 x:
    146 00 00 00 00
    147 +error: 'e8/call x/disp32': can't refer to global variable 'x'
    148 # sub-optimal error message; should be
    149 #? +error: can't call to the data segment ('x')
    150
    151 :(scenario disp32_data_with_modrm)
    152 == code
    153 8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX x/disp32
    154 == data
    155 x:
    156 00 00 00 00
    157 $error: 0
    158
    159 :(scenarios transform)
    160 :(scenario disp32_data_with_call)
    161 == code
    162 foo:
    163 e8/call bar/disp32
    164 bar:
    165 $error: 0
    166
    167 :(code)
    168 string to_full_string(const line& in) {
    169   ostringstream out;
    170   for (int i = 0;  i < SIZE(in.words);  ++i) {
    171     if (i > 0) out << ' ';
    172     out << in.words.at(i).data;
    173     for (int j = 0;  j < SIZE(in.words.at(i).metadata);  ++j)
    174       out << '/' << in.words.at(i).metadata.at(j);
    175   }
    176   return out.str();
    177 }