commit 5159061ff08e0461fb4ef1f1a63111bdd7e684d5 Author: Kelvin Sherlock Date: Sun Dec 8 13:55:33 2019 -0500 initial WIP diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0f44e56 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +LINK.o = $(LINK.cc) +CXXFLAGS = -std=c++17 -g -Wall -Wno-sign-compare +CCFLAGS = -g + +.PHONY: all + +all: rel-link + + +o: + mkdir $< + +rel-link: o/link.o o/mapped_file.o o/omf.o + $(LINK.o) $^ $(LDLIBS) -o $@ + +o/mapped_file.o : mapped_file.cpp mapped_file.h unique_resource.h +o/link.o : link.cpp mapped_file.h omf.h +o/omf.o : omf.cpp omf.h + +o/%.o: %.cpp | o + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.cpp: %.re2c + re2c -W -o $@ $< diff --git a/link.cpp b/link.cpp new file mode 100644 index 0000000..b81fd51 --- /dev/null +++ b/link.cpp @@ -0,0 +1,261 @@ +/* c++17 */ + +#include +#include +#include + +#include "omf.h" + +void save_omf(const std::string &path, std::vector &segments, bool compress, bool expressload); + + +struct symbol { + std::string name; + std::string file; + uint32_t value = 0; + unsigned id = 0; + unsigned count = 0; + + bool absolute = false; + bool defined = false; +}; + +struct pending_reloc : public omf::reloc { + unsigned id = 0; +}; + +std::unordered_map symbol_map; +std::vector symbol_table; + +reference *find_symbol(const std::string &name) { + + auto iter = symbol_map.find(name); + if (iter != symbol_map.end()) return &symbol_table[*iter - 1]; + + unsigned id = symbol_table.size() + 1; + symbol_map.emplace(name, id); + + auto &rv = symbol_table.emplace_back(); + rv.name = name; + rv.id = id; + return *rv; +} + + + +uint32_t start = 0; /* starting point of current unit */ +std::vector remap; + + +process_labels(std::span &data) { + + for(;;) { + assert(data.size()) + unsigned flag = data[0]; + if (flag == 0x00) return; + + unsigned length = flag & 0x1f; + assert(length != 0); + assert(data.size() >= length + 4); + + std::string name(data.data() + 1, data.data() + 1 + length); + data.remove_prefix(1 + length); + uint32_t value = data[0] | (data[1] << 8) | (data[2] << 16); + data.remove_prefix(3); + + reference *e = find_symbol(name); + switch (flag & ~0x1f) { + case SYMBOL_EXTERNAL: + /* map the unit symbol # to a global symbol # */ + value &= 0x7fff; + if (remap.size() < value + 1) + remap.resize(value + 1); + remap[value] = e->id; + break; + + + case SYMBOL_ENTRY+SYMBOL_ABSOLUTE: + if (e->defined && e->absolute && e->value == value) + break; /* allow redef */ + + case SYMBOL_ENTRY: + if (e->defined) { + warnx("%s previously defined (%s)", e->name, e->file); + break; + } + e->defined = true; + e->file = file; + if (flag & SYMBOL_ABSOLUTE) { + e->absolute = true; + e->value = value; + } else { + e->absolute = false; + e->value = value - 0x8000 + start; + } + break; + default: + } + } +} + + +process_reloc(std::span &data) { + + for(;;) { + assert(data.size()); + unsigned flag = data[0]; + if (flag == 0x00) return; + + assert(data.size() >= 4); + + uint32_t offset = data[1] | (data[2] << 8); + unsigned x = data[3]; + data.remove_prefix(4); + + offset += start; + bool external = false; + unsigned shift = 0; + uint32_t value = 0; + unsigned size = 0; + + if (flag == 0xff) { + /* shift */ + assert(data.size() >= 4); + unsigned flag = data[0]; + value = data[1] | (data[2] << 8) | (data[3] << 16); + value -= 0x8000; + external = flag & 0x04; + switch(flag & ~0x04) { + case 0xd0: + shift = 16; + size = 1; + break; + case 0xd1: + shift = 8; + size = 2; + break; + case 0xd3: + shift = 8; + size = 1; + break; + default: /* bad */ + } + } else { + assert(flag & ~(0x0f|0x10|0x20|0x80) == 0); + + unsigned size = 0; + switch(flag & (0x80 + 0x20)) { + case 0: size = 1; + case 0x20: size = 3; + case 0x80: size = 2; + default: /* bad size */ + } + external = flag & 0x10; + + switch(size) { + case 3: value |= seg_data[offset+2] << 16; + case 2: value |= seg_data[offset+1] << 8; + case 1: value |= seg_data[offset+0]; + } + + + if (size > 1) value -= 0x8000; + value += start; + + } + + /* external resolutions are deferred for later */ + + if (external) { + /* x = local symbol # */ + reloc r; + assert(x < remap.size()); + r.id = remap[x]; /* label reference is 0-based */ + r.size = size; + r.offset = offset; + r.value = value; + r.shift = shift; + + relocations.emplace_back(r); + } else { + uint32_t value = 0; + omf::reloc r; + r.size = size; + r.offset = start; + r.value = value; + r.shift = shift; + + seg.relocs.emplace_back(r); + } + + } +} + +void add_libraries() { + auto iter = libs.begin(); + auto end = libs.end(); + + for(;;) { + + + + } +} + + + +process_unit(span data) { + + /* skip over relocs, do symbols first */ + remap.clear(); + + span rr = data; + for(;;) { + if (data[0] == 0) break; + data.remove_prefix(4); + } + data.remove_prefix(1); + process_labels(data); + assert(data.length() == 1); + + /* now relocations */ + process_reloc(rr); +} + + +finalize(void) { + + for (auto &r in relocations) { + assert(r.id <= symbol_map.length()); + const auto &label = symbol_map[rr.id]; + + r.value = label.value; + seg.relocs.emplace_back(r); + } + relocations.clear(); +} + +void print_symbols(void) { + + if (symbol_table.empty()) return; + + /* alpha */ + std::sort(symbol_table.begin(), symbol_table.end(), + [](const symbol &a, const symbol &b){ + return a.name < b.name; + }); + + for (const auto &lab : symbol_table) { + fprintf(stdout, "%-20s: $%06x\n", lab.name.c_str(), lab.value); + } + fputs("\n", stdout); + /* numeric */ + std::sort(symbol_table.begin(), symbol_table.end(), + [](const symbol &a, const symbol &b){ + return a.value < b.value; + }); + + for (const auto &lab : symbol_table) { + fprintf(stdout, "%-20s: $%06x\n", lab.name.c_str(), lab.value); + } +} \ No newline at end of file diff --git a/mapped_file.cpp b/mapped_file.cpp new file mode 100644 index 0000000..a368431 --- /dev/null +++ b/mapped_file.cpp @@ -0,0 +1,389 @@ +#include "mapped_file.h" +#include "unique_resource.h" +#include +#include +#include + + +namespace { + + void set_or_throw_error(std::error_code *ec, int error, const std::string &what) { + if (ec) *ec = std::error_code(error, std::system_category()); + else throw std::system_error(error, std::system_category(), what); + } + +} + +#ifdef _WIN32 +#include + +namespace { + + /* + * allocating a new string could reset GetLastError() to 0. + */ + void set_or_throw_error(std::error_code *ec, const char *what) { + auto e = GetLastError(); + set_or_throw_error(ec, e, what); + } + + void set_or_throw_error(std::error_code *ec, const std::string &what) { + auto e = GetLastError(); + set_or_throw_error(ec, e, what); + } + + + template + HANDLE CreateFileX(const std::string &s, Args... args) { + return CreateFileA(s.c_str(), std::forward(args)...); + } + + template + HANDLE CreateFileX(const std::wstring &s, Args... args) { + return CreateFileW(s.c_str(), std::forward(args)...); + } + +} + +void mapped_file_base::close() { + if (is_open()) { + + UnmapViewOfFile(_data); + CloseHandle(_map_handle); + CloseHandle(_file_handle); + reset(); + } +} + +template +void mapped_file_base::open_common(const T& p, mapmode flags, size_t length, size_t offset, std::error_code *ec) { + + if (ec) ec->clear(); + + HANDLE fh; + HANDLE mh; + + // length of 0 in CreateFileMapping / MapViewOfFile + // means map the entire file. + + if (is_open()) close(); + + fh = CreateFileX(p, + flags == readonly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + flags == readonly ? FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL, + nullptr + ); + if (fh == INVALID_HANDLE_VALUE) + return set_or_throw_error(ec, "CreateFile"); + + auto fh_close = make_unique_resource(fh, CloseHandle); + + + if (length == -1) { + LARGE_INTEGER file_size; + GetFileSizeEx(fh, &file_size); + length = (size_t)file_size.QuadPart; + } + + if (length == 0) return; + + DWORD protect = 0; + DWORD access = 0; + switch (flags) { + case readonly: + protect = PAGE_READONLY; + access = FILE_MAP_READ; + break; + case readwrite: + protect = PAGE_READWRITE; + access = FILE_MAP_WRITE; + break; + case priv: + protect = PAGE_WRITECOPY; + access = FILE_MAP_COPY; + break; + } + + mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0); + if (mh == INVALID_HANDLE_VALUE) + return set_or_throw_error(ec, "CreateFileMapping"); + + auto mh_close = make_unique_resource(mh, CloseHandle); + + + ULARGE_INTEGER ll; + ll.QuadPart = offset; + + _data = MapViewOfFileEx(mh, + access, + ll.HighPart, + ll.LowPart, + length, + nullptr); + if (!_data) + return set_or_throw_error(ec, "MapViewOfFileEx"); + + + _file_handle = fh_close.release(); + _map_handle = mh_close.release(); + _size = length; + _flags = flags; +} + +void mapped_file_base::open(const std::string &p, mapmode flags, size_t length, size_t offset, std::error_code *ec) { + open_common(p, flags, length, offset, ec); +} + +void mapped_file_base::open(const std::wstring &p, mapmode flags, size_t length, size_t offset, std::error_code *ec) { + open_common(p, flags, length, offset, ec); +} + + + + +template +void mapped_file_base::create_common(const T& p, size_t length, std::error_code *ec) { + + if (ec) ec->clear(); + + const size_t offset = 0; + + HANDLE fh; + HANDLE mh; + LARGE_INTEGER file_size; + + const DWORD protect = PAGE_READWRITE; + const DWORD access = FILE_MAP_WRITE; + + + if (is_open()) close(); + + fh = CreateFileX(p, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, + nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr + ); + if (fh == INVALID_HANDLE_VALUE) + return set_or_throw_error(ec, "CreateFile"); + + auto fh_close = make_unique_resource(fh, CloseHandle); + + if (length == 0) return; + + file_size.QuadPart = length; + if (!SetFilePointerEx(fh, file_size, nullptr, FILE_BEGIN)) + return set_or_throw_error(ec, "SetFilePointerEx"); + + if (!SetEndOfFile(fh)) + return set_or_throw_error(ec, "SetEndOfFile"); + + mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0); + if (mh == INVALID_HANDLE_VALUE) + return set_or_throw_error(ec, "CreateFileMapping"); + + auto mh_close = make_unique_resource(mh, CloseHandle); + + ULARGE_INTEGER ll; + ll.QuadPart = offset; + + _data = MapViewOfFileEx(mh, + access, + ll.HighPart, + ll.LowPart, + length, + nullptr); + + if (!_data) + return set_or_throw_error(ec, "MapViewOfFileEx"); + + _file_handle = fh_close.release(); + _map_handle = mh_close.release(); + + _size = length; + _flags = readwrite; +} + +void mapped_file_base::create(const std::string &p, size_t length, std::error_code *ec) { + create_common(p, length, ec); +} +void mapped_file_base::create(const std::wstring &p, size_t length, std::error_code *ec) { + create_common(p, length, ec); +} + +#else + +#include +#include +#include +#include +#include + +namespace { + + + void set_or_throw_error(std::error_code *ec, const char *what) { + set_or_throw_error(ec, errno, what); + } + + void set_or_throw_error(std::error_code *ec, const std::string &what) { + set_or_throw_error(ec, errno, what); + } + + +} + +void mapped_file_base::close() { + if (is_open()) { + ::munmap(_data, _size); + ::close(_fd); + reset(); + } +} + + +void mapped_file_base::open(const std::string& p, mapmode flags, size_t length, size_t offset, std::error_code *ec) { + + if (ec) ec->clear(); + + int fd; + + int oflags = 0; + + if (is_open()) close(); + + switch (flags) { + case readonly: + oflags = O_RDONLY; + break; + default: + oflags = O_RDWR; + break; + } + + fd = ::open(p.c_str(), oflags); + if (fd < 0) { + return set_or_throw_error(ec, "open"); + } + + auto close_fd = make_unique_resource(fd, ::close); + + + if (length == -1) { + struct stat st; + + if (::fstat(fd, &st) < 0) { + set_or_throw_error(ec, "stat"); + return; + } + length = st.st_size; + } + + if (length == 0) return; + + _data = ::mmap(0, length, + flags == readonly ? PROT_READ : PROT_READ | PROT_WRITE, + flags == priv ? MAP_PRIVATE : MAP_SHARED, + fd, offset); + + if (_data == MAP_FAILED) { + _data = nullptr; + return set_or_throw_error(ec, "mmap"); + } + + _fd = close_fd.release(); + _size = length; + _flags = flags; +} + +void mapped_file_base::create(const std::string& p, size_t length, std::error_code *ec) { + + if (ec) ec->clear(); + + int fd; + const size_t offset = 0; + + if (is_open()) close(); + + fd = ::open(p.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666); + if (fd < 0) { + return set_or_throw_error(ec, "open"); + } + + + auto close_fd = make_unique_resource(fd, ::close); + + + if (length == 0) return; + + if (::ftruncate(fd, length) < 0) { + return set_or_throw_error(ec, "ftruncate"); + } + + + _data = ::mmap(0, length, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, offset); + + if (_data == MAP_FAILED) { + _data = nullptr; + return set_or_throw_error(ec, "mmap"); + } + + _fd = close_fd.release(); + _size = length; + _flags = readwrite; +} + + + +#endif + + +void mapped_file_base::reset() { + _data = nullptr; + _size = 0; + _flags = readonly; +#ifdef _WIN32 + _file_handle = nullptr; + _map_handle = nullptr; +#else + _fd = -1; +#endif +} + +void mapped_file_base::swap(mapped_file_base &rhs) +{ + if (std::addressof(rhs) != this) { + std::swap(_data, rhs._data); + std::swap(_size, rhs._size); + std::swap(_flags, rhs._flags); +#ifdef _WIN32 + std::swap(_file_handle, rhs._file_handle); + std::swap(_map_handle, rhs._map_handle); +#else + std::swap(_fd, rhs._fd); +#endif + } +} + +mapped_file::mapped_file(mapped_file &&rhs) : mapped_file() { + swap(rhs); + //rhs.reset(); +} + +mapped_file& mapped_file::operator=(mapped_file &&rhs) { + if (std::addressof(rhs) == this) return *this; + + swap(rhs); + rhs.close(); + //rhs.reset(); + return *this; +} + diff --git a/mapped_file.h b/mapped_file.h new file mode 100644 index 0000000..f4f9dcd --- /dev/null +++ b/mapped_file.h @@ -0,0 +1,237 @@ +#ifndef __mapped_file_h__ +#define __mapped_file_h__ + +#include +#include +#include + +class mapped_file_base { +public: + + + enum mapmode { readonly, readwrite, priv }; + enum createmode { truncate, exclusive }; + + void close(); + + bool is_open() const { + return _data != nullptr; + } + + size_t size() const { + return _size; + } + + operator bool() const { return is_open(); } + bool operator !() const { return !is_open(); } + + ~mapped_file_base() { close(); } + +protected: + + void swap(mapped_file_base &rhs); + + void open(const std::string &p, mapmode flags, size_t length, size_t offset, std::error_code *ec); + void create(const std::string &p, size_t new_size, std::error_code *ec); // always creates readwrite. + +#ifdef _WIN32 + + void open(const std::wstring &p, mapmode flags, size_t length, size_t offset, std::error_code *ec); + void create(const std::wstring &p, size_t new_size, std::error_code *ec); // always creates readwrite. + + template + void open_common(const S &p, mapmode flags, size_t length, size_t offset, std::error_code *ec); + + template + void create_common(const S &p, size_t new_size, std::error_code *ec); // always creates readwrite. + +#endif + + void reset(); + + + size_t _size = 0; + void *_data = nullptr; + mapmode _flags = readonly; + +#ifdef _WIN32 + void *_file_handle = nullptr; + void *_map_handle = nullptr; +#else + int _fd = -1; +#endif +}; + + + +class mapped_file : public mapped_file_base { + + typedef mapped_file_base base; + +public: + + typedef unsigned char value_type; + + typedef value_type *iterator; + typedef const value_type *const_iterator; + + typedef value_type &reference ; + typedef const value_type &const_reference; + + + mapped_file() = default; + mapped_file(mapped_file &&); + mapped_file(const mapped_file &) = delete; + + + mapped_file(const std::string &p, mapmode flags = readonly, size_t length = -1, size_t offset = 0) { + open(p, flags, length, offset); + } + + mapped_file(const std::string &p, std::error_code &ec) noexcept { + open(p, readonly, -1, 0, ec); + } + mapped_file(const std::string &p, mapmode flags, std::error_code &ec) noexcept { + open(p, flags, -1, 0, ec); + } + + mapped_file(const std::string &p, mapmode flags, size_t length, std::error_code &ec) noexcept { + open(p, flags, length, 0, ec); + } + + mapped_file(const std::string &p, mapmode flags, size_t length, size_t offset, std::error_code &ec) noexcept { + open(p, flags, length, offset, ec); + } + +#ifdef _WIN32 + mapped_file(const std::wstring &p, mapmode flags = readonly, size_t length = -1, size_t offset = 0) { + open(p, flags, length, offset); + } + + mapped_file(const std::wstring &p, std::error_code &ec) noexcept { + open(p, readonly, -1, 0, ec); + } + mapped_file(const std::wstring &p, mapmode flags, std::error_code &ec) noexcept { + open(p, flags, -1, 0, ec); + } + + mapped_file(const std::wstring &p, mapmode flags, size_t length, std::error_code &ec) noexcept { + open(p, flags, length, 0, ec); + } + + mapped_file(const std::wstring &p, mapmode flags, size_t length, size_t offset, std::error_code &ec) noexcept { + open(p, flags, length, offset, ec); + } + +#endif + + + mapped_file &operator=(mapped_file &&); + mapped_file &operator=(const mapped_file &) = delete; + + + void open(const std::string &p, mapmode flags, size_t length = -1, size_t offset = 0) { + base::open(p, flags, length, offset, nullptr); + } + void open(const std::string &p, std::error_code &ec) noexcept { + base::open(p, readonly, -1, 0, &ec); + } + void open(const std::string &p, mapmode flags, std::error_code &ec) noexcept { + base::open(p, flags, -1, 0, &ec); + } + void open(const std::string &p, mapmode flags, size_t length, std::error_code &ec) noexcept { + base::open(p, flags, length, 0, &ec); + } + void open(const std::string &p, mapmode flags, size_t length, size_t offset, std::error_code &ec) noexcept { + base::open(p, flags, length, offset, &ec); + } + +#ifdef _WIN32 + void open(const std::wstring &p, mapmode flags, size_t length = -1, size_t offset = 0) { + base::open(p, flags, length, offset, nullptr); + } + void open(const std::wstring &p, std::error_code &ec) noexcept { + base::open(p, readonly, -1, 0, &ec); + } + void open(const std::wstring &p, mapmode flags, std::error_code &ec) noexcept { + base::open(p, flags, -1, 0, &ec); + } + void open(const std::wstring &p, mapmode flags, size_t length, std::error_code &ec) noexcept { + base::open(p, flags, length, 0, &ec); + } + void open(const std::wstring &p, mapmode flags, size_t length, size_t offset, std::error_code &ec) noexcept { + base::open(p, flags, length, offset, &ec); + } + +#endif + + void create(const std::string &p, size_t size) { + base::create(p, size, nullptr); + } + void create(const std::string &p, size_t size, std::error_code &ec) noexcept { + base::create(p, size, &ec); + } + +#ifdef _WIN32 + void create(const std::wstring &p, size_t size) { + base::create(p, size, nullptr); + } + void create(const std::wstring &p, size_t size, std::error_code &ec) noexcept { + base::create(p, size, &ec); + } +#endif + + + const value_type *data() const { + return (const value_type *)_data; + } + + value_type *data() { + return (value_type *)_data; + } + + const_iterator cbegin() const { + return data(); + } + + const_iterator cend() const { + return data() + size(); + } + + const_iterator begin() const { + return cbegin(); + } + + const_iterator end() const { + return cend(); + } + + + + + iterator begin() { + return (iterator)_data; + } + + iterator end() { + return (iterator)_data + size(); + } + + mapmode flags() const { + return _flags; + } + + void swap(mapped_file &rhs) { + base::swap(rhs); + } + +}; + +namespace std { + template + void swap(mapped_file &a, mapped_file &b) { + a.swap(b); + } +} + +#endif \ No newline at end of file diff --git a/omf.cpp b/omf.cpp new file mode 100644 index 0000000..ebc082e --- /dev/null +++ b/omf.cpp @@ -0,0 +1,504 @@ +#include "omf.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#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; +}; + +struct omf_express_header { + uint32_t lconst_mark; + uint32_t lconst_size; + uint32_t reloc_mark; + uint32_t reloc_size; + 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()); +} + +class super_helper { + + std::vector _data; + uint32_t _page = 0; + int _count = 0; + +public: + + super_helper() = default; + + + void append(uint32_t pc) { + unsigned offset = pc & 0xff; + unsigned page = pc >> 8; + assert(page >= _page); + + if (page != _page) { + unsigned skip = page - _page; + if (skip > 1) { + + while (skip >= 0x80) { + _data.push_back(0xff); + skip -= 0x7f; + } + if (skip) + _data.push_back(0x80 | skip); + } + _page = page; + _count = 0; + } + + if (!_count) { + _data.push_back(0); // count-1 place holder. + } else { + _data[_data.size() - _count - 1] = _count; // count is count - 1 at this point, + } + + _data.push_back(offset); + ++_count; + } + + void reset() { + _data.clear(); + _page = 0; + _count = 0; + } + + const std::vector &data() { + return _data; + } + +}; + + +enum { + SUPER_RELOC2, + SUPER_RELOC3, + SUPER_INTERSEG1, + SUPER_INTERSEG2, + SUPER_INTERSEG3, + SUPER_INTERSEG4, + SUPER_INTERSEG5, + SUPER_INTERSEG6, + SUPER_INTERSEG7, + SUPER_INTERSEG8, + SUPER_INTERSEG9, + SUPER_INTERSEG10, + SUPER_INTERSEG11, + SUPER_INTERSEG12, + SUPER_INTERSEG13, + SUPER_INTERSEG14, + SUPER_INTERSEG15, + SUPER_INTERSEG16, + SUPER_INTERSEG17, + SUPER_INTERSEG18, + SUPER_INTERSEG19, + SUPER_INTERSEG20, + SUPER_INTERSEG21, + SUPER_INTERSEG22, + SUPER_INTERSEG23, + SUPER_INTERSEG24, + SUPER_INTERSEG25, + SUPER_INTERSEG26, + SUPER_INTERSEG27, + SUPER_INTERSEG28, + SUPER_INTERSEG29, + SUPER_INTERSEG30, + SUPER_INTERSEG31, + SUPER_INTERSEG32, + SUPER_INTERSEG33, + SUPER_INTERSEG34, + SUPER_INTERSEG35, + SUPER_INTERSEG36, +}; + +uint32_t add_relocs(std::vector &data, size_t data_offset, omf::segment &seg, bool compress) { + + std::array< std::optional, 38 > ss; + + + uint32_t reloc_size = 0; + + for (auto &r : seg.relocs) { + + if (r.can_compress()) { + + if (compress) { + if (r.shift == 0 && r.size == 2) { + constexpr int n = SUPER_RELOC2; + + auto &sr = ss[n]; + if (!sr) sr.emplace(); + + uint32_t value = r.value; + sr->append(r.offset); + for (int i = 0; i < 2; ++i, value >>= 8) + data[data_offset + r.offset + i] = value; + continue; + } + + // sreloc 3 is for 3 bytes. however 4 bytes is also ok since + // it's 24-bit address space. + if (r.shift == 0 && (r.size == 2 || r.size == 3)) + { + constexpr int n = SUPER_RELOC3; + + auto &sr = ss[n]; + if (!sr) sr.emplace(); + + uint32_t value = r.value; + sr->append(r.offset); + for (int i = 0; i < 3; ++i, value >>= 8) + data[data_offset + r.offset + i] = value; + continue; + } + + // if size == 2 && shift == -16, -> SUPER INTERSEG + if (seg.segnum <= 12 && r.shift == 0xf0 && r.size == 2) { + + int n = SUPER_INTERSEG24 + seg.segnum; + auto &sr = ss[n]; + if (!sr) sr.emplace(); + + uint32_t value = r.value; + sr->append(r.offset); + for (int i = 0; i < 2; ++i, value >>= 8) + data[data_offset + r.offset + i] = value; + continue; + } + } + + push(data, (uint8_t)omf::cRELOC); + push(data, (uint8_t)r.size); + push(data, (uint8_t)r.shift); + push(data, (uint16_t)r.offset); + push(data, (uint16_t)r.value); + reloc_size += 7; + } else { + push(data, (uint8_t)omf::RELOC); + push(data, (uint8_t)r.size); + push(data, (uint8_t)r.shift); + push(data, (uint32_t)r.offset); + push(data, (uint32_t)r.value); + reloc_size += 11; + } + } + + for (const auto &r : seg.intersegs) { + if (r.can_compress()) { + + if (compress) { + + if (r.shift == 0 && r.size == 3) { + constexpr int n = SUPER_INTERSEG1; + auto &sr = ss[n]; + if (!sr) sr.emplace(); + + uint32_t value = r.segment_offset; + data[data_offset + r.offset + 0] = value; value >>= 8; + data[data_offset + r.offset + 1] = value; value >>= 8; + data[data_offset + r.offset + 2] = r.segment; + continue; + } + + + if (r.shift == 0 && r.size == 2 && r.segment <= 12) { + + int n = SUPER_INTERSEG12 + r.segment; + auto &sr = ss[n]; + if (!sr) sr.emplace(); + + uint32_t value = r.segment_offset; + sr->append(r.offset); + for (int i = 0; i < 2; ++i, value >>= 8) + data[data_offset + r.offset + i] = value; + continue; + } + + if (r.shift == 0xf0 && r.size == 2 && r.segment <= 12) { + + int n = SUPER_INTERSEG24 + r.segment; + auto &sr = ss[n]; + if (!sr) sr.emplace(); + + uint32_t value = r.segment_offset; + sr->append(r.offset); + for (int i = 0; i < 2; ++i, value >>= 8) + data[data_offset + r.offset + i] = value; + continue; + } + } + + + push(data, (uint8_t)omf::cINTERSEG); + push(data, (uint8_t)r.size); + push(data, (uint8_t)r.shift); + push(data, (uint16_t)r.offset); + push(data, (uint8_t)r.segment); + push(data, (uint16_t)r.segment_offset); + reloc_size += 8; + } else { + push(data, (uint8_t)omf::INTERSEG); + 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, (uint16_t)r.segment); + push(data, (uint32_t)r.segment_offset); + reloc_size += 15; + } + } + + + for (int i = 0; i < ss.size(); ++i) { + auto &s = ss[i]; + if (!s) continue; + + auto tmp = s->data(); + if (tmp.empty()) continue; + + reloc_size += tmp.size() + 6; + data.push_back(omf::SUPER); + push(data, ((uint32_t)tmp.size() + 1)); + data.push_back(i); + + data.insert(data.end(), tmp.begin(), tmp.end()); + } + + return reloc_size; +} + +void save_omf(const std::string &path, std::vector &segments, bool compress, bool expressload) { + + // expressload doesn't support links to other files. + // fortunately, we don't either. + + std::vector expr_headers; + std::vector expr_offsets; + + 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()); + } + + + uint32_t offset = 0; + if (expressload) { + for (auto &s : segments) { + s.segnum++; + for (auto &r : s.intersegs) r.segment++; + } + + // calculate express load segment size. + // sizeof includes the trailing 0, so no need to add in byte size. + offset = sizeof(omf_header) + 10 + sizeof("~ExpressLoad"); + + offset += 6; // lconst + end + offset += 6; // header. + for (auto &s : segments) { + offset += 8 + 2; + offset += sizeof(omf_express_header) + 10; + offset += s.segname.length() + 1; + } + + lseek(fd, offset, SEEK_SET); + } + + + 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(); + + + + uint32_t lconst_offset = offset + sizeof(omf_header) + data.size() + 5; + uint32_t lconst_size = h.length; + + //lconst record + push(data, (uint8_t)omf::LCONST); + push(data, (uint32_t)h.length); + + size_t data_offset = data.size(); + + data.insert(data.end(), s.data.begin(), s.data.end()); + + + uint32_t reloc_offset = offset + sizeof(omf_header) + data.size(); + uint32_t reloc_size = 0; + + reloc_size = add_relocs(data, data_offset, s, compress); + + // end-of-record + push(data, (uint8_t)omf::END); + + h.bytecount = data.size() + sizeof(omf_header); + + // todo -- byteswap to little-endian! + offset += write(fd, &h, sizeof(h)); + offset += write(fd, data.data(), data.size()); + + if (expressload) { + + expr_offsets.emplace_back(expr_headers.size()); + + if (lconst_size == 0) lconst_offset = 0; + if (reloc_size == 0) reloc_offset = 0; + + + push(expr_headers, (uint32_t)lconst_offset); + push(expr_headers, (uint32_t)lconst_size); + push(expr_headers, (uint32_t)reloc_offset); + push(expr_headers, (uint32_t)reloc_size); + + push(expr_headers, h.unused1); + push(expr_headers, h.lablen); + push(expr_headers, h.numlen); + push(expr_headers, h.version); + push(expr_headers, h.banksize); + push(expr_headers, h.kind); + push(expr_headers, h.unused2); + push(expr_headers, h.org); + push(expr_headers, h.alignment); + push(expr_headers, h.numsex); + push(expr_headers, h.unused3); + push(expr_headers, h.segnum); + push(expr_headers, h.entry); + push(expr_headers, (uint16_t)(h.dispname-4)); + push(expr_headers, h.dispdata); + + expr_headers.insert(expr_headers.end(), 10, ' '); + push(expr_headers, s.segname); + } + + } + + if (expressload) { + omf_header h; + h.segnum = 1; + h.banksize = 0x00010000; + h.kind = 0x8001; + h.dispname = 0x2c; + h.dispdata = 0x43; + + unsigned fudge = 10 * segments.size(); + + h.length = 6 + expr_headers.size() + fudge; + + std::vector data; + data.insert(data.begin(), 10, ' '); + push(data, std::string("~ExpressLoad")); + push(data, (uint8_t)0xf2); // lconst. + push(data, (uint32_t)h.length); + + push(data, (uint32_t)0); // reserved + push(data, (uint16_t)(segments.size() - 1)); // seg count - 1 + + + for (auto &offset : expr_offsets) { + push(data, (uint16_t)(fudge + offset)); + push(data, (uint16_t)0); + push(data, (uint32_t)0); + } + + for (auto &s : segments) { + push(data, (uint16_t)s.segnum); + } + + data.insert(data.end(), expr_headers.begin(), expr_headers.end()); + push(data, (uint8_t)0); // end. + + h.bytecount = data.size() + sizeof(omf_header); + + lseek(fd, 0, SEEK_SET); + 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..5580118 --- /dev/null +++ b/omf.h @@ -0,0 +1,79 @@ +#ifndef __omf_h__ +#define __omf_h__ + +#include +#include +#include + +namespace omf { + + enum opcode : uint8_t { + END = 0x00, + // 0x01-0xdf CONST + ALIGN = 0xe0, + ORG = 0xe1, + RELOC = 0xe2, + INTERSEG = 0xe3, + USING = 0xe4, + STRONG = 0xe5, + GLOBAL = 0xe6, + GEQU = 0xe7, + MEM = 0xe8, + EXPR = 0xeb, + ZEXPR = 0xec, + BEXPR = 0xed, + RELEXPR = 0xee, + LOCAL = 0xef, + EQU = 0xf0, + DS = 0xf1, + LCONST = 0xf2, + LEXPR = 0xf3, + ENTRY = 0xf4, + cRELOC = 0xf5, + cINTERSEG = 0xf6, + SUPER = 0xf7, + }; + + 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 diff --git a/ops.h b/ops.h new file mode 100644 index 0000000..986d59b --- /dev/null +++ b/ops.h @@ -0,0 +1,40 @@ +x(LNK) +x(KND) +x(ORG) +x(ADR) +x(TYP) +x(END) +x(PUT) +x(ASM) +x(OVR) +x(SAV) +x(DS) +x(VER) +x(ALI) +x(LKV) +x(DAT) +x(LIB) +x(ENT) +x(EXT) +x(NOL) +x(FAS) +x(POS) +x(LEN) +x(IF) +x(DO) +x(ELS) +x(FIN) +x(EQU) +x(GEQ) +x(KBD) +x(RES) +x(RID) +x(RTY) +x(RAT) +x(FIL) +x(AUX) +x(CMD) +x(ZIP) +x(IMP) +x(PFX) + diff --git a/rel.h b/rel.h new file mode 100644 index 0000000..bfdc8cb --- /dev/null +++ b/rel.h @@ -0,0 +1,25 @@ +#ifndef REL_H +#define REL_H + +enum { + SYMBOL_ABSOLUTE = 0x20, + SYMBOL_ENTRY = 0x40, + SYMBOL_EXTERNAL = 0x80, +}; + +enum { + FLAG_EXTERNAL = 0x10, + FLAG_3_BYTE = 0x20, + FLAG_2_BYTE = 0x80, + FLAG_SHIFT = 0xff, +}; + +enum { + SHIFT_16_1 = 0xd0, + SHIFT_8_1 = 0xd3, + SHIFT_8_2 = 0xd1, + + SHIFT_EXTERNAL = 0x04, +}; + +#endif diff --git a/script.re2c b/script.re2c new file mode 100644 index 0000000..ccda33c --- /dev/null +++ b/script.re2c @@ -0,0 +1,627 @@ +/* link script support */ + +/* + + label opcode operand + + + + */ + +#include +#include +#include + +#include +#include +#include + +#include + + +/*!re2c + re2c:define:YYCTYPE = char; + re2c:yyfill:enable = 0; + + // :-~ includes ; which interferes with comments. + ident = [:<-~][0-~]*; + ws = [ \t]; + eof = "\x00"; +*/ + +enum { + CMD_NONE = 0, + #define x(op) CMD_##op, + #include "ops.h" + #undef x + CMD_EQ +}; + +static std::unordered_map commands = { + #define x(op) { #op, CMD_##op }, + + #include "ops.h" + #undef x + + /* aliases */ + { "AUX", CMD_ADR }, + { "REZ", CMD_RES }, + { "LIN", CMD_LNK }, + { "KIN", CMD_KND }, + { "=", CMD_EQ } +}; + + +static std::unordered_map types = { + + { "NON", 0x00 }, + { "BAD", 0x01 }, + { "BIN", 0x06 }, + { "TXT", 0x04 }, + { "DIR", 0x0f }, + { "ADB", 0x19 }, + { "AWP", 0x1a }, + { "ASP", 0x1b }, + { "GSB", 0xab }, + { "TDF", 0xac }, + { "BDF", 0xad }, + { "SRC", 0xb0 }, + { "OBJ", 0xb1 }, + { "LIB", 0xb2 }, + { "S16", 0xb3 }, + { "RTL", 0xb4 }, + { "EXE", 0xb5 }, + { "PIF", 0xb6 }, + { "TIF", 0xb7 }, + { "NDA", 0xb8 }, + { "CDA", 0xb9 }, + { "TOL", 0xba }, + { "DRV", 0xbb }, + { "DOC", 0xbf }, + { "PNT", 0xc0 }, + { "PIC", 0xc1 }, + { "FON", 0xcb }, + { "PAS", 0xef }, + { "CMD", 0xf0 }, + { "LNK", 0xf8 }, + { "BAS", 0xfc }, + { "VAR", 0xfd }, + { "REL", 0xfe }, + { "SYS", 0xff }, + +}; + + + +static uint32_t number_operand(const char *YYCURSOR, bool required = true) { + + char *iter = YYCURSOR; + uint32_t rv = 0; + /*!re2c + * { throw std::invalid_argument("bad operand"); } + [;*] | eof { + if (!required) return rv; + throw std::invalid_argument("missing operand"); + } + '%' [01]+ { + ++iter; + for(;iter < YYCURSOR; ++iter) { + rv <<= 1; + rv |= *iter - '0'; + } + goto exit; + } + + '$' [A-Fa-f0-9]+ { + ++iter; + for(;iter < YYCURSOR; ++iter) { + char c = *iter | 0x20; + rv <<= 4; + if (c <= '9') rv |= c - '0'; + else rv |= c - 'a' + 10; + } + goto exit; + } + + [0-9]+ { + for(;iter < YYCURSOR; ++iter) { + rv *= 10; + rv += *iter - '0'; + } + goto exit; + + } + + ident { + std::string s(iter, YYCURSOR); + //look up symbol, verify it's an absolute value, etc + } + */ +exit: + char c = *YYCURSOR; + if (isspace(c) || c == 0) return rv; + + throw std::invalid_argument("bad operand"); +} + +static uint32_t type_operand(const char *YYCURSOR, bool required = true) { + + char *iter = YYCURSOR; + uint32_t rv = 0; + /*!re2c + * { throw std::invalid_argument("bad operand"); } + + [;*] | eof { + if (!required) return rv; + throw std::invalid_argument("missing operand"); + } + + '%' [01]+ { + ++iter; + for(;iter < YYCURSOR; ++iter) { + rv <<= 1; + rv |= *iter - '0'; + } + goto exit; + } + + '$' [A-Fa-f0-9]+ { + ++iter; + for(;iter < YYCURSOR; ++iter) { + char c = *iter | 0x20; + rv <<= 4; + if (c <= '9') rv |= c - '0'; + else rv |= c - 'a' + 10; + } + goto exit; + } + + [0-9]+ { + for(;iter < YYCURSOR; ++iter) { + rv *= 10; + rv += *iter - '0'; + } + goto exit; + + } + + [A-Za-z][A-Za-z0-9]{2} { + std::string s(iter, YYCURSOR); + for(char &c : s) c = std::toupper(c); + auto iter = types.find(s); + if (iter == types.end) { + throw std::invalid_argument("bad operand"); + } + rv = *iter; + } + */ +exit: + char c = *YYCURSOR; + if (isspace(c) || c == 0) return rv; + + throw std::invalid_argument("bad operand"); +} + + + +static int x_number_operand(const char *YYCURSOR) { + char *iter = YYCURSOR; + uint32_t rv = 0; + /*!re2c + * { throw std::invalid_argument("bad operand"); } + + '%' [01]+ { + ++iter; + for(;iter < YYCURSOR; ++iter) { + rv <<= 1; + rv |= *iter - '0'; + } + goto exit; + } + + '$' [A-Fa-f0-9]+ { + ++iter; + for(;iter < YYCURSOR; ++iter) { + char c = *iter | 0x20; + rv <<= 4; + if (c <= '9') rv |= c - '0'; + else rv |= c - 'a' + 10; + } + goto exit; + } + + [0-9]+ { + for(;iter < YYCURSOR; ++iter) { + rv *= 10; + rv += *iter - '0'; + } + goto exit; + + } + */ +exit: + char c = *YYCURSOR; + if (isspace(c) || c == 0) return rv; + + throw std::invalid_argument("bad operand"); +} + +static std::string x_label_operand(const char *YYCURSOR) { + char *iter = YYCURSOR; + std::string rv; + /*!re2c + * { throw std::invalid_argument("bad operand"); } + + ident { + std::string s(iter, YYCURSOR); + //look up symbol, verify it's an absolute value, etc + } + */ +exit: + char c = *YYCURSOR; + if (isspace(c) || c == 0) return rv; + throw std::invalid_argument("bad operand"); +} + +static std::string x_string_operand(const char *YYCURSOR) { + char *iter = YYCURSOR; + std::string rv; + /*!re2c + * { throw std::invalid_argument("bad operand"); } + + ['] [^']* ['] | ["] [^"]* ["] { + rv = std::string(iter+1, YYCURSOR-1); + goto exit; + } + + */ +exit: + char c = *YYCURSOR; + if (isspace(c) || c == 0) return rv; + throw std::invalid_argument("bad operand"); +} + + + +static int ovr_operand(const char *YYCURSOR) { + int rv = 0; + + /*!re2c + * { throw std::invalid_argument("bad operand"); } + [;*] | eof { + return 1; + } + 'ALL' { + rv = -1; + } + 'OFF' { + rv = 0; + } + */ + + char c = *YYCURSOR; + if (isspace(c) || c == 0) return rv; + + throw std::invalid_argument("bad operand"); +} + +static std::string label_operand(const char *YYCURSOR, bool required = true) { + std::string rv; + char *iter = YYCURSOR; + /*!re2c + * { throw std::invalid_argument("bad operand"); } + [;*] | eof { + if (!required) return rv; + throw std::invalid_argument("missing operand"); + } + ident { + rv = std::string(iter, YYCURSOR); + goto exit; + } + */ + + char c = *YYCURSOR; + if (isspace(c) || c == 0) return rv; + + throw std::invalid_argument("bad operand"); +} + + +static std::string path_operand(const char *YYCURSOR, bool required = true) { + std::string rv; + char *iter = YYCURSOR; + /*!re2c + * { throw std::invalid_argument("bad operand"); } + [;*] | eof { + if (!required) return rv; + throw std::invalid_argument("missing operand"); + } + // don't allow leading quotes, eof, or comment chars + [^;*\x00'"][^ \t]* { + rv = std::string(iter, YYCURSOR); + goto exit; + } + ['] [^']* ['] | ["] [^"]* ["] { + rv = std::string(iter+1, YYCURSOR-1); + goto exit; + } + + */ + + char c = *YYCURSOR; + if (isspace(c) || c == 0) return rv; + + throw std::invalid_argument("bad operand"); +} + + +static void no_operand(const char *YYCURSOR) { + /*!re2c + * { throw std::invalid_argument("bad operand"); } + [;*] | eof { return } + */ +} + + +static std::string string_operand(const char *YYCURSOR, bool required = true) { + + std::string rv; + char *iter = YYCURSOR; + /*!re2c + * { throw std::invalid_argument("bad operand"); } + [;*] | eof { + if (!required) return rv; + throw std::invalid_argument("missing operand"); + } + ['] [^']* ['] | ["] [^"]* ["] { + rv = std::string(iter+1, YYCURSOR-1); + goto exit; + } + */ + + char c = *YYCURSOR; + if (isspace(c) || c == 0) return rv; + throw std::invalid_argument("bad operand"); +} + + + +static void parse_line(const char *YYCURSOR) { + + unsigned cmd = 0; + std::string label; + + const char *iter = YYCURSOR; + /*!re2c + + * { throw std::invalid_argument("bad label") } + [;*] | eof { + return; + } + ws { goto opcode } + ident / (ws|eof) { + label(iter, YYCURSOR); + goto opcode; + } + + */ + + +opcode: + + + while (isspace(*YYCURSOR)) ++YYCURSOR; + iter = YYCURSOR; + + /*!re2c + + * { throw std::invalid_argument("bad opcode"); } + [;*]|eof { return 0; } + + '=' / (ws|eof) { cmd = CMD_EQ; goto operand; } + + [A-Za-z]+ / (ws|eof) { + size_t = l YYCURSOR - iter; + if (l > 3) l = 3; + std::string s(iter, iter + l); + for (char &c in s): c = std::toupper(c); + auto iter = commands.find(s); + if (!iter == commands.end()) { + throw std::invalid_argument("bad opcode"); + } + cmd = *iter; + goto operand; + } + */ + +operand: + + while (isspace(*YYCURSOR)) ++YYCURSOR; + iter = YYCURSOR; + + std::string str_operand; + long int_operand; + + switch(cmd) { + case CMD_LNK: + case CMD_PUT: + case CMD_ASM: + case CMD_SAV: + case CMD_LIB: + case CMD_IF: + case CMD_PFX: + case CMD_IMP: + case CMD_RES: + case CMD_FIL: + str_operand = path_operand(YYCURSOR); + break; + case CMD_ORG: + case CMD_ADR: + case CMD_DS: + case CMD_KND: + case CMD_VER: + case CMD_ALI: + case CMD_LKV: + case CMD_DO: + case CMD_EQU: + case CMD_EQ: + case CMD_GEQ: + int_operand = number_operand(YYCURSOR); + break; + case CMD_TYP: + int_operand = type_operand(YYCURSOR); + break; + case CMD_OVR: + int_operand = ovr_operand(YYCURSOR); + break; + case CMD_POS: + case CMD_LEN: + str_operand = label_operand(YYCURSOR, false); + break; + case CMD_KBD: + str_operand = string_operand(YYCURSOR, false); + break; + + case CMD_CMD: + str_operand = string(YYCURSOR); + break; + + default: + no_operand(YYCURSOR); + break; + } + + switch(cmd) { + + case CMD_NONE: + case CMD_NOL: /* asm: default list off */ + case CMD_PUT: /* check if source file is outdated */ + case CMD_OVR: /* override outdated check and force assembly */ + case CMD_FAS: /* fast linker. only one SAV allowed */ + /* nop */ + break; + + case CMD_LNK: + /* link file ... */ + break; + case CMD_ORG: + /* set the link origin. also used as aux type */ + break; + case CMD_ADR: + /* set the file aux type */ + break; + + case CMD_SAV: + /* save linked file. for xl linker, specifies segment name */ + break; + + case CMD_TYP: + /* set the file type */ + break; + + case CMD_EXT: + /* print addresses of all resolved externals (not just errors) + disabled after each SAV + */ + break; + case CMD_ENT: + /* print the entry list */ + /* flag symbol w ? if unused */ + break; + case CMD_DAT: + /* print the current date and time */ + break; + + case CMD_END: + /* end of command file (optional) */ + break; + + case CMD_LIB: + /* search directory for unresolved symbols */ + break; + + case CMD_LKV: + /* specify linker version */ + /* 0 = binary, 1 = Linker.GS, 2 = Linker.XL, 3 = convert to OMF object file */ + switch (int_operand) { + case 0: throw std::runtime_error("binary linker not supported"); + case 3: throw std::runtime_error("object file linker not supported"); + case 1: + case 2: + /* default file type = S16 */ + break; + default: + throw std::runtime_error("bad linker version"); + } + break; + + case CMD_VER: + /*specify OMF version. 1 or 2 */ + break; + + case CMD_KND: + /* set the OMF kind flag */ + /* 8-bit for v 1, 16-bit for v2 */ + break; + + case CMD_ALI: + /* OMF align field. default = 0 */ + break; + + case CMD_DS: + /* OMF RESSPC field */ + break; + + + + case CMD_ASM: + default: + throw std::runtime_error("opcode not supported"); + } + + + +} + +int process_link_file(const std::string &path) { + + FILE *fp; + fp = fopen(path, "r"); + if (!fp) { + warn("Unable to open %s", path.c_str()); + return -1; + } + + int no = 1; + int errors = 0; + const char *line = NULL; + size_t cap = 0; + for(;; ++no) { + + ssize_t len = getline(&line, &cap, fp); + if (len == 0) break; + if (len < 0) { + warn("read error"); + ++errors; + break; + } + /* strip trailing ws */ + while (len && isspace(line[len-1])) --len; + line[len] = 0; + if (len == 0) continue; + + try { + parse_line(line); + } catch (std::exception &ex) { + fprintf(stderr, "%s in line: %d\n", ex.what(), no); + fprintf(stderr, "%s\n", line); + if (++errors >= 10) { + fputs("Too many errors, aborting\n", stderr); + break; + } + } + } + fclose(fp); + free(line); + return errors ? -1 : 0; +} diff --git a/unique_resource.h b/unique_resource.h new file mode 100644 index 0000000..9b98401 --- /dev/null +++ b/unique_resource.h @@ -0,0 +1,95 @@ +#ifndef __unique_resource_h__ +#define __unique_resource_h__ + +#include + +template +class unique_resource { +public: + typedef T element_type; + typedef D deleter_type; + + unique_resource() = default; + unique_resource(const unique_resource &) = delete; + unique_resource(unique_resource &&rhs) { + swap(rhs); + } + + unique_resource(T t, D d): _pair(t,d), _active(true) + {} + + ~unique_resource() { + reset(); + } + + unique_resource &operator=(const unique_resource &) = delete; + unique_resource &operator=(unique_resource &&rhs) { + if (this != std::addressof(rhs)) { + reset(); + swap(rhs); + } + return *this; + } + + void swap(unique_resource & rhs) { + if (this != std::addressof(rhs)) { + std::swap(_active, rhs._active); + std::swap(_pair, rhs._pair); + } + } + + void reset(T t) { + reset(); + _active = true; + _pair.first = t; + } + + void reset(T t, D d) { + reset(); + _active = true; + _pair = std::make_pair(t, d); + } + + void reset() { + if (_active) { + (*_pair.second)(_pair.first); + _active = false; + } + } + + T release() { + _active = false; + return _pair.first;; + } + + T get() { + return _pair.first; + } + + operator bool() const { + return _active; + } + + D& get_deleter() { + return _pair.second; + } + + const D& get_deleter() const { + return _pair.second; + } + + +private: + std::pair _pair; + bool _active = false; +}; + +#define MAKE_UNIQUE_RESOURCE(T, D) \ + unique_resource(T, D) + +template +unique_resource make_unique_resource(T t, D d) { + return unique_resource(t, d); +} + +#endif \ No newline at end of file