mirror of
https://github.com/ksherlock/wdc-utils.git
synced 2025-02-06 17:30:17 +00:00
Merge branch 'master' of git://qnap.local/wdcdumpobj
This commit is contained in:
commit
822b685d46
16
Makefile
16
Makefile
@ -2,20 +2,28 @@ LINK.o = $(LINK.cc)
|
|||||||
CXXFLAGS = -std=c++11 -g -Wall
|
CXXFLAGS = -std=c++11 -g -Wall
|
||||||
CCFLAGS = -g
|
CCFLAGS = -g
|
||||||
|
|
||||||
OBJS = dumpobj.o disassembler.o zrdz_disassembler.o
|
DUMP_OBJS = dumpobj.o disassembler.o zrdz_disassembler.o
|
||||||
|
LINK_OBJS = link.o expression.o
|
||||||
|
|
||||||
#UNAME_S := $(shell uname -s)
|
#UNAME_S := $(shell uname -s)
|
||||||
#ifeq ($(UNAME_S),MINGW64_NT-10.0)
|
#ifeq ($(UNAME_S),MINGW64_NT-10.0)
|
||||||
ifeq ($(MSYSTEM),MINGW64)
|
ifeq ($(MSYSTEM),MINGW64)
|
||||||
OBJS += mingw/err.o
|
DUMP_OBJS += mingw/err.o
|
||||||
|
LINK_OBJS += mingw/err.o
|
||||||
CPPFLAGS += -I mingw/
|
CPPFLAGS += -I mingw/
|
||||||
LDLIBS += -static
|
LDLIBS += -static
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: wdcdumpobj wdclink
|
||||||
|
|
||||||
wdcdumpobj : $(OBJS)
|
wdcdumpobj : $(DUMP_OBJS)
|
||||||
$(LINK.o) $^ $(LDLIBS) -o $@
|
$(LINK.o) $^ $(LDLIBS) -o $@
|
||||||
|
|
||||||
|
wdclink : $(LINK_OBJS)
|
||||||
|
$(LINK.o) $^ $(LDLIBS) -o $@
|
||||||
|
|
||||||
|
|
||||||
disassembler.o : disassembler.cpp disassembler.h
|
disassembler.o : disassembler.cpp disassembler.h
|
||||||
zrdz_disassembler.o : zrdz_disassembler.cpp zrdz_disassembler.h disassembler.h
|
zrdz_disassembler.o : zrdz_disassembler.cpp zrdz_disassembler.h disassembler.h
|
||||||
dumpobj.o : dumpobj.cpp zrdz_disassembler.h disassembler.h
|
dumpobj.o : dumpobj.cpp zrdz_disassembler.h disassembler.h
|
||||||
@ -23,7 +31,7 @@ mingw/err.o : mingw/err.c mingw/err.h
|
|||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
$(RM) wdcdumpobj $(OBJS)
|
$(RM) wdcdumpobj $(DUMP_OBJS) $(LINK_OBJS)
|
||||||
|
|
||||||
|
|
||||||
.PHONY: variables
|
.PHONY: variables
|
||||||
|
@ -72,7 +72,7 @@ void usage() {
|
|||||||
struct Header {
|
struct Header {
|
||||||
uint32_t magic; /* magic number for detection */
|
uint32_t magic; /* magic number for detection */
|
||||||
uint16_t version; /* version number of object format */
|
uint16_t version; /* version number of object format */
|
||||||
uint8_t filetype; /* file type, object or library */
|
uint8_t filetype; /* file type, object or library */
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
169
expression.cpp
Normal file
169
expression.cpp
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#include "expression.h"
|
||||||
|
#include <vector>
|
||||||
|
#include "obj816.h"
|
||||||
|
|
||||||
|
/* a ** b */
|
||||||
|
uint32_t power(uint32_t a, uint32_t b) {
|
||||||
|
|
||||||
|
uint32_t rv = 1;
|
||||||
|
for( ; b; b >>= 1) {
|
||||||
|
if (b & 0x01) rv *= a;
|
||||||
|
a = a * a;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unary_op(unsigned op, std::vector<expr> &v) {
|
||||||
|
if (v.size() >=1 ) {
|
||||||
|
expr &a = v.back();
|
||||||
|
if (a.tag == OP_VAL) {
|
||||||
|
switch(a.tag) {
|
||||||
|
case OP_NOT: a.value = !a.value; break;
|
||||||
|
case OP_NEG: a.value = -a.value; break;
|
||||||
|
case OP_FLP: a.value = ~a.value; break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.emplace_back(op);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool binary_op(unsigned op, std::vector<expr> &v) {
|
||||||
|
if (v.size() >= 2) {
|
||||||
|
expr &a = v[v.size() - 2];
|
||||||
|
expr &b = v[v.size() - 1];
|
||||||
|
|
||||||
|
if (a.tag == OP_VAL && b.tag == OP_VAL) {
|
||||||
|
uint32_t value = 0;
|
||||||
|
switch(op) {
|
||||||
|
case OP_EXP: value = power(a.value, b.value); break;
|
||||||
|
case OP_MUL: value = a.value * b.value; break;
|
||||||
|
case OP_DIV: value = a.value / b.value; break;
|
||||||
|
case OP_MOD: value = a.value % b.value; break;
|
||||||
|
case OP_SHR: value = a.value >> b.value; break;
|
||||||
|
case OP_SHL: value = a.value << b.value; break;
|
||||||
|
case OP_ADD: value = a.value + b.value; break;
|
||||||
|
case OP_SUB: value = a.value - b.value; break;
|
||||||
|
case OP_AND: value = a.value & b.value; break;
|
||||||
|
case OP_OR: value = a.value | b.value; break;
|
||||||
|
case OP_XOR: value = a.value ^ b.value; break;
|
||||||
|
case OP_EQ: value = a.value == b.value; break;
|
||||||
|
case OP_GT: value = (int32_t)a.value > (int32_t)b.value; break;
|
||||||
|
case OP_LT: value = (int32_t)a.value < (int32_t)b.value; break;
|
||||||
|
case OP_UGT: value = a.value > b.value; break;
|
||||||
|
case OP_ULT: value = a.value < b.value; break;
|
||||||
|
}
|
||||||
|
v.pop_back();
|
||||||
|
v.back().value = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (a.tag == OP_VAL && b.tag == OP_LOC) {
|
||||||
|
switch(op) {
|
||||||
|
case OP_ADD: {
|
||||||
|
// constant + symbol
|
||||||
|
expr tmp = b;
|
||||||
|
tmp.value = a.value + b.value;
|
||||||
|
v.pop_back();
|
||||||
|
v.pop_back();
|
||||||
|
v.emplace_back(tmp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
case OP_SUB: {
|
||||||
|
// constant - symbol .. does this even make sense?
|
||||||
|
expr tmp = b;
|
||||||
|
tmp.value = a.value - b.value;
|
||||||
|
v.pop_back();
|
||||||
|
v.pop_back();
|
||||||
|
v.emplace_back(tmp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (a.tag == OP_LOC && b.tag == OP_VAL) {
|
||||||
|
switch(op) {
|
||||||
|
case OP_ADD: a.value += b.value; return true;
|
||||||
|
case OP_SUB: a.value -= b.value; return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.tag == OP_LOC && b.tag == OP_LOC && a.section == b.section) {
|
||||||
|
uint32_t value = 0;
|
||||||
|
bool rv = false;
|
||||||
|
switch (op) {
|
||||||
|
case OP_SUB:
|
||||||
|
// end - start
|
||||||
|
value = a.value - b.value;
|
||||||
|
rv = true; break;
|
||||||
|
case OP_EQ:
|
||||||
|
value = a.value == b.value;
|
||||||
|
rv = true; break;
|
||||||
|
case OP_GT:
|
||||||
|
case OP_UGT:
|
||||||
|
value = a.value > b.value;
|
||||||
|
rv = true; break;
|
||||||
|
case OP_LT:
|
||||||
|
case OP_ULT:
|
||||||
|
value = a.value < b.value;
|
||||||
|
rv = true; break;
|
||||||
|
}
|
||||||
|
if (rv) {
|
||||||
|
v.pop_back();
|
||||||
|
v.pop_back();
|
||||||
|
v.emplace_back(OP_VAL, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
v.emplace_back(op);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool simplify_expression(expression &e) {
|
||||||
|
std::vector<expr> tmp;
|
||||||
|
bool rv = false;
|
||||||
|
for (auto &t : e.stack) {
|
||||||
|
if (t.tag >= OP_BIN) {
|
||||||
|
rv = binary_op(t.tag, tmp) || rv;
|
||||||
|
} else if (t.tag >= OP_UNA) {
|
||||||
|
rv = unary_op(t.tag, tmp) || rv;
|
||||||
|
} else {
|
||||||
|
tmp.emplace_back(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rv) e.stack = std::move(tmp);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if force is true, treat OP_LOC records as OP_VAL (for omf)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// should return std::optional<uint32_t>...
|
||||||
|
|
||||||
|
uint32_t evaluate_expression(expression &e, bool force) {
|
||||||
|
std::vector<expr> tmp;
|
||||||
|
for (const auto &t : e.stack) {
|
||||||
|
if (t.tag == OP_LOC && force) {
|
||||||
|
tmp.emplace_back(OP_VAL, t.value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (t.tag >= OP_BIN) {
|
||||||
|
binary_op(t.tag, tmp);
|
||||||
|
} else if (t.tag >= OP_UNA) {
|
||||||
|
unary_op(t.tag, tmp);
|
||||||
|
} else {
|
||||||
|
tmp.emplace_back(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp.size() == 1 && tmp.front().tag == OP_VAL) return tmp.front().value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
32
expression.h
Normal file
32
expression.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef __expression_h__
|
||||||
|
#define __expression_h__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct expr {
|
||||||
|
expr(int t = 0, uint32_t v = 0, uint32_t s = 0)
|
||||||
|
: tag(t), value(v), section(s)
|
||||||
|
{}
|
||||||
|
int tag = 0;
|
||||||
|
uint32_t value = 0;
|
||||||
|
unsigned section = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct expression {
|
||||||
|
unsigned section = 0;
|
||||||
|
uint32_t offset = 0;
|
||||||
|
uint8_t size = 0;
|
||||||
|
uint8_t relative = false;
|
||||||
|
bool undefined = false;
|
||||||
|
|
||||||
|
std::vector<expr> stack;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t evaluate_expression(expression &e, bool force = false);
|
||||||
|
|
||||||
|
bool simplify_expression(expression &e);
|
||||||
|
|
||||||
|
#endif
|
838
link.cpp
838
link.cpp
@ -7,7 +7,818 @@
|
|||||||
#include <sysexits.h>
|
#include <sysexits.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <array>
|
||||||
|
#include <utility>
|
||||||
|
#include <numeric>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
#include "obj816.h"
|
||||||
|
#include "expression.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef O_BINARY
|
||||||
|
#define O_BINARY 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum class endian {
|
||||||
|
little = __ORDER_LITTLE_ENDIAN__,
|
||||||
|
big = __ORDER_BIG_ENDIAN__,
|
||||||
|
native = __BYTE_ORDER__
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
void swap_if(T &t, std::false_type) {}
|
||||||
|
|
||||||
|
void swap_if(uint8_t &, std::true_type) {}
|
||||||
|
|
||||||
|
void swap_if(uint16_t &value, std::true_type) {
|
||||||
|
value = __builtin_bswap16(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap_if(uint32_t &value, std::true_type) {
|
||||||
|
value = __builtin_bswap32(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap_if(uint64_t &value, std::true_type) {
|
||||||
|
value = __builtin_bswap64(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
void le_to_host(T &value) {
|
||||||
|
swap_if(value, std::integral_constant<bool, endian::native == endian::big>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct Header {
|
||||||
|
uint32_t magic; /* magic number for detection */
|
||||||
|
uint16_t version; /* version number of object format */
|
||||||
|
uint8_t filetype; /* file type, object or library */
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
|
||||||
|
struct section {
|
||||||
|
std::string name;
|
||||||
|
uint8_t flags = 0;
|
||||||
|
uint32_t org = 0;
|
||||||
|
uint32_t size = 0;
|
||||||
|
|
||||||
|
unsigned number = -1;
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
std::vector<expression> expressions;
|
||||||
|
|
||||||
|
unsigned end_symbol = 0; // auto-generated _END_{name} symbol.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct symbol {
|
||||||
|
std::string name;
|
||||||
|
uint8_t type = 0;
|
||||||
|
uint8_t flags = 0;
|
||||||
|
uint32_t offset = 0;
|
||||||
|
int section = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
uint8_t read_8(T &iter) {
|
||||||
|
uint8_t tmp = *iter;
|
||||||
|
++iter;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
uint16_t read_16(T &iter) {
|
||||||
|
uint16_t tmp = 0;
|
||||||
|
|
||||||
|
tmp |= *iter << 0;
|
||||||
|
++iter;
|
||||||
|
tmp |= *iter << 8;
|
||||||
|
++iter;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
uint32_t read_32(T &iter) {
|
||||||
|
uint32_t tmp = 0;
|
||||||
|
|
||||||
|
tmp |= *iter << 0;
|
||||||
|
++iter;
|
||||||
|
tmp |= *iter << 8;
|
||||||
|
++iter;
|
||||||
|
tmp |= *iter << 16;
|
||||||
|
++iter;
|
||||||
|
tmp |= *iter << 24;
|
||||||
|
++iter;
|
||||||
|
|
||||||
|
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
std::string read_cstring(T &iter) {
|
||||||
|
std::string s;
|
||||||
|
for(;;) {
|
||||||
|
uint8_t c = *iter;
|
||||||
|
++iter;
|
||||||
|
if (!c) break;
|
||||||
|
s.push_back(c);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
std::string read_pstring(T &iter) {
|
||||||
|
std::string s;
|
||||||
|
unsigned size = *iter;
|
||||||
|
++iter;
|
||||||
|
s.reserve(size);
|
||||||
|
while (size--) {
|
||||||
|
uint8_t c = *iter;
|
||||||
|
++iter;
|
||||||
|
s.push_back(c);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<section> read_sections(const std::vector<uint8_t> §ion_data) {
|
||||||
|
|
||||||
|
std::vector<section> sections;
|
||||||
|
auto iter = section_data.begin();
|
||||||
|
while (iter != section_data.end()) {
|
||||||
|
|
||||||
|
section s;
|
||||||
|
|
||||||
|
s.number = read_8(iter);
|
||||||
|
s.flags = read_8(iter);
|
||||||
|
s.size = read_32(iter);
|
||||||
|
s.org = read_32(iter);
|
||||||
|
|
||||||
|
if (!(s.flags & SEC_NONAME)) s.name = read_cstring(iter);
|
||||||
|
|
||||||
|
sections.emplace_back(std::move(s));
|
||||||
|
}
|
||||||
|
return sections;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<symbol> read_symbols(const std::vector<uint8_t> &symbol_data) {
|
||||||
|
|
||||||
|
std::vector<symbol> symbols;
|
||||||
|
|
||||||
|
auto iter = symbol_data.begin();
|
||||||
|
while (iter != symbol_data.end()) {
|
||||||
|
symbol s;
|
||||||
|
s.type = read_8(iter);
|
||||||
|
s.flags = read_8(iter);
|
||||||
|
s.section = read_8(iter);
|
||||||
|
s.offset = s.type == S_UND ? 0 : read_32(iter);
|
||||||
|
s.name = read_cstring(iter);
|
||||||
|
|
||||||
|
|
||||||
|
symbols.emplace_back(std::move(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
return symbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::unordered_map<std::string, int> section_map;
|
||||||
|
std::vector<section> sections;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, int> symbol_map;
|
||||||
|
std::vector<symbol> symbols;
|
||||||
|
|
||||||
|
std::unordered_set<std::string> undefined_symbols;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* replace undefined symbols (if possible) and simplify expressions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void simplify() {
|
||||||
|
for (auto &s : sections) {
|
||||||
|
for (auto &e : s.expressions) {
|
||||||
|
bool delta = false;
|
||||||
|
|
||||||
|
// first check for undefined symbols.
|
||||||
|
if (e.undefined) {
|
||||||
|
e.undefined = false;
|
||||||
|
for (auto &t : e.stack) {
|
||||||
|
|
||||||
|
if (t.tag == OP_SYM) {
|
||||||
|
const auto &ss = symbols[t.section];
|
||||||
|
switch(ss.type & 0x0f) {
|
||||||
|
case S_UND:
|
||||||
|
e.undefined = true;
|
||||||
|
break;
|
||||||
|
case S_REL:
|
||||||
|
t = expr{OP_LOC, ss.offset, (uint32_t)ss.section};
|
||||||
|
delta = true;
|
||||||
|
break;
|
||||||
|
case S_ABS:
|
||||||
|
t = expr{OP_VAL, (uint32_t)ss.offset};
|
||||||
|
delta = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e.stack.size() > 1) simplify_expression(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* read and process all sections...
|
||||||
|
* if section > 5, remap based on name.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
void one_module(const std::vector<uint8_t> &data, const std::vector<uint8_t> §ion_data, const std::vector<uint8_t> &symbol_data) {
|
||||||
|
|
||||||
|
std::array<int, 256> remap_section;
|
||||||
|
|
||||||
|
int current_section = SECT_CODE;
|
||||||
|
std::vector<uint8_t> *data_ptr = §ions[current_section].data;
|
||||||
|
|
||||||
|
std::fill(remap_section.begin(), remap_section.end(), -1);
|
||||||
|
remap_section[SECT_PAGE0] = SECT_PAGE0;
|
||||||
|
remap_section[SECT_CODE] = SECT_CODE;
|
||||||
|
remap_section[SECT_KDATA] = SECT_KDATA;
|
||||||
|
remap_section[SECT_DATA] = SECT_DATA;
|
||||||
|
remap_section[SECT_UDATA] = SECT_UDATA;
|
||||||
|
|
||||||
|
std::vector<section> local_sections = read_sections(section_data);
|
||||||
|
std::vector<symbol> local_symbols = read_symbols(symbol_data);
|
||||||
|
|
||||||
|
// convert local sections to global
|
||||||
|
for (auto &s : local_sections) {
|
||||||
|
if (s.number <= SECT_UDATA) continue;
|
||||||
|
|
||||||
|
auto iter = section_map.find(s.name);
|
||||||
|
if (iter == section_map.end()) {
|
||||||
|
int virtual_section = sections.size();
|
||||||
|
remap_section[s.number] = virtual_section;
|
||||||
|
s.number = virtual_section;
|
||||||
|
sections.emplace_back(s);
|
||||||
|
symbol_map.emplace(s.name, virtual_section);
|
||||||
|
} else {
|
||||||
|
const auto &ss = sections[iter->second];
|
||||||
|
assert(ss.flags == s.flags); // check org????
|
||||||
|
remap_section[s.number] = iter->second;
|
||||||
|
s.number = iter->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert local symbols to global.
|
||||||
|
for (auto &s : local_symbols) {
|
||||||
|
if (s.type == S_UND) {
|
||||||
|
auto iter = symbol_map.find(s.name);
|
||||||
|
if (iter == symbol_map.end()) {
|
||||||
|
s.section = symbols.size();
|
||||||
|
symbol_map.emplace(s.name, s.section);
|
||||||
|
undefined_symbols.emplace(s.name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// already exists...
|
||||||
|
const auto &ss = symbols[iter->second];
|
||||||
|
if (ss.type != S_UND) s = ss;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remap and fudge the offset.
|
||||||
|
if ((s.type & 0x0f) == S_REL) {
|
||||||
|
int virtual_section = remap_section[s.section];
|
||||||
|
assert(virtual_section != -1);
|
||||||
|
s.section = virtual_section;
|
||||||
|
s.offset += sections[virtual_section].data.size();
|
||||||
|
} else {
|
||||||
|
s.section = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const unsigned mask = SF_GBL | SF_DEF;
|
||||||
|
if ((s.flags & mask) == mask) {
|
||||||
|
|
||||||
|
auto iter = symbol_map.find(s.name);
|
||||||
|
|
||||||
|
if (iter == symbol_map.end()) {
|
||||||
|
unsigned tmp = symbols.size();
|
||||||
|
symbol_map.emplace(s.name, tmp);
|
||||||
|
symbols.emplace_back(s);
|
||||||
|
} else {
|
||||||
|
auto &ss = symbols[iter->second];
|
||||||
|
|
||||||
|
// if it was undefined, define it!
|
||||||
|
if (ss.type == S_UND) {
|
||||||
|
ss = s;
|
||||||
|
undefined_symbols.erase(s.name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// ok if symbols are identical..
|
||||||
|
assert(ss.type == ss.type && ss.flags == s.flags && ss.section == s.section && ss.offset == s.offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
auto iter = data.begin();
|
||||||
|
for(;;) {
|
||||||
|
uint8_t op = read_8(iter);
|
||||||
|
if (op == REC_END) return;
|
||||||
|
|
||||||
|
++iter;
|
||||||
|
if (op < 0xf0) {
|
||||||
|
data_ptr->insert(data_ptr->end(), iter, iter + op);
|
||||||
|
iter += op;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(op) {
|
||||||
|
case REC_SPACE: {
|
||||||
|
uint16_t count = read_16(iter);
|
||||||
|
data_ptr->insert(data_ptr->end(), count, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case REC_SECT: {
|
||||||
|
/* switch sections */
|
||||||
|
uint8_t s = read_8(iter);
|
||||||
|
current_section = remap_section[s];
|
||||||
|
assert(current_section > 0 && current_section < sections.size());
|
||||||
|
|
||||||
|
data_ptr = §ions[current_section].data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case REC_ORG: {
|
||||||
|
assert(!"ORG not supported.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case REC_RELEXP:
|
||||||
|
case REC_EXPR: {
|
||||||
|
|
||||||
|
expression e;
|
||||||
|
e.relative = op == REC_RELEXP;
|
||||||
|
|
||||||
|
e.offset = data_ptr->size();
|
||||||
|
e.size = read_8(iter);
|
||||||
|
|
||||||
|
data_ptr->insert(data_ptr->end(), e.size, 0);
|
||||||
|
|
||||||
|
/**/
|
||||||
|
for(;;) {
|
||||||
|
op = read_8(iter);
|
||||||
|
if (op == OP_END) break;
|
||||||
|
|
||||||
|
switch(op) {
|
||||||
|
case OP_VAL: {
|
||||||
|
uint32_t offset = read_32(iter);
|
||||||
|
e.stack.emplace_back(op, offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_SYM: {
|
||||||
|
uint16_t symbol = read_16(iter);
|
||||||
|
assert(symbol < local_symbols.size());
|
||||||
|
auto &s = local_symbols[symbol];
|
||||||
|
switch (s.type & 0x0f) {
|
||||||
|
case S_UND:
|
||||||
|
// S_UND indicates it's still undefined globally.
|
||||||
|
e.stack.emplace_back(OP_SYM, 0, s.section); /* section is actually a symbol number */
|
||||||
|
e.undefined = true;
|
||||||
|
break;
|
||||||
|
case S_REL:
|
||||||
|
e.stack.emplace_back(OP_LOC, s.offset, s.section);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case S_ABS:
|
||||||
|
e.stack.emplace_back(OP_VAL, s.offset);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(!"unsupported symbol flags.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_LOC: {
|
||||||
|
uint8_t section = read_8(iter);
|
||||||
|
uint32_t offset = read_32(iter);
|
||||||
|
int real_section = remap_section[section];
|
||||||
|
assert(real_section >= 0);
|
||||||
|
e.stack.emplace_back(op, offset, real_section);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// operations..
|
||||||
|
//unary
|
||||||
|
case OP_NOT:
|
||||||
|
case OP_NEG:
|
||||||
|
case OP_FLP:
|
||||||
|
// binary
|
||||||
|
case OP_EXP:
|
||||||
|
case OP_MUL:
|
||||||
|
case OP_DIV:
|
||||||
|
case OP_MOD:
|
||||||
|
case OP_SHR:
|
||||||
|
case OP_SHL:
|
||||||
|
case OP_ADD:
|
||||||
|
case OP_SUB:
|
||||||
|
case OP_AND:
|
||||||
|
case OP_OR:
|
||||||
|
case OP_XOR:
|
||||||
|
case OP_EQ:
|
||||||
|
case OP_GT:
|
||||||
|
case OP_LT:
|
||||||
|
case OP_UGT:
|
||||||
|
case OP_ULT:
|
||||||
|
e.stack.emplace_back(op);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(!"unsupported expression opcode.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case REC_LINE: break;
|
||||||
|
case REC_DEBUG: {
|
||||||
|
uint16_t size = read_16(iter);
|
||||||
|
iter += size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
|
||||||
|
sections.resize(5);
|
||||||
|
|
||||||
|
sections[SECT_PAGE0].number = SECT_PAGE0;
|
||||||
|
sections[SECT_PAGE0].flags = SEC_DATA | SEC_NONAME | SEC_DIRECT | SEC_REF_ONLY;
|
||||||
|
sections[SECT_PAGE0].name = "page0";
|
||||||
|
|
||||||
|
sections[SECT_CODE].number = SECT_CODE;
|
||||||
|
sections[SECT_CODE].flags = SEC_NONAME;
|
||||||
|
sections[SECT_CODE].name = "code";
|
||||||
|
|
||||||
|
sections[SECT_KDATA].number = SECT_KDATA;
|
||||||
|
sections[SECT_KDATA].flags = SEC_DATA | SEC_NONAME;
|
||||||
|
sections[SECT_KDATA].name = "kdata";
|
||||||
|
|
||||||
|
sections[SECT_DATA].number = SECT_DATA;
|
||||||
|
sections[SECT_DATA].flags = SEC_DATA | SEC_NONAME;
|
||||||
|
sections[SECT_DATA].name = "data";
|
||||||
|
|
||||||
|
sections[SECT_UDATA].number = SECT_UDATA;
|
||||||
|
sections[SECT_UDATA].flags = SEC_DATA | SEC_NONAME | SEC_REF_ONLY;
|
||||||
|
sections[SECT_UDATA].name = "udata";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For each section, [the linker] creates three symbols,
|
||||||
|
* _ROM_BEG_secname, _BEG_secname and _END_secname, which
|
||||||
|
* correspond to the rom location and the execution beginning
|
||||||
|
* and end of the section. These will be used more in the next
|
||||||
|
* two sections of code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// n.b - only for pre-defined sections [?], skip the _ROM_BEG_* symbols...
|
||||||
|
|
||||||
|
static std::string names[] = {
|
||||||
|
"_BEG_PAGE0", "_END_PAGE0",
|
||||||
|
"_BEG_CODE", "_END_CODE",
|
||||||
|
"_BEG_KDATA", "_END_KDATA"
|
||||||
|
"_BEG_DATA", "_END_DATA"
|
||||||
|
"_BEG_UDATA", "_END_UDATA"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
|
|
||||||
|
// begin is 0.
|
||||||
|
symbol s;
|
||||||
|
s.name = names[i * 2];
|
||||||
|
s.section = i;
|
||||||
|
s.type = S_REL;
|
||||||
|
s.flags = SF_DEF | SF_GBL;
|
||||||
|
symbol_map.emplace(s.name, i * 2);
|
||||||
|
symbols.emplace_back(s);
|
||||||
|
|
||||||
|
// end is undefined...
|
||||||
|
s.name = names[i * 2 + 1];
|
||||||
|
s.section = i * 2 + 1; // symbol number.
|
||||||
|
s.type = S_UND;
|
||||||
|
s.flags = 0;
|
||||||
|
|
||||||
|
symbol_map.emplace(s.name, i * 2 + 1);
|
||||||
|
symbols.emplace_back(s);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void generate_end() {
|
||||||
|
const std::string names[] = {
|
||||||
|
"_END_PAGE0",
|
||||||
|
"_END_CODE",
|
||||||
|
"_END_KDATA"
|
||||||
|
"_END_DATA"
|
||||||
|
"_END_UDATA"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
|
symbol s;
|
||||||
|
s.section = i;
|
||||||
|
s.type = S_REL;
|
||||||
|
s.flags = SF_DEF | SF_GBL;
|
||||||
|
s.offset = sections[i].data.size();
|
||||||
|
|
||||||
|
symbols[i * 2 + 1] = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void merge_data() {
|
||||||
|
|
||||||
|
// merge data sections -- kdata, data, udata.
|
||||||
|
// merge custom data sections?
|
||||||
|
|
||||||
|
std::vector<uint8_t> new_data;
|
||||||
|
std::vector<expression> new_expr;
|
||||||
|
|
||||||
|
unsigned new_number = 0;
|
||||||
|
|
||||||
|
uint32_t total_data_size = 0;
|
||||||
|
uint32_t total_code_size = 0;
|
||||||
|
for (const auto &s : sections) {
|
||||||
|
if (s.flags & SEC_REF_ONLY) continue;
|
||||||
|
if (s.flags & SEC_DATA) total_data_size += s.data.size();
|
||||||
|
else total_code_size += s.data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// also merge code if total size is ok...
|
||||||
|
uint32_t size = 0;
|
||||||
|
for (int i = 1; i < 5; ++i) {
|
||||||
|
size += sections[i].data.size();
|
||||||
|
}
|
||||||
|
if (size <= 0xffff) {
|
||||||
|
new_data = std::move(sections[SECT_CODE].data);
|
||||||
|
new_expr = std::move(sections[SECT_CODE].expressions);
|
||||||
|
new_number = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector< std::pair<unsigned, uint32_t> > remap;
|
||||||
|
remap.reserve(sections.size());
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < sections.size(); ++i)
|
||||||
|
remap.emplace_back(std::make_pair(i, i));
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t fudge_kdata = 0;
|
||||||
|
uint32_t fudge_data = 0;
|
||||||
|
uint32_t fudge_udata = 0;
|
||||||
|
|
||||||
|
section *s = §ions[SECT_KDATA];
|
||||||
|
fudge_kdata = new_data.size();
|
||||||
|
|
||||||
|
new_data.insert(new_data.end(), s->data.begin(), s->data.end());
|
||||||
|
new_expr.insert(new_expr.end(),
|
||||||
|
std::make_move_iterator(s->expressions.begin()),
|
||||||
|
std::make_move_iterator(s->expressions.end())
|
||||||
|
);
|
||||||
|
*s = section{};
|
||||||
|
|
||||||
|
s = §ions[SECT_DATA];
|
||||||
|
fudge_data = new_data.size();
|
||||||
|
|
||||||
|
new_data.insert(new_data.end(), s->data.begin(), s->data.end());
|
||||||
|
new_expr.insert(new_expr.end(),
|
||||||
|
std::make_move_iterator(s->expressions.begin()),
|
||||||
|
std::make_move_iterator(s->expressions.end())
|
||||||
|
);
|
||||||
|
*s = section{};
|
||||||
|
|
||||||
|
|
||||||
|
s = §ions[SECT_UDATA];
|
||||||
|
fudge_udata = new_data.size();
|
||||||
|
|
||||||
|
new_data.insert(new_data.end(), s->data.begin(), s->data.end());
|
||||||
|
new_expr.insert(new_expr.end(),
|
||||||
|
std::make_move_iterator(s->expressions.begin()),
|
||||||
|
std::make_move_iterator(s->expressions.end())
|
||||||
|
);
|
||||||
|
*s = section{};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (size > 0xffff) {
|
||||||
|
// not merged into code.
|
||||||
|
|
||||||
|
new_number = sections.size();
|
||||||
|
|
||||||
|
section tmp;
|
||||||
|
tmp.name = "data";
|
||||||
|
tmp.flags = SEC_DATA;
|
||||||
|
tmp.data = std::move(new_data);
|
||||||
|
tmp.expressions = std::move(new_expr);
|
||||||
|
sections.emplace_back(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// update all symbols with the new location / offset.
|
||||||
|
|
||||||
|
for (auto &s : symbols) {
|
||||||
|
if ((s.type & 0x0f) == S_REL) switch(s.section) {
|
||||||
|
case SECT_DATA:
|
||||||
|
s.section = new_number;
|
||||||
|
s.offset += fudge_data;
|
||||||
|
break;
|
||||||
|
case SECT_KDATA:
|
||||||
|
s.section = new_number;
|
||||||
|
s.offset += fudge_kdata;
|
||||||
|
break;
|
||||||
|
case SECT_UDATA:
|
||||||
|
s.section = new_number;
|
||||||
|
s.offset += fudge_udata;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update expressions with new section / offset.
|
||||||
|
for (auto &s : sections) {
|
||||||
|
for (auto &e : s.expressions) {
|
||||||
|
bool delta = false;
|
||||||
|
for (auto &t : e.stack) {
|
||||||
|
if (t.tag == OP_LOC) {
|
||||||
|
switch(t.section) {
|
||||||
|
case SECT_DATA:
|
||||||
|
t.section = new_number;
|
||||||
|
t.value += fudge_data;
|
||||||
|
delta = true;
|
||||||
|
break;
|
||||||
|
case SECT_KDATA:
|
||||||
|
t.section = new_number;
|
||||||
|
t.value += fudge_kdata;
|
||||||
|
delta = true;
|
||||||
|
break;
|
||||||
|
case SECT_UDATA:
|
||||||
|
t.section = new_number;
|
||||||
|
t.value += fudge_udata;
|
||||||
|
delta = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (delta) simplify_expression(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool one_file(const std::string &name) {
|
||||||
|
|
||||||
|
int fd = open(name.c_str(), O_RDONLY | O_BINARY);
|
||||||
|
if (fd < 0) {
|
||||||
|
warn("Unable to open %s", name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool rv = false;
|
||||||
|
|
||||||
|
Header h;
|
||||||
|
ssize_t ok;
|
||||||
|
|
||||||
|
ok = read(fd, &h, sizeof(h));
|
||||||
|
if (ok != sizeof(h)) {
|
||||||
|
warnx("Invalid object file: %s", name.c_str());
|
||||||
|
close(fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
le_to_host(h.magic);
|
||||||
|
le_to_host(h.version);
|
||||||
|
le_to_host(h.filetype);
|
||||||
|
|
||||||
|
if (h.magic != MOD_MAGIC || h.version != MOD_VERSION || h.filetype < MOD_OBJECT || h.filetype > MOD_LIBRARY) {
|
||||||
|
warnx("Invalid object file: %s", name.c_str());
|
||||||
|
close(fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (h.filetype == MOD_LIBRARY) {
|
||||||
|
warnx("%s is a library", name.c_str());
|
||||||
|
close(fd);
|
||||||
|
// todo -- add to library list...
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
rv = true;
|
||||||
|
lseek(fd, 0, SEEK_SET);
|
||||||
|
for(;;) {
|
||||||
|
Mod_head h;
|
||||||
|
|
||||||
|
ok = read(fd, &h, sizeof(h));
|
||||||
|
if (ok == 0) break; // eof.
|
||||||
|
|
||||||
|
rv = false;
|
||||||
|
if (ok < sizeof(h)) {
|
||||||
|
warnx("Invalid object file: %s", name.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
le_to_host(h.h_magic);
|
||||||
|
le_to_host(h.h_version);
|
||||||
|
le_to_host(h.h_filtyp);
|
||||||
|
le_to_host(h.h_namlen);
|
||||||
|
le_to_host(h.h_recsize);
|
||||||
|
le_to_host(h.h_secsize);
|
||||||
|
le_to_host(h.h_symsize);
|
||||||
|
le_to_host(h.h_optsize);
|
||||||
|
le_to_host(h.h_tot_secs);
|
||||||
|
le_to_host(h.h_num_secs);
|
||||||
|
le_to_host(h.h_num_syms);
|
||||||
|
|
||||||
|
assert(h.h_magic == MOD_MAGIC);
|
||||||
|
assert(h.h_version == 1);
|
||||||
|
assert(h.h_filtyp == 1);
|
||||||
|
|
||||||
|
|
||||||
|
std::string module_name;
|
||||||
|
{
|
||||||
|
// now read the name (h_namlen includes 0 terminator.)
|
||||||
|
std::vector<char> tmp;
|
||||||
|
tmp.resize(h.h_namlen);
|
||||||
|
ok = read(fd, tmp.data(), h.h_namlen);
|
||||||
|
if (ok != h.h_namlen) {
|
||||||
|
warnx("Invalid object file: %s", name.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
module_name.assign(tmp.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> record_data;
|
||||||
|
std::vector<uint8_t> symbol_data;
|
||||||
|
std::vector<uint8_t> section_data;
|
||||||
|
|
||||||
|
record_data.resize(h.h_recsize);
|
||||||
|
ok = read(fd, record_data.data(), h.h_recsize);
|
||||||
|
if (ok != h.h_recsize) {
|
||||||
|
warnx("Truncated object file: %s", name.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
section_data.resize(h.h_secsize);
|
||||||
|
ok = read(fd, section_data.data(), h.h_secsize);
|
||||||
|
if (ok != h.h_secsize) {
|
||||||
|
warnx("Truncated object file: %s", name.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol_data.resize(h.h_symsize);
|
||||||
|
ok = read(fd, symbol_data.data(), h.h_symsize);
|
||||||
|
if (ok != h.h_symsize) {
|
||||||
|
warnx("Truncated object file: %s", name.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// should probably pass in name and module....
|
||||||
|
one_module(record_data, section_data, symbol_data);
|
||||||
|
rv = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
void help() {
|
void help() {
|
||||||
exit(0);
|
exit(0);
|
||||||
@ -48,11 +859,26 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
if (argc == 0) usage();
|
if (argc == 0) usage();
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
for (int i = 0 ; i < argc; ++i) {
|
||||||
|
one_file(argv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
simplify();
|
||||||
|
|
||||||
|
// for each undefined, try to find it in a library...
|
||||||
|
|
||||||
|
for (const auto & s : undefined_symbols) {
|
||||||
|
printf("%s\n", s.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_end();
|
||||||
|
|
||||||
|
merge_data();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct section {
|
|
||||||
std::vector<uint8_t> data;
|
|
||||||
uint32_t offset;
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user