From 9723ce1f0001acda76c7f9ba384012dfa7f06d25 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sun, 17 May 2020 17:01:03 -0400 Subject: [PATCH] support for linker 3, generate OMF object files. --- link.cpp | 256 +++++++++++++++++++++++++++++++++++++++++++++++++++---- omf.cpp | 35 ++++++++ 2 files changed, 276 insertions(+), 15 deletions(-) diff --git a/link.cpp b/link.cpp index a4b1557..5867338 100644 --- a/link.cpp +++ b/link.cpp @@ -483,6 +483,7 @@ static void process_unit(const std::string &path) { len_offset = offset; } + static void import(const std::string &path, const std::string &name) { std::error_code ec; @@ -511,13 +512,14 @@ static void import(const std::string &path, const std::string &name) { len_offset = mf.size(); } -static void resolve(void) { +static void resolve(bool allow_unresolved = false) { for (unsigned ix = 0; ix < segments.size(); ++ix) { auto &seg = segments[ix]; auto &pending = relocations[ix]; + std::vector unresolved; if ((seg.kind & 0x0001) == 0x0001 && seg.data.size() > 65535) { throw std::runtime_error("code exceeds bank"); @@ -527,12 +529,16 @@ static void resolve(void) { assert(r.id < symbol_map.size()); const auto &e = symbol_table[r.id]; - /* if this is an absolute value, do the math */ if (!e.defined) { - warnx("%s is not defined", e.name.c_str()); + if (allow_unresolved) { + unresolved.emplace_back(std::move(r)); + } else { + warnx("%s is not defined", e.name.c_str()); + } continue; } + /* if this is an absolute value, do the math */ if (e.absolute) { uint32_t value = e.value + r.value; /* shift is a uint8_t so negating doesn't work right */ @@ -572,6 +578,11 @@ static void resolve(void) { std::sort(seg.intersegs.begin(), seg.intersegs.end(), [](const auto &a, const auto &b){ return a.offset < b.offset; }); + + std::sort(unresolved.begin(), unresolved.end(), [](const auto &a, const auto &b){ + return a.offset < b.offset; + }); + pending = std::move(unresolved); } } @@ -651,6 +662,215 @@ void finish(void) { relocations.clear(); } +namespace { + + 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.begin() + count); + } + + void push(std::vector &v, const std::string &s, size_t count) { + std::string tmp(s, 0, count); + tmp.resize(count, ' '); + v.insert(v.end(), tmp.begin(), tmp.end()); + } + +} + +static void add_expr(std::vector &buffer, const omf::reloc &r, int ix) { + + push(buffer, omf::opcode::EXPR); + push(buffer, static_cast(r.size)); + + if (ix >= 0) { + /* external */ + push(buffer, static_cast(0x83)); /* label reference */ + push(buffer, symbol_table[ix].name); + + if (r.value) { + + push(buffer, static_cast(0x81)); /* abs */ + push(buffer, static_cast(r.value)); + push(buffer, static_cast(0x01)); /* + */ + } + } else { + push(buffer, static_cast(0x87)); /* rel */ + push(buffer, static_cast(r.value)); + } + + if (r.shift){ + push(buffer, static_cast(0x81)); /* abs */ + push(buffer, static_cast(static_cast(r.shift))); + push(buffer, static_cast(0x07)); /* << */ + } + + push(buffer, static_cast(0)); /* end of expr */ + +} + +/* REL to OMF object file */ +/* relocations and labels need to be placed inline */ +void finish3(void) { + + resolve(true); /* allow unresolved references */ + + std::vector< std::pair > globals; + + auto &seg = segments.back(); + auto &unresolved = relocations.back(); + auto &resolved = seg.relocs; + auto &data = seg.data; + + std::vector buffer; + /* 1. generate GEQU for all global equates */ + for (const auto &sym : symbol_table) { + if (sym.defined) { + if (sym.absolute) { + + push(buffer, omf::opcode::GEQU); + push(buffer, sym.name); + push(buffer, static_cast(0x00)); /* length attr */ + push(buffer, static_cast('G')); /* type attr */ + push(buffer, static_cast(sym.value)); + } else { + globals.emplace_back(sym.value, sym.name); + } + } + } + std::sort(globals.begin(), globals.end()); + + + + auto iter1 = globals.begin(); + auto iter2 = unresolved.begin(); + auto iter3 = resolved.begin(); + + std::vector breaks; + for (const auto &x : globals) { + breaks.push_back(x.first); + } + for (const auto &x : resolved) { + breaks.push_back(x.offset); + } + for (const auto &x : unresolved) { + breaks.push_back(x.offset); + } + /* sort in reverse order */ + std::sort(breaks.begin(), breaks.end(), std::greater()); + breaks.erase(std::unique(breaks.begin(), breaks.end()), breaks.end()); + + + unsigned pc = 0; + unsigned offset = 0; + for(;;) { + unsigned next = data.size(); + + while (!breaks.empty() && breaks.back() < offset) breaks.pop_back(); + + if (!breaks.empty()) { + next = std::min(next, breaks.back()); + breaks.pop_back(); + } + + if (next < offset) + throw std::runtime_error("relocation offset error"); + + unsigned size = next - offset; + if (size) { + if (size <= 0xdf) + push(buffer, static_cast(size)); + else { + push(buffer, omf::opcode::LCONST); + push(buffer, static_cast(size)); + } + while (offset < next) buffer.push_back(data[offset++]); + pc += size; + } + + + /* global expr global expr */ + for(;;) { + bool delta = false; + while (iter1 != globals.end() && iter1->first == offset) { + /* add global record */ + push(buffer, omf::opcode::GLOBAL); + push(buffer, iter1->second); /* name */ + push(buffer, static_cast(0x00)); /* length attr */ + push(buffer, static_cast('N')); /* type attr */ + push(buffer, static_cast(0x00)); /* public */ + ++iter1; + } + + if (iter2 != unresolved.end() && iter2->offset == offset) { + const auto &r = *iter2; + add_expr(buffer, r, r.id); + offset += r.size; + pc += r.size; + delta = true; + ++iter2; + } + if (iter3 != resolved.end() && iter3->offset == offset) { + const auto &r = *iter3; + add_expr(buffer, r, -1); + offset += r.size; + pc += r.size; + delta = true; + ++iter3; + } + if (!delta) break; + } + if (offset >= data.size()) break; + } + + push(buffer, omf::opcode::END); + seg.data = std::move(buffer); + + if (iter1 != globals.end()) + throw std::runtime_error("label offset error"); + if (iter2 != unresolved.end()) + throw std::runtime_error("relocation offset error"); + if (iter3 != resolved.end()) + throw std::runtime_error("relocation offset error"); + + void save_object(const std::string &path, omf::segment &s, uint32_t length); + + + std::string path = save_file; + if (path.empty()) path = "omf.out"; + if (verbose) printf("Saving %s\n", path.c_str()); + + try { + save_object(path, seg, pc); + set_file_type(path, 0xb1, 0x0000); + } catch (std::exception &ex) { + errx(EX_OSERR, "%s: %s", path.c_str(), ex.what()); + } + + print_symbols(); + segments.clear(); + relocations.clear(); +} void lib(const std::string &path) { @@ -865,10 +1085,10 @@ void evaluate(label_t label, opcode_t opcode, const char *cursor) { uint32_t value = number_operand(cursor, local_symbol_table); switch (value) { - case 3: throw std::runtime_error("object file linker not supported"); case 0: case 1: case 2: + case 3: lkv = value; break; default: @@ -921,10 +1141,10 @@ void evaluate(label_t label, opcode_t opcode, const char *cursor) { if (loadname.empty()) loadname = base; /* - lkv 0 = binary linker (unsupported) + lkv 0 = binary linker lkv 1 = 1 segment GS linker lkv 2 = multi-segment GS linker - lkv 3 = convert REL to OMF object file (unsupported) + lkv 3 = convert REL to OMF object file */ if (lkv == 1 || lkv == 2 || lkv == 3) { @@ -935,17 +1155,23 @@ void evaluate(label_t label, opcode_t opcode, const char *cursor) { // seg.kind = kind; } - if (lkv == 0 || lkv == 1) { - finish(); - // reset. could have another link afterwards. - new_segment(true); + switch (lkv) { + case 0: + case 1: + finish(); + new_segment(true); + break; + case 2: + if (verbose) printf("Segment %d: %s\n", seg.segnum, base.c_str()); + /* add a new segment */ + new_segment(); + break; + case 3: + finish3(); + new_segment(true); + break; } - if (lkv == 2) { - if (verbose) printf("Segment %d: %s\n", seg.segnum, base.c_str()); - /* add a new segment */ - new_segment(); - } ++sav; break; } diff --git a/omf.cpp b/omf.cpp index 89649a4..6caf2ff 100644 --- a/omf.cpp +++ b/omf.cpp @@ -383,6 +383,41 @@ void save_bin(const std::string &path, omf::segment &segment, uint32_t org) { close(fd); } +void save_object(const std::string &path, omf::segment &s, uint32_t length) { + + /* data is already in OMF format. */ + + int fd; + fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); + if (fd < 0) { + err(EX_CANTCREAT, "Unable to open %s", path.c_str()); + } + + omf_header h; + h.length = length + s.reserved_space; + h.kind = s.kind; + h.banksize = length > 0xffff ? 0x0000 : 0x010000; + h.segnum = 0; + h.alignment = s.alignment; + h.reserved_space = s.reserved_space; + + std::vector data; + + // push segname and load name onto data. + // data.insert(data.end(), 10, ' '); + push(data, s.loadname, 10); + push(data, s.segname); + + h.dispname = sizeof(omf_header); + h.dispdata = sizeof(omf_header) + data.size(); + h.bytecount = sizeof(omf_header) + data.size() + s.data.size(); + + unsigned offset = 0; + offset += write(fd, &h, sizeof(h)); + offset += write(fd, data.data(), data.size()); + offset += write(fd, s.data.data(), s.data.size()); + close(fd); +} void save_omf(const std::string &path, std::vector &segments, bool compress, bool expressload) {