2015-10-28 03:57:14 +00:00
|
|
|
//
|
|
|
|
// dump_x65.cpp
|
|
|
|
// dump_x65
|
|
|
|
//
|
|
|
|
// Created by Carl-Henrik Skårstedt on 10/26/15.
|
|
|
|
// Copyright © 2015 Carl-Henrik Skårstedt. All rights reserved.
|
|
|
|
//
|
|
|
|
// The MIT License (MIT)
|
|
|
|
//
|
|
|
|
// Copyright (c) 2015 Carl-Henrik Skårstedt
|
|
|
|
//
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
|
|
|
// and associated documentation files (the "Software"), to deal in the Software without restriction,
|
|
|
|
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
|
|
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
|
|
|
|
// is furnished to do so, subject to the following conditions:
|
|
|
|
//
|
|
|
|
// The above copyright notice and this permission notice shall be included in all copies or
|
|
|
|
// substantial portions of the Software.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
|
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
|
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
|
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
|
|
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
//
|
|
|
|
// Details, source and documentation at https://github.com/Sakrac/x65.
|
|
|
|
//
|
|
|
|
// "struse.h" can be found at https://github.com/Sakrac/struse, only the header file is required.
|
|
|
|
//
|
|
|
|
|
|
|
|
#define _CRT_SECURE_NO_WARNINGS // Windows shenanigans
|
|
|
|
#define STRUSE_IMPLEMENTATION // include implementation of struse in this file
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "struse.h"
|
|
|
|
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// OBJECT FILE HANDLING
|
|
|
|
//
|
|
|
|
//
|
|
|
|
|
2015-11-07 06:48:50 +00:00
|
|
|
enum SectionType : char {
|
|
|
|
ST_UNDEFINED, // not set
|
|
|
|
ST_CODE, // default type
|
|
|
|
ST_DATA, // data section (matters for GS/OS OMF)
|
|
|
|
ST_BSS, // uninitialized data section
|
|
|
|
ST_ZEROPAGE // ununitialized data section in zero page / direct page
|
|
|
|
};
|
|
|
|
|
2015-10-28 03:57:14 +00:00
|
|
|
struct ObjFileHeader {
|
2015-11-06 05:23:38 +00:00
|
|
|
short id; // 'x6'
|
2015-10-28 03:57:14 +00:00
|
|
|
short sections;
|
|
|
|
short relocs;
|
|
|
|
short labels;
|
|
|
|
short late_evals;
|
|
|
|
short map_symbols;
|
|
|
|
unsigned int stringdata;
|
|
|
|
int bindata;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ObjFileStr {
|
|
|
|
int offs; // offset into string table
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ObjFileSection {
|
|
|
|
enum SectionFlags {
|
|
|
|
OFS_DUMMY,
|
|
|
|
OFS_FIXED,
|
|
|
|
OFS_MERGED,
|
|
|
|
};
|
|
|
|
struct ObjFileStr name;
|
|
|
|
struct ObjFileStr exp_app;
|
|
|
|
int start_address;
|
2015-11-09 02:06:45 +00:00
|
|
|
int end_address;
|
2015-10-28 03:57:14 +00:00
|
|
|
int output_size; // assembled binary size
|
|
|
|
int align_address;
|
2015-11-09 02:06:45 +00:00
|
|
|
short next_group; // next section of group
|
|
|
|
short first_group; // first section of group
|
2015-10-28 03:57:14 +00:00
|
|
|
short relocs;
|
2015-11-07 06:48:50 +00:00
|
|
|
SectionType type;
|
|
|
|
char flags;
|
2015-10-28 03:57:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ObjFileReloc {
|
|
|
|
int base_value;
|
|
|
|
int section_offset;
|
|
|
|
short target_section;
|
2015-11-06 05:23:38 +00:00
|
|
|
char bytes;
|
|
|
|
char shift;
|
2015-10-28 03:57:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ObjFileLabel {
|
|
|
|
enum LabelFlags {
|
|
|
|
OFL_EVAL = (1<<15), // Evaluated (may still be relative)
|
|
|
|
OFL_ADDR = (1<<14), // Address or Assign
|
|
|
|
OFL_CNST = (1<<13), // Constant
|
|
|
|
OFL_XDEF = OFL_CNST-1 // External (index into file array)
|
|
|
|
};
|
|
|
|
struct ObjFileStr name;
|
|
|
|
int value;
|
|
|
|
int flags; // 1<<(LabelFlags)
|
|
|
|
short section; // -1 if resolved, file section # if section rel
|
|
|
|
short mapIndex; // -1 if resolved, index into map if relative
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ObjFileLateEval {
|
|
|
|
struct ObjFileStr label;
|
|
|
|
struct ObjFileStr expression;
|
|
|
|
int address; // PC relative to section or fixed
|
2015-11-03 07:14:01 +00:00
|
|
|
int target; // offset into section memory
|
2015-10-28 03:57:14 +00:00
|
|
|
short section; // section to target
|
2015-11-03 07:14:01 +00:00
|
|
|
short rept; // value of rept for this late eval
|
2015-10-28 03:57:14 +00:00
|
|
|
short scope; // PC start of scope
|
|
|
|
short type; // label, byte, branch, word (LateEval::Type)
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ObjFileMapSymbol {
|
|
|
|
struct ObjFileStr name; // symbol name
|
|
|
|
int value;
|
|
|
|
short section;
|
|
|
|
bool local; // local labels are probably needed
|
|
|
|
};
|
|
|
|
|
|
|
|
enum ShowFlags {
|
|
|
|
SHOW_SECTIONS = 1,
|
|
|
|
SHOW_RELOCS = 2,
|
|
|
|
SHOW_LABELS = 4,
|
|
|
|
SHOW_MAP_SYMBOLS = 8,
|
|
|
|
SHOW_LATE_EVAL = 16,
|
2015-11-06 05:23:38 +00:00
|
|
|
SHOW_CODE_RANGE = 32,
|
2015-10-28 03:57:14 +00:00
|
|
|
|
|
|
|
SHOW_DEFAULT = SHOW_SECTIONS
|
|
|
|
};
|
|
|
|
|
|
|
|
char* LoadBinary(const char* filename, size_t &size)
|
|
|
|
{
|
|
|
|
if (FILE *f = fopen(filename, "rb")) {
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
|
|
size_t _size = ftell(f);
|
|
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
if (char *buf = (char*)malloc(_size)) {
|
|
|
|
fread(buf, _size, 1, f);
|
|
|
|
fclose(f);
|
|
|
|
size = _size;
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
size = 0;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
strref PoolStr(struct ObjFileStr off, const char *str_pool)
|
|
|
|
{
|
|
|
|
return off.offs>=0 ? strref(str_pool + off.offs) : strref();
|
|
|
|
}
|
|
|
|
|
|
|
|
enum LEType { // When an expression is evaluated late, determine how to encode the result
|
|
|
|
LET_LABEL, // this evaluation applies to a label and not memory
|
|
|
|
LET_ABS_REF, // calculate an absolute address and store at 0, +1
|
|
|
|
LET_ABS_L_REF, // calculate a bank + absolute address and store at 0, +1, +2
|
|
|
|
LET_ABS_4_REF, // calculate a 32 bit number
|
|
|
|
LET_BRANCH, // calculate a branch offset and store at this address
|
|
|
|
LET_BRANCH_16, // calculate a branch offset of 16 bits and store at this address
|
|
|
|
LET_BYTE, // calculate a byte and store at this address
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *late_type[] = {
|
|
|
|
"LABEL", "ABS_REF", "ABS_L_REF", "ABS_4_REF",
|
|
|
|
"BRANCH", "BRANCH_16", "BYTE" };
|
2015-11-07 06:48:50 +00:00
|
|
|
static const char *section_type[] = {
|
|
|
|
"UNDEFINED", // not set
|
|
|
|
"CODE", // default type
|
|
|
|
"DATA", // data section (matters for GS/OS OMF)
|
|
|
|
"BSS", // uninitialized data section
|
|
|
|
"ZEROPAGE" // ununitialized data section in zero page / direct page
|
|
|
|
};
|
|
|
|
static const int section_type_str = sizeof(section_type) / sizeof(section_type[0]);
|
2015-10-28 03:57:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
void ReadObjectFile(const char *file, unsigned int show = SHOW_DEFAULT)
|
|
|
|
{
|
|
|
|
size_t size;
|
|
|
|
if (char *data = LoadBinary(file, size)) {
|
|
|
|
struct ObjFileHeader &hdr = *(struct ObjFileHeader*)data;
|
|
|
|
size_t sum = sizeof(hdr) + hdr.sections*sizeof(struct ObjFileSection) +
|
|
|
|
hdr.relocs * sizeof(struct ObjFileReloc) + hdr.labels * sizeof(struct ObjFileLabel) +
|
|
|
|
hdr.late_evals * sizeof(struct ObjFileLateEval) +
|
|
|
|
hdr.map_symbols * sizeof(struct ObjFileMapSymbol) + hdr.stringdata + hdr.bindata;
|
2015-11-06 05:23:38 +00:00
|
|
|
if (hdr.id == 0x7836 && sum == size) {
|
2015-10-28 03:57:14 +00:00
|
|
|
struct ObjFileSection *aSect = (struct ObjFileSection*)(&hdr + 1);
|
|
|
|
struct ObjFileReloc *aReloc = (struct ObjFileReloc*)(aSect + hdr.sections);
|
|
|
|
struct ObjFileLabel *aLabels = (struct ObjFileLabel*)(aReloc + hdr.relocs);
|
|
|
|
struct ObjFileLateEval *aLateEval = (struct ObjFileLateEval*)(aLabels + hdr.labels);
|
|
|
|
struct ObjFileMapSymbol *aMapSyms = (struct ObjFileMapSymbol*)(aLateEval + hdr.late_evals);
|
|
|
|
const char *str_orig = (const char*)(aMapSyms + hdr.map_symbols);
|
2015-11-09 02:06:45 +00:00
|
|
|
size_t code_start = sum - hdr.bindata, code_curr = code_start;
|
2015-10-28 03:57:14 +00:00
|
|
|
|
|
|
|
// sections
|
|
|
|
if (show & SHOW_SECTIONS) {
|
|
|
|
int reloc_idx = 0;
|
|
|
|
for (int si = 0; si < hdr.sections; si++) {
|
|
|
|
struct ObjFileSection &s = aSect[si];
|
|
|
|
short f = s.flags;
|
2015-11-07 06:48:50 +00:00
|
|
|
const char *tstr = s.type > 0 && s.type < section_type_str ? section_type[s.type] : "error";
|
2015-10-28 03:57:14 +00:00
|
|
|
if (f & (1 << ObjFileSection::OFS_MERGED)) {
|
2015-11-09 02:06:45 +00:00
|
|
|
printf("Section %d: \"" STRREF_FMT "\": (Merged)",
|
2015-10-28 03:57:14 +00:00
|
|
|
reloc_idx, STRREF_ARG(PoolStr(s.name, str_orig)));
|
|
|
|
} else if (f & (1 << ObjFileSection::OFS_DUMMY)) {
|
|
|
|
if (f&(1 << ObjFileSection::OFS_FIXED)) {
|
2015-11-09 02:06:45 +00:00
|
|
|
printf("Section %d: \"" STRREF_FMT "\": (Dummy [%s], fixed at $%04x)",
|
2015-11-07 06:48:50 +00:00
|
|
|
reloc_idx, STRREF_ARG(PoolStr(s.name, str_orig)), tstr, s.start_address);
|
2015-10-28 03:57:14 +00:00
|
|
|
} else {
|
2015-11-09 02:06:45 +00:00
|
|
|
printf("Section %d: \"" STRREF_FMT "\": (Dummy [%s], relative)",
|
2015-11-07 06:48:50 +00:00
|
|
|
reloc_idx, STRREF_ARG(PoolStr(s.name, str_orig)), tstr);
|
2015-10-28 03:57:14 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (f&(1 << ObjFileSection::OFS_FIXED)) {
|
2015-11-09 02:06:45 +00:00
|
|
|
printf("Section %d: \"" STRREF_FMT "\": (Fixed [%s] $%04x, $%x bytes)",
|
|
|
|
reloc_idx, STRREF_ARG(PoolStr(s.name, str_orig)), tstr, s.start_address, s.end_address - s.start_address);
|
2015-10-28 03:57:14 +00:00
|
|
|
} else {
|
2015-11-09 02:06:45 +00:00
|
|
|
printf("Section %d: \"" STRREF_FMT "\": (Relative [%s], $%x bytes, align to $%x)",
|
|
|
|
reloc_idx, STRREF_ARG(PoolStr(s.name, str_orig)), tstr, s.end_address - s.start_address, s.align_address);
|
2015-10-28 03:57:14 +00:00
|
|
|
}
|
|
|
|
strref export_append = PoolStr(s.exp_app, str_orig);
|
|
|
|
if (export_append)
|
2015-11-09 02:06:45 +00:00
|
|
|
printf(" Export as: \"" STRREF_FMT "\"", STRREF_ARG(export_append));
|
2015-10-28 03:57:14 +00:00
|
|
|
}
|
2015-11-09 02:06:45 +00:00
|
|
|
if (s.first_group >= 0)
|
|
|
|
printf(" grouped with section %d", s.first_group);
|
|
|
|
if (s.next_group >= 0)
|
|
|
|
printf(" next in group: %d", s.next_group);
|
|
|
|
if ((show & SHOW_CODE_RANGE) && s.output_size)
|
2015-11-09 08:07:55 +00:00
|
|
|
printf(" code: $%x-$%x", (int)code_curr, (int)(code_curr + s.output_size));
|
2015-11-09 02:06:45 +00:00
|
|
|
code_curr += s.output_size;
|
|
|
|
printf("\n");
|
2015-10-28 03:57:14 +00:00
|
|
|
reloc_idx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (show & SHOW_RELOCS) {
|
2015-11-06 05:23:38 +00:00
|
|
|
int curRel = 0;
|
2015-10-28 03:57:14 +00:00
|
|
|
for (int si = 0; si < hdr.sections; si++) {
|
|
|
|
printf("section %d relocs: %d\n", si, aSect[si].relocs);
|
|
|
|
for (int r = 0; r < aSect[si].relocs; r++) {
|
2015-11-06 05:23:38 +00:00
|
|
|
struct ObjFileReloc &rs = aReloc[curRel++];
|
|
|
|
printf("Reloc: section %d offset $%x base $%x bytes: %d shift: %d\n",
|
|
|
|
rs.target_section, rs.section_offset, rs.base_value, rs.bytes, rs.shift);
|
2015-10-28 03:57:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (show & SHOW_MAP_SYMBOLS) {
|
|
|
|
for (int mi = 0; mi < hdr.map_symbols; mi++) {
|
|
|
|
struct ObjFileMapSymbol &m = aMapSyms[mi];
|
|
|
|
printf("Symbol: \"" STRREF_FMT "\" section: %d value: $%04x%s\n",
|
|
|
|
STRREF_ARG(PoolStr(m.name, str_orig)), m.section, m.value, m.local ? " (local)" : "");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (show & SHOW_LABELS) {
|
|
|
|
for (int li = 0; li < hdr.labels; li++) {
|
|
|
|
struct ObjFileLabel &l = aLabels[li];
|
|
|
|
strref name = PoolStr(l.name, str_orig);
|
|
|
|
short f = l.flags;
|
|
|
|
int external = f & ObjFileLabel::OFL_XDEF;
|
2015-11-09 02:06:45 +00:00
|
|
|
printf("Label: " STRREF_FMT " %s base $%x", STRREF_ARG(name), external==ObjFileLabel::OFL_XDEF ? "external" : "protected", l.value);
|
2015-10-28 03:57:14 +00:00
|
|
|
if (external!=ObjFileLabel::OFL_XDEF)
|
|
|
|
printf(" file %d", external);
|
|
|
|
if (f & ObjFileLabel::OFL_CNST)
|
|
|
|
printf(" constant");
|
|
|
|
if (f & ObjFileLabel::OFL_ADDR)
|
|
|
|
printf(" PC rel");
|
|
|
|
if (f & ObjFileLabel::OFL_EVAL)
|
|
|
|
printf(" evaluated");
|
|
|
|
|
|
|
|
printf(" map index: %d\n", l.mapIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (show & SHOW_LATE_EVAL) {
|
|
|
|
for (int li = 0; li < hdr.late_evals; ++li) {
|
|
|
|
struct ObjFileLateEval &le = aLateEval[li];
|
|
|
|
strref name = PoolStr(le.label, str_orig);
|
|
|
|
if (le.type == LEType::LET_LABEL) {
|
|
|
|
printf("Late eval label: " STRREF_FMT " expression: " STRREF_FMT "\n",
|
|
|
|
STRREF_ARG(name), STRREF_ARG(PoolStr(le.expression, str_orig)));
|
|
|
|
} else {
|
|
|
|
printf("Late eval section: %d addr: $%04x scope: $%04x target: $%04x expression: " STRREF_FMT " %s\n",
|
|
|
|
le.section, le.address, le.scope, le.target, STRREF_ARG(PoolStr(le.expression, str_orig)),
|
|
|
|
late_type[le.type]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-06 05:23:38 +00:00
|
|
|
if (show & SHOW_CODE_RANGE)
|
2015-11-07 06:48:50 +00:00
|
|
|
printf("Code block: $%x - $%x (%d bytes)\n", (int)code_start, (int)size, (int)(size - hdr.bindata));
|
2015-11-06 05:23:38 +00:00
|
|
|
|
2015-10-28 03:57:14 +00:00
|
|
|
// restore previous section
|
|
|
|
} else
|
|
|
|
printf("Not a valid x65 file\n");
|
|
|
|
} else
|
|
|
|
printf("Could not open %s\n", file);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
const char *file = nullptr;
|
2015-11-06 05:23:38 +00:00
|
|
|
unsigned int show = 0;
|
2015-10-28 03:57:14 +00:00
|
|
|
for (int a = 1; a<argc; a++) {
|
|
|
|
if (argv[a][0]=='-') {
|
|
|
|
strref arg = argv[a]+1;
|
2015-11-06 05:23:38 +00:00
|
|
|
if (arg.same_str("sections"))
|
|
|
|
show = show | SHOW_SECTIONS;
|
|
|
|
else if (arg.same_str("relocs"))
|
|
|
|
show = show | SHOW_RELOCS;
|
|
|
|
else if (arg.same_str("labels"))
|
|
|
|
show = show | SHOW_LABELS;
|
|
|
|
else if (arg.same_str("map"))
|
|
|
|
show = show | SHOW_MAP_SYMBOLS;
|
|
|
|
else if (arg.same_str("late_eval"))
|
|
|
|
show = show | SHOW_LATE_EVAL;
|
|
|
|
else if (arg.same_str("code"))
|
|
|
|
show = show | SHOW_CODE_RANGE;
|
|
|
|
else if (arg.same_str("all"))
|
|
|
|
show = SHOW_SECTIONS | SHOW_RELOCS | SHOW_LABELS | SHOW_MAP_SYMBOLS | SHOW_LATE_EVAL | SHOW_CODE_RANGE;
|
2015-10-28 03:57:14 +00:00
|
|
|
} else if (!file)
|
|
|
|
file = argv[a];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!file) {
|
2015-11-17 07:55:16 +00:00
|
|
|
printf("Usage:\ndump_x65 filename [-sections] [-relocs] [-labels] [-map] [-late_eval] [-code]\n");
|
2015-10-28 03:57:14 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc>1) {
|
2015-11-06 05:23:38 +00:00
|
|
|
ReadObjectFile(file, show ? show : SHOW_DEFAULT);
|
2015-10-28 03:57:14 +00:00
|
|
|
}
|
|
|
|
}
|