From ef52bbf908f4cba70fa95b54a32d28035fda545c Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Thu, 25 Jul 2019 10:40:27 -0700 Subject: 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. --- subx/011run.cc | 12 ++++++++---- subx/028translate.cc | 10 ++++++++-- subx/035labels.cc | 55 ++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 59 insertions(+), 18 deletions(-) (limited to 'subx') 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 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& 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. -- cgit 1.4.1-2-gfad0 a id='n26' href='#n26'>26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90