about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--subx/035labels.cc4
-rw-r--r--subx/038check_local_jumps.cc60
2 files changed, 62 insertions, 2 deletions
diff --git a/subx/035labels.cc b/subx/035labels.cc
index c7274613..efab173d 100644
--- a/subx/035labels.cc
+++ b/subx/035labels.cc
@@ -189,7 +189,7 @@ string drop_last(const string& s) {
           # op          subop               mod             rm32          base        index         scale       r32
           # 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 # address 1
- $loop:
+loop:
  $loop2:
 # address 1 (labels take up no space)
             05                                                                                                                              0x0d0c0b0a/imm32  # add to EAX
@@ -199,7 +199,7 @@ string drop_last(const string& s) {
             eb                                                                                                              $loop3/disp8
 # address 10
  $loop3:
-+transform: label '$loop' is at address 1
++transform: label 'loop' is at address 1
 +transform: label '$loop2' is at address 1
 +transform: label '$loop3' is at address 10
 # first jump is to -7
diff --git a/subx/038check_local_jumps.cc b/subx/038check_local_jumps.cc
new file mode 100644
index 00000000..41f8e471
--- /dev/null
+++ b/subx/038check_local_jumps.cc
@@ -0,0 +1,60 @@
+//: Make sure that we never jump from one function to within another.
+//:
+//: (The check for label types already ensures we can't jump to the start of
+//: another function.)
+
+:(scenario jump_to_different_function)
+% Hide_errors = true;
+== 0x1
+fn1:
+  7e/jump-if $target/disp8
+fn2:
+ $target:
++error: '7e/jump-if $target/disp8' in function 'fn1': jump to within another function 'fn2' is a *really* bad idea
+
+:(before "Rewrite Labels(segment code)")
+check_local_jumps(code);
+if (trace_contains_errors()) return;
+:(code)
+void check_local_jumps(const segment& code) {
+  map</*jump target*/string, /*containing call target*/string> function;
+  compute_function_target(code, function);
+  if (trace_contains_errors()) return;
+  string current_function;
+  for (int i = 0;  i < SIZE(code.lines);  ++i) {
+    const line& inst = code.lines.at(i);
+    if (SIZE(inst.words) == 1 && is_label(inst.words.at(0))) {
+      // label definition
+      if (inst.words.at(0).data.at(0) != '$')
+        current_function = drop_last(inst.words.at(0).data);
+    }
+    else if (is_jump(inst)) {
+      const word& target = inst.words.at(first_operand(inst));
+      if (!contains_key(function, target.data)) continue;  // error/warning handled elsewhere
+      if (get(function, target.data) == current_function) continue;
+      raise << "'" << to_string(inst) << "' in function '" << current_function << "': jump to within another function '" << get(function, target.data) << "' is a *really* bad idea\n" << end();
+      return;
+    }
+  }
+}
+
+void compute_function_target(const segment& code, map<string, string>& out) {
+  string current_function;
+  for (int i = 0;  i < SIZE(code.lines);  ++i) {
+    const line& inst = code.lines.at(i);
+    if (SIZE(inst.words) != 1) continue;
+    const word& curr = inst.words.at(0);
+    if (!is_label(curr)) continue;
+    const string& label = drop_last(curr.data);
+    if (label.at(0) != '$') {
+      current_function = label;
+      continue;
+    }
+    if (contains_key(out, label)) {
+      raise << "duplicate label '" << label << "'\n" << end();
+      return;
+    }
+    // current_function can be empty! if so that would be 'main'.
+    put(out, label, current_function);
+  }
+}