mirror of
https://github.com/ksherlock/merlin-utils.git
synced 2024-12-11 01:50:25 +00:00
initial WIP
This commit is contained in:
commit
5159061ff0
24
Makefile
Normal file
24
Makefile
Normal 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
261
link.cpp
Normal 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
389
mapped_file.cpp
Normal 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
237
mapped_file.h
Normal 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
504
omf.cpp
Normal 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
79
omf.h
Normal 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
|