support (?) for multiple segments (linker.xl)

add =, EQU, GEQ, POS, LEN, KBD support.
This commit is contained in:
Kelvin Sherlock 2019-12-13 23:49:10 -05:00
parent d33a823cc5
commit 903a6be697
4 changed files with 400 additions and 207 deletions

384
link.cpp
View File

@ -17,6 +17,7 @@
#include <err.h>
#include <sysexits.h>
#include <unistd.h>
#include <afp/finder_info.h>
@ -58,13 +59,79 @@ namespace {
std::unordered_map<std::string, unsigned> symbol_map;
std::vector<symbol> symbol_table;
std::vector<pending_reloc> relocations;
std::vector<omf::segment> segments;
std::vector<std::vector<pending_reloc>> relocations;
}
/*
Variable types:
linker symbol table includes =, EQU, GEQ, and KBD
GEQ - global absolute label, in effect for all subsequent asms.
inhibits KBD, otherwise causes duplicate symbol errors during assembly.
KBD - same as GEQ
EQU - same as GEQ BUT symbol is discarded after ASM (ie, only in effect for 1 assembly)
= - internal to link script (DO, etc). not passed to assembler. not passed to linker.
POS - current offset
LEN - length of last linked file
a = assembler
l = linker
c = command file
a l c
EQU y n n
= n n y
GEQ y y y
KBD y y y
POS n y n
LEN n y n
seems like it might be nice for POS and LEN to be available in the command file, eg
POS xxx
DO xxx>4096
ERR too big
ELS
DS 4096-xxx
FIN
*/
namespace {
/* script related */
unsigned lkv = 1;
unsigned ver = 2;
unsigned ftype = 0xb3;
unsigned atype = 0x0000;
unsigned kind = 0x0000;
unsigned sav = 0;
bool end = false;
size_t pos_offset = 0;
size_t len_offset = 0;
/* do/els/fin stuff */
uint32_t active_bits = 1;
bool active = true;
std::unordered_map<std::string, uint32_t> local_symbol_table;
}
/* nb - pointer may be invalidated by next call */
symbol *find_symbol(const std::string &name, bool insert) {
@ -81,6 +148,36 @@ symbol *find_symbol(const std::string &name, bool insert) {
return &rv;
}
void define(std::string name, uint32_t value, int type) {
bool warn = false;
if (type & 4) {
/* command script */
auto iter = local_symbol_table.find(name);
if (iter == local_symbol_table.end()) {
local_symbol_table.emplace(std::make_pair(name, value));
} else if (iter->second != value) {
warn = true;
}
}
if (type & 2) {
/* linker */
auto e = find_symbol(name, true);
if (e->defined) {
if (!e->absolute || e->value != value) {
warn = true;
}
} else {
e->absolute = true;
e->defined = true;
e->file = "-D";
e->value = value;
}
}
if (warn) warnx("duplicate symbol %s", name.c_str());
}
static void process_labels(byte_view &data, cookie &cookie) {
@ -121,6 +218,7 @@ static void process_labels(byte_view &data, cookie &cookie) {
}
e->defined = true;
e->file = cookie.file;
e->segment = segments.size();
if (flag & SYMBOL_ABSOLUTE) {
e->absolute = true;
e->value = value;
@ -140,6 +238,7 @@ static void process_labels(byte_view &data, cookie &cookie) {
static void process_reloc(byte_view &data, cookie &cookie) {
auto &seg = segments.back();
auto &pending = relocations.back();
for(;;) {
assert(data.size());
@ -229,7 +328,7 @@ static void process_reloc(byte_view &data, cookie &cookie) {
r.shift = shift;
symbol_table[r.id].count += 1;
relocations.emplace_back(r);
pending.emplace_back(r);
} else {
omf::reloc r;
r.size = size;
@ -240,7 +339,7 @@ static void process_reloc(byte_view &data, cookie &cookie) {
seg.relocs.emplace_back(r);
}
/* clear out the inline relocation data */
for(unsigned i = 0; i < size; ++i) {
for (unsigned i = 0; i < size; ++i) {
seg.data[offset + i] = 0;
}
//cookie.zero.emplace_back(std::make_pair(offset, size));
@ -278,7 +377,7 @@ static void process_unit(const std::string &path) {
errx(1, "Invalid aux type %s", path.c_str());
}
omf::segment &seg = segments.back();
auto &seg = segments.back();
cookie.begin = seg.data.size();
cookie.end = cookie.begin + offset;
@ -304,47 +403,70 @@ static void process_unit(const std::string &path) {
/* now relocations */
process_reloc(rr, cookie);
// LEN support
len_offset = offset;
}
static void resolve(void) {
/* this needs to be updated if supporting multiple segments */
auto &seg = segments.back();
for (unsigned seg_num = 0; seg_num < segments.size(); ++seg_num) {
for (auto &r : relocations) {
assert(r.id < symbol_map.size());
const auto &e = symbol_table[r.id];
auto &seg = segments[seg_num];
auto &pending = relocations[seg_num];
/* if this is an absolute value, do the math */
if (!e.defined) {
warnx("%s is not defined", e.name.c_str());
continue;
}
if (e.absolute) {
uint32_t value = e.value + r.value;
/* shift is a uint8_t so negating doesn't work right */
value >>= -(int8_t)r.shift;
for (auto &r : pending) {
assert(r.id < symbol_map.size());
const auto &e = symbol_table[r.id];
unsigned offset = r.offset;
unsigned size = r.size;
while (size--) {
seg.data[offset++] = value & 0xff;
value >>= 8;
/* if this is an absolute value, do the math */
if (!e.defined) {
warnx("%s is not defined", e.name.c_str());
continue;
}
continue;
if (e.absolute) {
uint32_t value = e.value + r.value;
/* shift is a uint8_t so negating doesn't work right */
value >>= -(int8_t)r.shift;
unsigned offset = r.offset;
unsigned size = r.size;
while (size--) {
seg.data[offset++] = value & 0xff;
value >>= 8;
}
continue;
}
if (e.segment == seg_num) {
r.value += e.value;
seg.relocs.emplace_back(r);
continue;
}
omf::interseg inter;
inter.size = r.size;
inter.shift = r.shift;
inter.offset = r.offset;
inter.segment = e.segment;
inter.segment_offset = r.value + e.value;
seg.intersegs.emplace_back(inter);
}
pending.clear();
r.value += e.value;
seg.relocs.emplace_back(r);
/* sort them */
std::sort(seg.relocs.begin(), seg.relocs.end(), [](const auto &a, const auto &b){
return a.offset < b.offset;
});
std::sort(seg.intersegs.begin(), seg.intersegs.end(), [](const auto &a, const auto &b){
return a.offset < b.offset;
});
}
relocations.clear();
/* sort them */
std::sort(seg.relocs.begin(), seg.relocs.end(), [](const omf::reloc &a, const omf::reloc &b){
return a.offset < b.offset;
});
}
static void print_symbols2(void) {
@ -382,6 +504,9 @@ static void print_symbols(void) {
}
void finish(void) {
resolve();
@ -389,49 +514,54 @@ void finish(void) {
try {
save_omf(save_file, segments, compress, express);
set_file_type(save_file, 0xb3, 0x0000);
set_file_type(save_file, ftype, atype);
} catch (std::exception &ex) {
errx(EX_OSERR, "%s: %s", save_file.c_str(), ex.what());
}
}
void process_files(int argc, char **argv) {
segments.emplace_back();
for (int i = 0; i < argc; ++i) {
char *path = argv[i];
try {
process_unit(path);
} catch (std::exception &ex) {
errx(EX_DATAERR, "%s: %s", path, ex.what());
}
static bool op_needs_label(opcode_t op) {
switch (op) {
case OP_KBD:
case OP_EQ:
case OP_EQU:
case OP_GEQ:
return true;
default:
return false;
}
}
namespace {
unsigned lkv = 1;
unsigned ver = 2;
unsigned ftype = 0xb3;
unsigned atype = 0x0000;
unsigned kind = 0x0000;
unsigned do_level = 0;
bool end = false;
uint32_t active_bits = 1;
bool active = true;
std::unordered_map<std::string, uint32_t> local_symbol_table;
static bool op_after_end(opcode_t op) {
switch(op) {
case OP_END:
case OP_CMD:
return true;
default:
return false;
}
}
static uint32_t eval(operand_t op) {
if (std::holds_alternative<uint32_t>(op)) return std::get<uint32_t>(op);
std::string &name = std::get<std::string>(op);
auto iter = local_symbol_table.find(name);
if (iter == local_symbol_table.end()) throw std::runtime_error("Bad symbol");
return iter->second;
}
void evaluate(label_t label, opcode_t opcode, operand_t operand) {
// todo - should move operand parsing to here.
switch(opcode) {
case OP_DO:
if (active_bits & 0x80000000) throw std::runtime_error("too much do do");
active_bits <<= 1;
active_bits |= std::get<uint32_t>(operand) ? 1 : 0;
active_bits |= eval(operand) ? 1 : 0;
active = (active_bits & (active_bits + 1)) == 0;
return;
break;
@ -452,18 +582,25 @@ void evaluate(label_t label, opcode_t opcode, operand_t operand) {
throw std::runtime_error("fin without do");
}
active = (active_bits & (active_bits + 1)) == 0;
return;
break;
default:
break;
}
if (!active) return;
if (label.empty() && op_needs_label(opcode))
throw std::runtime_error("Bad label");
if (end && !op_after_end(opcode)) return;
switch(opcode) {
case OP_END:
if (!end && lkv == 2) {
/* finish up */
segments.pop_back();
relocations.pop_back();
if (!segments.empty())
finish();
}
@ -476,8 +613,8 @@ void evaluate(label_t label, opcode_t opcode, operand_t operand) {
struct tm *tm = localtime(&t);
char buffer[32];
strftime(buffer, sizeof(buffer), "%d-%b-%y %H:%M:%S %p", tm);
for(char &c : buffer) c = std::toupper(c);
strftime(buffer, sizeof(buffer), "%d-%b-%y %l:%M:%S %p", tm);
for (char &c : buffer) c = std::toupper(c);
fprintf(stdout, "%s\n", buffer);
break;
@ -485,38 +622,44 @@ void evaluate(label_t label, opcode_t opcode, operand_t operand) {
case OP_TYP:
ftype = std::get<uint32_t>(operand);
// todo - should evaluate with file type dictionary.
ftype = eval(operand);
break;
case OP_ADR:
atype = std::get<uint32_t>(operand);
atype = eval(operand);
break;
case OP_KND:
kind = std::get<uint32_t>(operand);
kind = eval(operand);
break;
case OP_LKV:
case OP_LKV: {
/* specify linker version */
/* 0 = binary, 1 = Linker.GS, 2 = Linker.XL, 3 = convert to OMF object file */
switch (std::get<uint32_t>(operand)) {
uint32_t value = eval(operand);
switch (value) {
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:
lkv = std::get<uint32_t>(operand);
lkv = value;
break;
default:
throw std::runtime_error("bad linker version");
}
break;
}
case OP_VER:
case OP_VER: {
/* OMF version, 1 or 2 */
if (std::get<uint32_t>(operand) != 2)
throw std::runtime_error("bad OMF version");
break;
uint32_t value = eval(operand);
if (value != 2)
throw std::runtime_error("bad OMF version");
ver = value;
break;
}
case OP_LNK:
if (end) throw std::runtime_error("link after end");
@ -543,12 +686,79 @@ void evaluate(label_t label, opcode_t opcode, operand_t operand) {
/* add a new segment */
segments.emplace_back();
relocations.emplace_back();
pos_offset = 0; // POS support
}
++sav;
break;
case OP_KBD: {
std::string prompt;
char buffer[256];
if (!isatty(STDIN_FILENO)) return;
/* todo if already defined (via -D) don't prompt */
if (local_symbol_table.find(label) != local_symbol_table.end())
return;
if (std::holds_alternative<std::string>(operand))
prompt = std::get<std::string>(operand);
if (prompt.empty()) prompt = "Give value for " + label;
prompt += ": ";
fputs(prompt.c_str(), stdout);
fflush(stdout);
char *cp = fgets(buffer, sizeof(buffer), stdin);
if (!cp) return;
uint32_t value = 0;
// evaluate string. expressions and labels are allowed....
define(label, value, LBL_KBD);
break;
}
case OP_POS: {
// POS label << sets label = current segment offset
// POS << resets pos byte counter.
if (std::holds_alternative<std::monostate>(operand)) {
pos_offset = segments.back().data.size();
} else {
std::string label = std::get<std::string>(operand);
uint32_t value = segments.back().data.size() - pos_offset;
define(label, value, LBL_POS);
}
break;
}
case OP_LEN: {
// LEN label
// sets label = length of most recent file linked
std::string label = std::get<std::string>(operand);
uint32_t value = len_offset;
define(label, value, LBL_LEN);
break;
}
case OP_EQ:
define(label, eval(operand), LBL_EQ);
break;
case OP_EQU:
define(label, eval(operand), LBL_EQU);
break;
case OP_GEQ:
define(label, eval(operand), LBL_GEQ);
break;
case OP_FAS:
/* fast linker, only 1 file allowed */
case OP_OVR:
case OP_PUT:
case OP_IF:
break;
case OP_ASM:
default:
@ -572,6 +782,9 @@ void process_script(const char *path) {
}
segments.emplace_back();
relocations.emplace_back();
int no = 1;
int errors = 0;
char *line = NULL;
@ -580,11 +793,8 @@ void process_script(const char *path) {
ssize_t len = getline(&line, &cap, fp);
if (len == 0) break;
if (len < 0) {
warn("read error");
++errors;
break;
}
if (len < 0) break;
/* strip trailing ws */
while (len && isspace(line[len-1])) --len;
line[len] = 0;
@ -609,3 +819,21 @@ void process_script(const char *path) {
exit(errors ? EX_DATAERR : 0);
}
void process_files(int argc, char **argv) {
segments.emplace_back();
relocations.emplace_back();
for (int i = 0; i < argc; ++i) {
char *path = argv[i];
try {
process_unit(path);
} catch (std::exception &ex) {
errx(EX_DATAERR, "%s: %s", path, ex.what());
}
}
}

28
link.h
View File

@ -15,12 +15,37 @@ struct symbol {
std::string file;
uint32_t value = 0;
unsigned id = 0;
unsigned segment = 0;
unsigned count = 0;
bool absolute = false;
bool defined = false;
};
/*
a = assembler
l = linker
c = command file
a l c
EQU y n n
= n n y
GEQ y y y
KBD y y y
POS n y n
LEN n y n
*/
enum {
LBL_EQU = (1 << 0),
LBL_GEQ = (1 << 0) | (1 << 1) | (1 << 2),
LBL_KBD = (1 << 0) | (1 << 1) | (1 << 2),
LBL_D = (1 << 0) | (1 << 1) | (1 << 2),
LBL_EQ = (1 << 2),
LBL_POS = (1 << 1),
LBL_LEN = (1 << 1),
};
void process_script(const char *argv);
void process_files(int argc, char **argv);
@ -28,4 +53,7 @@ void process_files(int argc, char **argv);
symbol *find_symbol(const std::string &name, bool insert = true);
void define(std::string name, uint32_t value, int type);
#endif

View File

@ -93,18 +93,7 @@ static void add_define(std::string str) {
}
symbol *e = find_symbol(str);
if (e->defined && e->absolute && e->value == value) return;
if (e->defined) {
warnx("%s previously defined", str.c_str());
return;
}
e->defined = true;
e->absolute = true;
e->file = "-D";
e->value = value;
define(str, value, LBL_D);
}
/* .ends_with() is c++20 */

View File

@ -30,6 +30,10 @@
ident = [:<-~][0-~]*;
ws = [ \t];
eof = "\x00";
number_prefix = [%$0-9];
ident_prefix = [:-~];
string_prefix = ['"];
*/
namespace {
@ -89,122 +93,11 @@ namespace {
}
static uint32_t number_operand(const char *YYCURSOR, bool required = true) {
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
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) {
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
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->second;
}
*/
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) {
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
// const char *YYMARKER = nullptr;
uint32_t rv = 0;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
@ -247,13 +140,13 @@ exit:
static std::string x_label_operand(const char *YYCURSOR) {
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
// const char *YYMARKER = nullptr;
std::string rv;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
ident {
std::string s(iter, YYCURSOR);
rv = std::string(iter, YYCURSOR);
goto exit;
}
*/
@ -286,6 +179,61 @@ exit:
static operand_t number_operand(const char *YYCURSOR, bool required = true) {
const char *iter = YYCURSOR;
// const char *YYMARKER = nullptr;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;*] | eof {
if (!required) return 0;
throw std::invalid_argument("missing operand");
}
number_prefix {
return x_number_operand(iter);
}
ident_prefix { return x_label_operand(iter); }
*/
}
static uint32_t type_operand(const char *YYCURSOR, bool required = true) {
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
uint32_t rv = 0;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;*] | eof {
if (!required) return rv;
throw std::invalid_argument("missing operand");
}
number_prefix { return x_number_operand(iter); }
[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->second;
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;
const char *YYMARKER = nullptr;
@ -312,7 +260,7 @@ static int ovr_operand(const char *YYCURSOR) {
static std::string label_operand(const char *YYCURSOR, bool required = true) {
std::string rv;
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
// const char *YYMARKER = nullptr;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;*] | eof {
@ -336,7 +284,7 @@ exit:
static std::string path_operand(const char *YYCURSOR, bool required = true) {
std::string rv;
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
// const char *YYMARKER = nullptr;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;*] | eof {
@ -375,7 +323,7 @@ static std::string string_operand(const char *YYCURSOR, bool required = true) {
std::string rv;
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
// const char *YYMARKER = nullptr;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;*] | eof {