initial WIP

This commit is contained in:
Kelvin Sherlock 2019-12-08 13:55:33 -05:00
commit 5159061ff0
10 changed files with 2281 additions and 0 deletions

24
Makefile Normal file
View File

@ -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 $@ $<

261
link.cpp Normal file
View File

@ -0,0 +1,261 @@
/* c++17 */
#include <vector>
#include <unordered_map>
#include <string>
#include "omf.h"
void save_omf(const std::string &path, std::vector<omf::segment> &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<std::string, unsigned> symbol_map;
std::vector<symbol> 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<unsigned> 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);
}
}

389
mapped_file.cpp Normal file
View File

@ -0,0 +1,389 @@
#include "mapped_file.h"
#include "unique_resource.h"
#include <memory>
#include <functional>
#include <system_error>
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 <windows.h>
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<class ...Args>
HANDLE CreateFileX(const std::string &s, Args... args) {
return CreateFileA(s.c_str(), std::forward<Args>(args)...);
}
template<class ...Args>
HANDLE CreateFileX(const std::wstring &s, Args... args) {
return CreateFileW(s.c_str(), std::forward<Args>(args)...);
}
}
void mapped_file_base::close() {
if (is_open()) {
UnmapViewOfFile(_data);
CloseHandle(_map_handle);
CloseHandle(_file_handle);
reset();
}
}
template<class T>
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<class T>
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 <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <cerrno>
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;
}

237
mapped_file.h Normal file
View File

@ -0,0 +1,237 @@
#ifndef __mapped_file_h__
#define __mapped_file_h__
#include <cstddef>
#include <system_error>
#include <string>
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<class S>
void open_common(const S &p, mapmode flags, size_t length, size_t offset, std::error_code *ec);
template<class S>
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<class T>
void swap(mapped_file &a, mapped_file &b) {
a.swap(b);
}
}
#endif

504
omf.cpp Normal file
View File

@ -0,0 +1,504 @@
#include "omf.h"
#include <vector>
#include <string>
#include <algorithm>
#include <array>
#include <optional>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
#include <sysexits.h>
#include <assert.h>
#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<uint8_t> &v, uint8_t x) {
v.push_back(x);
}
void push(std::vector<uint8_t> &v, uint16_t x) {
v.push_back(x & 0xff);
x >>= 8;
v.push_back(x & 0xff);
}
void push(std::vector<uint8_t> &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<uint8_t> &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<uint8_t> _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<uint8_t> &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<uint8_t> &data, size_t data_offset, omf::segment &seg, bool compress) {
std::array< std::optional<super_helper>, 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<omf::segment> &segments, bool compress, bool expressload) {
// expressload doesn't support links to other files.
// fortunately, we don't either.
std::vector<uint8_t> expr_headers;
std::vector<unsigned> 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<uint8_t> 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<uint8_t> 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);
}

79
omf.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef __omf_h__
#define __omf_h__
#include <stdint.h>
#include <vector>
#include <string>
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<uint8_t> data;
std::vector<interseg> intersegs;
std::vector<reloc> relocs;
};
}
#endif

40
ops.h Normal file
View File

@ -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)