From b35358fdd8a1bbdbc061809b4232026017435757 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Tue, 10 Jan 2017 08:36:06 -0500 Subject: [PATCH] linker compiles and works for simple cases. --- Makefile | 4 +- expression.cpp | 1 + link.cpp | 387 +++++++++++++++++++++++++++++++++++-------------- omf.cpp | 147 +++++++++++++++++++ omf.h | 52 +++++++ 5 files changed, 483 insertions(+), 108 deletions(-) create mode 100644 omf.cpp create mode 100644 omf.h diff --git a/Makefile b/Makefile index ec82e73..40ed5af 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CXXFLAGS = -std=c++14 -g -Wall CCFLAGS = -g DUMP_OBJS = dumpobj.o disassembler.o zrdz_disassembler.o -LINK_OBJS = link.o expression.o +LINK_OBJS = link.o expression.o omf.o #UNAME_S := $(shell uname -s) #ifeq ($(UNAME_S),MINGW64_NT-10.0) @@ -27,6 +27,8 @@ wdclink : $(LINK_OBJS) disassembler.o : disassembler.cpp disassembler.h zrdz_disassembler.o : zrdz_disassembler.cpp zrdz_disassembler.h disassembler.h dumpobj.o : dumpobj.cpp zrdz_disassembler.h disassembler.h +omf.o : omf.cpp omf.h +expression.o : expression.cpp expression.h mingw/err.o : mingw/err.c mingw/err.h .PHONY: clean diff --git a/expression.cpp b/expression.cpp index 801ae4f..6b6e7a5 100644 --- a/expression.cpp +++ b/expression.cpp @@ -83,6 +83,7 @@ bool binary_op(unsigned op, std::vector &v) { #endif } } + // optimization... regardless of where it's located, shifting > 24 bits will result in 0... if (a.tag == OP_LOC && b.tag == OP_VAL) { switch(op) { case OP_ADD: a.value += b.value; return true; diff --git a/link.cpp b/link.cpp index 0c5e21d..eaf67ab 100644 --- a/link.cpp +++ b/link.cpp @@ -24,7 +24,7 @@ #include "obj816.h" #include "expression.h" - +#include "omf.h" #ifndef O_BINARY #define O_BINARY 0 @@ -35,6 +35,9 @@ struct { bool _v = false; bool _C = false; bool _X = false; + bool _S = false; + std::string _o; + unsigned _errors = 0; } flags; @@ -477,6 +480,7 @@ void one_module(const std::vector &data, const std::vector &se assert(!"unsupported expression opcode."); } } + sections[current_section].expressions.emplace_back(std::move(e)); break; } @@ -566,6 +570,8 @@ void init() { void generate_end() { + +/* const std::string names[] = { "_END_PAGE0", "_END_CODE", @@ -573,158 +579,318 @@ void generate_end() { "_END_DATA" "_END_UDATA" }; - +*/ for (int i = 0; i < 5; ++i) { symbol s; s.section = i; s.type = S_REL; s.flags = SF_DEF | SF_GBL; - s.offset = sections[i].data.size(); + s.offset = sections[i].size; // data.size() doesn't word w/ ref_only symbols[i * 2 + 1] = s; } } -void merge_data() { - // merge data sections -- kdata, data, udata. - // merge custom data sections? +std::vector omf_segments; - std::vector new_data; - std::vector new_expr; - unsigned new_number = 0; +template +void append(std::vector &to, std::vector &from) { + to.insert(to.end(), + std::make_move_iterator(from.begin()), + std::make_move_iterator(from.end()) + ); +} + + +template +void append(std::vector &to, const std::vector &from) { + to.insert(to.end(), + from.begin(), + from.end() + ); +} + +template +void append(std::vector &to, unsigned count, const T& value) { + to.insert(to.end(), + count, + value + ); +} + +/* + * convert a wdc expression to an omf reloc/interseg record. + * + */ +void to_omf(const expression &e, omf::segment &seg) { + if (e.stack.empty()) return; //? + + if (e.stack.size() == 1 && e.stack[0].tag == OP_VAL) { + uint32_t value = e.stack[0].value; + + for(int i = 0; i < e.size; ++i, value >>= 8) + seg.data[e.offset + i] = value & 0xff; + return; + } + + if (e.stack.size() == 1 && e.stack[0].tag == OP_LOC) { + auto &loc = e.stack[0]; + + uint32_t value = loc.value; + + if (loc.section == 0) { + warnx("Unable to relocate (invalid segment)."); + flags._errors++; + return; + } + if (loc.section == seg.segnum) { + omf::reloc r; + r.size = e.size; + r.offset = e.offset; + r.value = value; + + // also store value in data + for (int i = 0; i < e.size; ++i, value >>= 8) + seg.data[e.offset + i] = value & 0xff; + + seg.relocs.emplace_back(r); + } else { + omf::interseg r; + r.size = e.size; + r.offset = e.offset; + r.segment = loc.section; + r.segment_offset = loc.value; + + seg.intersegs.emplace_back(r); + } + return; + } + + if (e.stack.size() == 3 + && e.stack[0].tag == OP_LOC + && e.stack[1].tag == OP_VAL + && (e.stack[2].tag == OP_SHL || e.stack[2].tag == OP_SHR)) { + + auto &loc = e.stack[0]; + auto &shift = e.stack[1]; + auto &op = e.stack[2]; + + + + if (shift.value > 24) { + warnx("shift %d", shift.value); + for(int i = 0; i < e.size; ++i) + seg.data[e.offset +i] = 0; + return; + } + + + if (loc.section == 0) { + warnx("Unable to relocate expression (invalid segment)."); + flags._errors++; + return; + } + + uint32_t value = loc.value; + uint8_t shift_value = shift.value; + if (op.tag == OP_SHR) { + value >>= shift_value; + shift_value = -shift_value; + } else { + value <<= shift_value; + } + + if (loc.section == seg.segnum) { + omf::reloc r; + r.size = e.size; + r.offset = e.offset; + r.value = loc.value; + r.shift = shift_value; + + // also store value in data + for (int i = 0; i < e.size; ++i, value >>= 8) + seg.data[e.offset + i] = value & 0xff; + + seg.relocs.emplace_back(r); + } else { + omf::interseg r; + r.size = e.size; + r.offset = e.offset; + r.segment = loc.section; + r.segment_offset = loc.value; + r.shift = shift_value; + + seg.intersegs.emplace_back(r); + } + return; + } + + warnx("Relocation expression too complex."); + flags._errors++; + return; + +} + + +void build_omf_segments() { + + + std::vector< std::pair > remap; + + remap.resize(sections.size()); + + +#if 0 + if (!flags._X) { + // create an expressload segments. + // (should verify it's expressable later...) + omf::segment seg; + seg.segnum = omf_segments.size()+ 1; + seg.kind = 0x8001; // dynamic data segment + seg.segname = "~ExpressLoad"; + + omf_segments.emplace_back(std::move(seg)); + + } +#endif + + + // if data + code can fit in one bank, merge them + // otherwise, merge all data sections and 1 omf segment + // per code section. + + // code is next segment... + unsigned code_segment = 0; + unsigned data_segment = 0; + { + omf_segments.emplace_back(); + auto &seg = omf_segments.back(); + + code_segment = data_segment = seg.segnum = omf_segments.size(); + seg.kind = 0x0000; // static code segment. + + auto &s = sections[SECT_CODE]; + + remap[s.number] = std::make_pair(code_segment, 0); + append(seg.data, s.data); + s.data.clear(); + } + - uint32_t total_data_size = 0; uint32_t total_code_size = 0; - unsigned data_sections = 0; - unsigned code_sections = 0; - + uint32_t total_data_size = 0; for (const auto &s : sections) { if (s.flags & SEC_REF_ONLY) continue; if (s.flags & SEC_DATA) { - total_data_size += s.data.size(); - data_sections++; - } - else { - if (s.data.size() > 0xffff) { - flags._errors++; - warnx("code section %s ($%04x) exceeds bank size.", - s.name.c_str(), (uint32_t)s.data.size()); - } - total_code_size += s.data.size(); - code_sections++; + total_data_size += s.size; + } else { + total_code_size += s.size; } } - // add in udata... + // add in UDATA total_data_size += sections[SECT_UDATA].size; + if (total_data_size + sections[SECT_CODE].size > 0xffff) { - if (sections[SECT_CODE].data.size() + total_data_size < 0xffff) { - auto &s = sections[SECT_CODE]; - new_number = SECT_CODE; - new_data = std::move(s.data); - new_expr = std::move(s.expressions); - s.data.clear(); - s.expressions.clear(); - } else { - new_number = SECT_DATA; + omf_segments.emplace_back(); + + auto &seg = omf_segments.back(); + data_segment = seg.segnum = omf_segments.size(); + seg.kind = 0x0001; // static data segment. } - - // new section, offset fudge pair. - std::vector< std::pair > remap; - remap.reserve(sections.size()); - - for (unsigned i = 0; i < sections.size(); ++i) - remap.emplace_back(std::make_pair(i, 0)); - - - if (total_data_size > 0xffff) { - warnx("total data ($%04x) exceeds bank size,", total_data_size); - } - - - // merge all code sections? - if (total_code_size <= 0xffff && code_sections > 0) { - - } + //omf::segment &code_seg = omf_segments[code_segment-1]; + omf::segment &data_seg = omf_segments[data_segment-1]; + // KDATA, DATA, UDATA, other segment order. for (auto &s : sections) { if (s.flags & SEC_REF_ONLY) continue; if ((s.flags & SEC_DATA) == 0) continue; - uint32_t offset = new_data.size(); - remap[s.number] = std::make_pair(new_number, offset); - new_data.insert(new_data.end(), s.data.begin(), s.data.end()); - new_expr.insert(new_expr.end(), - std::make_move_iterator(s.expressions.begin()), - std::make_move_iterator(s.expressions.end()) - ); + remap[s.number] = std::make_pair(data_segment, data_seg.data.size()); - // preserve the name and flags. + append(data_seg.data, s.data); s.data.clear(); - s.expressions.clear(); - s.size = 0; } - // add in SECT_UDATA + // add in UDATA { auto &s = sections[SECT_UDATA]; - uint32_t offset = new_data.size(); - - remap[s.number] = std::make_pair(new_number, offset); - // reference only -- no expressions or data. - new_data.insert(new_data.end(), s.size, 0); - - s.size = 0; + remap[s.number] = std::make_pair(data_segment, data_seg.data.size()); + append(data_seg.data, s.size, (uint8_t)0); } - { - auto &s = sections[new_number]; - s.size = new_data.size(); - s.data = std::move(new_data); - s.expressions = std::move(new_expr); + // for all other sections, create a new segment. + for (auto &s : sections) { + if (s.flags & SEC_REF_ONLY) continue; + if (s.flags & SEC_DATA) continue; + if (s.number == SECT_CODE) continue; + + omf::segment seg; + seg.segnum = omf_segments.size() + 1; + seg.kind = 0x0000; // static code. + seg.data = std::move(s.data); + seg.segname = s.name; + s.data.clear(); + + remap[s.number] = std::make_pair(seg.segnum, 0); + + omf_segments.emplace_back(std::move(seg)); } + // add a stack segment at the end + if (flags._S) { + auto &s = sections[SECT_PAGE0]; + // create stack/dp segment. + uint32_t size = s.size; + if (size) { + // ???? + size = (size + 255) & ~255; - // now remap symbols - for (auto &s : symbols) { - if ((s.type & 0x0f) == S_REL) { - auto x = remap[s.section]; - s.section = x.first; - s.offset += x.second; + omf::segment seg; + seg.segnum = omf_segments.size() + 1; + seg.kind = 0x12; // static dp/stack segment. + seg.data.resize(size, 0); + seg.loadname = "~Stack"; + omf_segments.emplace_back(std::move(seg)); + + // remap SECT_PAGE0... + remap[s.number] = std::make_pair(seg.segnum, 0); + + } else { + warnx("page0 is 0 sized. Stack/dp segment not created."); } } - // and expressions... - - // update expressions with new section / offset. - for (auto &s : sections) { + // now adjust all the expressions, simplify, and convert to reloc records. + for (auto &s :sections) { for (auto &e : s.expressions) { - bool delta = false; + for (auto &t : e.stack) { if (t.tag == OP_LOC) { - auto x = remap[t.section]; - if (x.first != t.section || x.second != 0) { - delta = true; - t.section = x.first; - t.value += x.second; - } + const auto &x = remap[t.section]; + t.section = x.first; + t.value += x.second; } } - if (delta) simplify_expression(e); + simplify_expression(e); + to_omf(e, omf_segments[s.number-1]); } } + // and we're done... } + + bool one_file(const std::string &name) { int fd = open(name.c_str(), O_RDONLY | O_BINARY); @@ -855,10 +1021,6 @@ void usage() { int main(int argc, char **argv) { - std::string _o; - bool _C = false; - bool _X = false; - std::vector _l; std::vector _L; @@ -867,9 +1029,9 @@ int main(int argc, char **argv) { while ((c = getopt(argc, argv, "vCXL:l:o:")) != -1) { switch(c) { case 'v': flags._v = true; break; - case 'X': _X = true; break; - case 'C': _C = true; break; - case 'o': _o = optarg; break; + case 'X': flags._X = true; break; + case 'C': flags._C = true; break; + case 'o': flags._o = optarg; break; case 'l': _l.emplace_back(optarg); break; case 'L': _L.emplace_back(optarg); break; case 'h': help(); break; @@ -905,7 +1067,7 @@ int main(int argc, char **argv) { if (flags._v) { - for (auto &s : sections) { + for (const auto &s : sections) { //if (s.flags & SEC_REF_ONLY) continue; printf("section %3d %-20s $%04x $%04x\n", s.number, s.name.c_str(), (uint32_t)s.data.size(), s.size); @@ -913,17 +1075,28 @@ int main(int argc, char **argv) { fputs("\n", stdout); } - merge_data(); + build_omf_segments(); if (flags._v) { - for (auto &s : sections) { - //if (s.flags & SEC_REF_ONLY) continue; - printf("section %3d %-20s $%04x $%04x\n", - s.number, s.name.c_str(), (uint32_t)s.data.size(), s.size); + for (const auto &s : omf_segments) { + printf("segment %3d %-20s $%04x\n", + s.segnum, s.segname.c_str(), (uint32_t)s.data.size()); + + for (auto &r : s.relocs) { + printf(" %02x %02x %06x %06x\n", + r.size, r.shift, r.offset, r.value); + } + for (auto &r : s.intersegs) { + printf(" %02x %02x %06x %02x %04x %06x\n", + r.size, r.shift, r.offset, r.file, r.segment, r.segment_offset); + } } - fputs("\n", stdout); } + void save_omf(std::vector &segments, bool expressload, const std::string &path); + + save_omf(omf_segments, false, "out.omf"); + } diff --git a/omf.cpp b/omf.cpp new file mode 100644 index 0000000..c384c22 --- /dev/null +++ b/omf.cpp @@ -0,0 +1,147 @@ +#include "omf.h" + +#include +#include +#include + +#include +#include +#include +#include + +#pragma pack(push, 1) +struct omf_header { + uint32_t bytecount = 0; + uint32_t reserved_space = 0; + uint32_t length = 0; + uint8_t unused1 = 0; + uint8_t lablen = 0; + uint8_t numlen = 4; + uint8_t version = 2; + uint32_t banksize = 0; + uint16_t kind = 0; + uint16_t unused2 = 0; + uint32_t org = 0; + uint32_t alignment = 0; + uint8_t numsex = 0; + uint8_t unused3 = 0; + uint16_t segnum = 0; + uint32_t entry = 0; + uint16_t dispname = 0; + uint16_t dispdata = 0; +}; + +#pragma pack(pop) + +void push(std::vector &v, uint8_t x) { + v.push_back(x); +} + +void push(std::vector &v, uint16_t x) { + v.push_back(x & 0xff); + x >>= 8; + v.push_back(x & 0xff); +} + +void push(std::vector &v, uint32_t x) { + v.push_back(x & 0xff); + x >>= 8; + v.push_back(x & 0xff); + x >>= 8; + v.push_back(x & 0xff); + x >>= 8; + v.push_back(x & 0xff); +} + +void push(std::vector &v, const std::string &s) { + uint8_t count = std::min((int)s.size(), 255); + push(v, count); + v.insert(v.end(), s.begin(), s.end()); +} + + +void save_omf(std::vector &segments, bool expressload, const std::string &path) { + + if (expressload) { + for (auto &s : segments) { + s.segnum++; + for (auto &r : s.intersegs) r.segment++; + } + } + + int fd; + fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd < 0) { + err(EX_CANTCREAT, "Unable to open %s", path.c_str()); + } + + + for (auto &s : segments) { + omf_header h; + h.length = s.data.size(); + h.kind = s.kind; + h.banksize = s.data.size() > 0xffff ? 0x0000 : 0x010000; + h.segnum = s.segnum; + + std::vector data; + + // push segname and load name onto data. + data.insert(data.end(), 10, ' '); + push(data, s.segname); + + h.dispname = sizeof(omf_header); + h.dispdata = sizeof(omf_header) + data.size(); + + //lconst record + push(data, (uint8_t)0xf2); + push(data, (uint32_t)h.length); + data.insert(data.end(), s.data.begin(), s.data.end()); + + // should interseg/reloc records be sorted? + // todo -- compress into super records. + for (const auto &r : s.relocs) { + if (r.can_compress()) { + push(data, (uint8_t)0xf5); + push(data, (uint8_t)r.size); + push(data, (uint8_t)r.shift); + push(data, (uint16_t)r.offset); + push(data, (uint16_t)r.value); + } else { + push(data, (uint8_t)0xe5); + push(data, (uint8_t)r.size); + push(data, (uint8_t)r.shift); + push(data, (uint32_t)r.offset); + push(data, (uint32_t)r.value); + } + } + + for (const auto &r : s.intersegs) { + if (r.can_compress()) { + push(data, (uint8_t)0xf6); + push(data, (uint8_t)r.size); + push(data, (uint8_t)r.shift); + push(data, (uint16_t)r.offset); + push(data, (uint16_t)r.segment); + push(data, (uint16_t)r.segment_offset); + } else { + push(data, (uint8_t)0xe3); + push(data, (uint8_t)r.size); + push(data, (uint8_t)r.shift); + push(data, (uint32_t)r.offset); + push(data, (uint16_t)r.file); + push(data, (uint32_t)r.segment); + push(data, (uint32_t)r.segment_offset); + } + } + + // end-of-record + push(data, (uint8_t)0x00); + + h.bytecount = data.size() + sizeof(omf_header); + + write(fd, &h, sizeof(h)); + write(fd, data.data(), data.size()); + } + + close(fd); +} \ No newline at end of file diff --git a/omf.h b/omf.h new file mode 100644 index 0000000..b048f54 --- /dev/null +++ b/omf.h @@ -0,0 +1,52 @@ +#ifndef __omf_h__ +#define __omf_h__ + +#include +#include +#include + +namespace omf { + + struct reloc { + uint8_t size = 0; + uint8_t shift = 0; + uint32_t offset = 0; + uint32_t value = 0; + + constexpr bool can_compress() const { + return offset <= 0xffff && value <= 0xffff; + } + }; + + + struct interseg { + + uint8_t size = 0; + uint8_t shift = 0; + uint32_t offset = 0; + uint16_t file = 1; + uint16_t segment = 0; + uint32_t segment_offset = 0; + + constexpr bool can_compress() const { + return file == 1 && segment <= 255 && offset <= 0xffff && segment_offset <= 0xffff; + } + }; + + struct segment { + + uint16_t segnum = 0; + uint16_t kind = 0; + + std::string loadname; + std::string segname; + + std::vector data; + std::vector intersegs; + std::vector relocs; + }; + + +} + +#endif