about summary refs log tree commit diff stats
path: root/subx
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-07-25 10:40:27 -0700
committerKartik Agaram <vc@akkartik.com>2019-07-25 10:40:27 -0700
commitef52bbf908f4cba70fa95b54a32d28035fda545c (patch)
tree760b0954928e9949f6d326183437a7e58d2782bc /subx
parent8bc689672480339f6975c94fcce120a46a8fa7df (diff)
downloadmu-ef52bbf908f4cba70fa95b54a32d28035fda545c.tar.gz
5472 - disallow programs without `Entry` labels
This makes the C++ translator more consistent with the self-hosted
translator.

We go through some contortions to continue supporting unit tests without
'Entry' labels. But we still want to throw good errors when translating
.subx files at the commandline.
Diffstat (limited to 'subx')
-rw-r--r--subx/011run.cc12
-rw-r--r--subx/028translate.cc10
-rw-r--r--subx/035labels.cc55
3 files changed, 59 insertions, 18 deletions
diff --git a/subx/011run.cc b/subx/011run.cc
index 4c53d781..d9877c4d 100644
--- a/subx/011run.cc
+++ b/subx/011run.cc
@@ -16,8 +16,8 @@ put_new(Help, "syntax",
   "the segment and a (sometimes approximate) starting address in memory.\n"
   "The name 'code' is special; instructions to execute should always go here.\n"
   "\n"
-  "The resulting binary starts running from the start of the segment by default.\n"
-  "To start elsewhere in the code segment, define a special label called 'Entry'.\n"
+  "The resulting binary starts running code from a label called 'Entry'\n"
+  "in the code segment.\n"
   "\n"
   "Segments with the same name get merged together. This rule helps keep functions and\n"
   "their data close together in .subx files.\n"
@@ -91,6 +91,10 @@ void run(const string& text_bytes) {
   if (trace_contains_errors()) return;
   load(p);
   if (trace_contains_errors()) return;
+  if (p.entry)
+    EIP = p.entry;
+  else
+    EIP = find(p, "code")->start;  // just in unit tests
   while (EIP < End_of_program)
     run_one_instruction();
 }
@@ -99,7 +103,9 @@ void run(const string& text_bytes) {
 
 :(before "End Types")
 struct program {
+  uint32_t entry;
   vector<segment> segments;
+  program() { entry = 0; }
 };
 :(before "struct program")
 struct segment {
@@ -281,8 +287,6 @@ void load(const program& p) {
     }
     if (seg.name == "code") {
       End_of_program = addr;
-      EIP = seg.start;
-      // End Initialize EIP
     }
   }
 }
diff --git a/subx/028translate.cc b/subx/028translate.cc
index 5a8c60c1..937647f2 100644
--- a/subx/028translate.cc
+++ b/subx/028translate.cc
@@ -75,10 +75,14 @@ void print_translate_usage() {
 // write out a program to a bare-bones ELF file
 void save_elf(const program& p, const string& filename) {
   ofstream out(filename.c_str(), ios::binary);
+  save_elf(p, out);
+  out.close();
+}
+
+void save_elf(const program& p, ostream& out) {
   write_elf_header(out, p);
   for (size_t i = 0;  i < p.segments.size();  ++i)
     write_segment(p.segments.at(i), out);
-  out.close();
 }
 
 void write_elf_header(ostream& out, const program& p) {
@@ -100,7 +104,9 @@ void write_elf_header(ostream& out, const program& p) {
   // e_version
   O(0x01); O(0x00); O(0x00); O(0x00);
   // e_entry
-  uint32_t e_entry = find(p, "code")->start;
+  if (p.entry == 0)
+    raise << "no 'Entry' label found\n" << end();
+  uint32_t e_entry = p.entry;
   // Override e_entry
   emit(e_entry);
   // e_phoff -- immediately after ELF header
diff --git a/subx/035labels.cc b/subx/035labels.cc
index 3c2d0dd8..fe39a1bf 100644
--- a/subx/035labels.cc
+++ b/subx/035labels.cc
@@ -19,9 +19,12 @@
 //: Later layers may add more conventions partitioning the space of names. But
 //: the above rules will remain inviolate.
 
-//: One special label: the address to start running the program at.
+//: One special label is 'Entry', the address to start running the program at.
+//: It can be non-unique; the last declaration overrides earlier ones.
+//: It must exist in a program. Otherwise we don't know where to start running
+//: programs.
 
-void test_entry_label() {
+void test_Entry_label() {
   run(
       "== code 0x1\n"
       "05 0x0d0c0b0a/imm32\n"
@@ -34,15 +37,6 @@ void test_entry_label() {
   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000001 opcode: 05");
 }
 
-:(before "End Globals")
-uint32_t Entry_address = 0;
-:(before "End Reset")
-Entry_address = 0;
-:(before "End Initialize EIP")
-if (Entry_address) EIP = Entry_address;
-:(after "Override e_entry")
-if (Entry_address) e_entry = Entry_address;
-
 :(before "End looks_like_hex_int(s) Detectors")
 if (SIZE(s) == 2) return true;
 
@@ -131,7 +125,7 @@ void rewrite_labels(program& p) {
   if (trace_contains_errors()) return;
   replace_labels_with_displacements(code, byte_index);
   if (contains_key(byte_index, "Entry"))
-    Entry_address = code.start + get(byte_index, "Entry");
+    p.entry = code.start + get(byte_index, "Entry");
 }
 
 void compute_byte_indices_for_labels(const segment& code, map<string, int32_t>& byte_index) {
@@ -363,6 +357,43 @@ void test_label_negative_hex() {
   );
 }
 
+//: As said up top, the 'Entry' label is special.
+//: It can be non-unique; the last declaration overrides earlier ones.
+//: It must exist in a program. Otherwise we don't know where to start running
+//: programs.
+
+void test_duplicate_Entry_label() {
+  transform(
+      "== code 0x1\n"
+      "Entry:\n"
+      "Entry:\n"
+      "    05  0x0d0c0b0a/imm32\n"
+  );
+  CHECK_TRACE_DOESNT_CONTAIN_ERRORS();
+}
+
+// This test could do with some refactoring.
+// We're duplicating the flow inside `subx translate`, but without
+// reading/writing files.
+// We can't just use run(string) because most of our tests allow programs
+// without 'Entry' labels, as a convenience.
+void test_programs_without_Entry_label() {
+  Hide_errors = true;
+  program p;
+  istringstream in(
+      "== code 0x1\n"
+      "05 0x0d0c0b0a/imm32\n"
+      "05 0x0d0c0b0a/imm32\n"
+  );
+  parse(in, p);
+  transform(p);
+  ostringstream dummy;
+  save_elf(p, dummy);
+  CHECK_TRACE_CONTENTS(
+      "error: no 'Entry' label found\n"
+  );
+}
+
 //: now that we have labels, we need to adjust segment size computation to
 //: ignore them.