about summary refs log tree commit diff stats
path: root/counters.mu
Commit message (Expand)AuthorAgeFilesLines
* 4134 - 'input' = 'ingredient'Kartik K. Agaram2017-12-031-2/+2
* 4119Kartik K. Agaram2017-11-101-2/+2
* 4089Kartik K. Agaram2017-10-221-1/+1
* 4001Kartik K. Agaram2017-09-181-1/+1
* 3394Kartik K. Agaram2016-09-171-5/+5
* 3390Kartik K. Agaram2016-09-171-5/+5
* 3389Kartik K. Agaram2016-09-171-5/+5
* 3380Kartik K. Agaram2016-09-171-4/+4
* 3302Kartik K. Agaram2016-09-071-1/+0
* 2864 - replace all address:shared with just addressKartik K. Agaram2016-04-241-5/+5
* 2735 - define recipes using 'def'Kartik K. Agaram2016-03-081-7/+5
* 2576 - distinguish allocated addresses from othersKartik K. Agaram2016-01-191-5/+5
* 2325Kartik K. Agaram2015-10-301-2/+1
* 2324 - static dispatch works with arcane headers!Kartik K. Agaram2015-10-291-8/+7
* 2295 - drop first-class recipes and continuationsKartik K. Agaram2015-10-281-8/+8
* 2294Kartik K. Agaram2015-10-281-8/+8
* 1880 - switch .mu files to new type-deducing idiomKartik K. Agaram2015-07-291-6/+6
* 1868 - start using naked literals everywhereKartik K. Agaram2015-07-281-6/+6
* 1780 - now we always reclaim local scopesKartik K. Agaram2015-07-131-2/+2
* 1773 - update all mu recipes to new-default-spaceKartik K. Agaram2015-07-131-2/+2
* 1599Kartik K. Agaram2015-06-191-4/+4
* 1469Kartik K. Agaram2015-05-261-6/+6
* 1363 - rename 'integer' to 'number'Kartik K. Agaram2015-05-131-7/+7
* 1345Kartik K. Agaram2015-05-111-3/+6
* 1298 - better ingredient/product handlingKartik K. Agaram2015-05-071-5/+1
* 1276 - make C++ version the defaultKartik K. Agaram2015-05-051-30/+32
* 690 - convention: '$' commands for debugging onlyKartik K. Agaram2015-02-011-3/+3
* 578 - switch to non-polymorphic 'print' functionsKartik K. Agaram2015-01-171-2/+2
* 574 - printing string literals is a hack; hard-code it in for nowKartik K. Agaram2015-01-161-3/+3
* 571 - screen primitives take an explicit terminalKartik K. Agaram2015-01-151-5/+5
* 498 - how mu provides closuresKartik K. Agaram2015-01-021-1/+1
* 497 - strengthen the concept of 'space'Kartik K. Agaram2015-01-021-0/+33
g { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
//: The bedrock level 1 of abstraction is now done, and we're going to start
//: building levels above it that make programming in x86 machine code a
//: little more ergonomic.
//:
//: All levels will be "pass through by default". Whatever they don't
//: understand they will silently pass through to lower levels.
//:
//: Since raw hex bytes of machine code are always possible to inject, SubX is
//: not a language, and we aren't building a compiler. This is something
//: deliberately leakier. Levels are more for improving auditing, checks and
//: error messages rather than for hiding low-level details.

//: Translator workflow: read 'source' file. Run a series of transforms on it,
//: each passing through what it doesn't understand. The final program should
//: be just machine code, suitable to write to an ELF binary.
//:
//: Higher levels usually transform code on the basis of metadata.

:(before "End Main")
if (is_equal(argv[1], "translate")) {
  START_TRACING_UNTIL_END_OF_SCOPE;
  reset();
  // Begin subx translate
  program p;
  string output_filename;
  for (int i = /*skip 'subx translate'*/2;  i < argc;  ++i) {
    if (is_equal(argv[i], "-o")) {
      ++i;
      if (i >= argc) {
        print_translate_usage();
        cerr << "'-o' must be followed by a filename to write results to\n";
        exit(1);
      }
      output_filename = argv[i];
    }
    else {
      trace(2, "parse") << argv[i] << end();
      ifstream fin(argv[i]);
      if (!fin) {
        cerr << "could not open " << argv[i] << '\n';
        return 1;
      }
      parse(fin, p);
      if (trace_contains_errors()) return 1;
    }
  }
  if (p.segments.empty()) {
    print_translate_usage();
    cerr << "nothing to do; must provide at least one file to read\n";
    exit(1);
  }
  if (output_filename.empty()) {
    print_translate_usage();
    cerr << "must provide a filename to write to using '-o'\n";
    exit(1);
  }
  trace(2, "transform") << "begin" << end();
  transform(p);
  if (trace_contains_errors()) return 1;
  trace(2, "translate") << "begin" << end();
  save_elf(p, output_filename);
  if (trace_contains_errors()) {
    unlink(output_filename.c_str());
    return 1;
  }
  // End subx translate
  return 0;
}

:(code)
void print_translate_usage() {
  cerr << "Usage: subx translate file1 file2 ... -o output\n";
}

// 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);
  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) {
  char c = '\0';
#define O(X)  c = (X); out.write(&c, sizeof(c))
// host is required to be little-endian
#define emit(X)  out.write(reinterpret_cast<const char*>(&X), sizeof(X))
  //// ehdr
  // e_ident
  O(0x7f); O(/*E*/0x45); O(/*L*/0x4c); O(/*F*/0x46);
    O(0x1);  // 32-bit format
    O(0x1);  // little-endian
    O(0x1); O(0x0);
  for (size_t i = 0;  i < 8;  ++i) { O(0x0); }
  // e_type
  O(0x02); O(0x00);
  // e_machine
  O(0x03); O(0x00);
  // e_version
  O(0x01); O(0x00); O(0x00); O(0x00);
  // e_entry
  uint32_t e_entry = find(p, "code")->start;
  // Override e_entry
  emit(e_entry);
  // e_phoff -- immediately after ELF header
  uint32_t e_phoff = 0x34;
  emit(e_phoff);
  // e_shoff; unused
  uint32_t dummy32 = 0;
  emit(dummy32);
  // e_flags; unused
  emit(dummy32);
  // e_ehsize
  uint16_t e_ehsize = 0x34;
  emit(e_ehsize);
  // e_phentsize
  uint16_t e_phentsize = 0x20;
  emit(e_phentsize);
  // e_phnum
  uint16_t e_phnum = SIZE(p.segments);
  emit(e_phnum);
  // e_shentsize
  uint16_t dummy16 = 0x0;
  emit(dummy16);
  // e_shnum
  emit(dummy16);
  // e_shstrndx
  emit(dummy16);

  uint32_t p_offset = /*size of ehdr*/0x34 + SIZE(p.segments)*0x20/*size of each phdr*/;
  for (int i = 0;  i < SIZE(p.segments);  ++i) {
    const segment& curr = p.segments.at(i);
    //// phdr
    // p_type
    uint32_t p_type = 0x1;
    emit(p_type);
    // p_offset
    emit(p_offset);
    // p_vaddr
    uint32_t p_start = curr.start;
    emit(p_start);
    // p_paddr
    emit(p_start);
    // p_filesz
    uint32_t size = num_words(curr);
    assert(p_offset + size < SEGMENT_ALIGNMENT);
    emit(size);
    // p_memsz
    emit(size);
    // p_flags
    uint32_t p_flags = (curr.name == "code") ? /*r-x*/0x5 : /*rw-*/0x6;
    emit(p_flags);

    // p_align
    // "As the system creates or augments a process image, it logically copies
    // a file's segment to a virtual memory segment.  When—and if— the system
    // physically reads the file depends on the program's execution behavior,
    // system load, and so on.  A process does not require a physical page
    // unless it references the logical page during execution, and processes
    // commonly leave many pages unreferenced. Therefore delaying physical
    // reads frequently obviates them, improving system performance. To obtain
    // this efficiency in practice, executable and shared object files must
    // have segment images whose file offsets and virtual addresses are
    // congruent, modulo the page size." -- http://refspecs.linuxbase.org/elf/elf.pdf (page 95)
    uint32_t p_align = 0x1000;  // default page size on linux
    emit(p_align);
    if (p_offset % p_align != p_start % p_align) {
      raise << "segment starting at 0x" << HEXWORD << p_start << " is improperly aligned; alignment for p_offset " << p_offset << " should be " << (p_offset % p_align) << " but is " << (p_start % p_align) << '\n' << end();
      return;
    }

    // prepare for next segment
    p_offset += size;
  }
#undef O
#undef emit
}

void write_segment(const segment& s, ostream& out) {
  for (int i = 0;  i < SIZE(s.lines);  ++i) {
    const vector<word>& w = s.lines.at(i).words;
    for (int j = 0;  j < SIZE(w);  ++j) {
      uint8_t x = hex_byte(w.at(j).data);  // we're done with metadata by this point
      out.write(reinterpret_cast<const char*>(&x), /*sizeof(byte)*/1);
    }
  }
}

uint32_t num_words(const segment& s) {
  uint32_t sum = 0;
  for (int i = 0;  i < SIZE(s.lines);  ++i)
    sum += SIZE(s.lines.at(i).words);
  return sum;
}

:(before "End Includes")
using std::ios;