initial WIP
This commit is contained in:
commit
5159061ff0
|
@ -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 $@ $<
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1,627 @@
|
||||||
|
/* link script support */
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
label opcode operand
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*!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<std::string, int> 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<std::string, int> 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;
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
#ifndef __unique_resource_h__
|
||||||
|
#define __unique_resource_h__
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
template <class T, class D>
|
||||||
|
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<T, D> _pair;
|
||||||
|
bool _active = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAKE_UNIQUE_RESOURCE(T, D) \
|
||||||
|
unique_resource<decltype(T), decltype(D) *>(T, D)
|
||||||
|
|
||||||
|
template<class T, class D>
|
||||||
|
unique_resource<T, D> make_unique_resource(T t, D d) {
|
||||||
|
return unique_resource<T, D>(t, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue