From a455e48a2b02ea363cd4b1f3b179457759dea957 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Thu, 15 Dec 2022 21:13:08 -0500 Subject: [PATCH] v1 omf support (-1), improved big endian support, -S to convert PAGE0 to a stack segment. --- link.cpp | 22 +++++---- omf.cpp | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++----- omf.h | 14 ++++++ 3 files changed, 150 insertions(+), 20 deletions(-) diff --git a/link.cpp b/link.cpp index 6ce5ac9..5b1d0fb 100644 --- a/link.cpp +++ b/link.cpp @@ -41,8 +41,6 @@ struct { bool v = false; - bool C = false; - bool X = false; bool S = false; std::string o; @@ -50,8 +48,10 @@ struct { std::vector L; unsigned errors = 0; - uint16_t file_type; - uint32_t aux_type; + uint16_t file_type = 0; + uint32_t aux_type = 0; + + unsigned omf_flags = 0; } flags; @@ -1672,14 +1672,19 @@ int main(int argc, char **argv) { int c; - while ((c = getopt(argc, argv, "vCXL:l:o:t:")) != -1) { + while ((c = getopt(argc, argv, "vCXSL:l:o:t:")) != -1) { switch(c) { case 'h': usage(0); break; case 'v': flags.v = true; break; - case 'X': flags.X = true; break; - case 'C': flags.C = true; break; + case 'S': flags.S = true; break; + + case '1': flags.omf_flags |= OMF_V1; break; + case 'X': flags.omf_flags |= OMF_NO_EXPRESS; break; + case 'C': flags.omf_flags |= OMF_NO_SUPER; break; + case 'o': flags.o = optarg; break; + case 'l': { if (*optarg) flags.l.emplace_back(optarg); break; @@ -1775,10 +1780,9 @@ int main(int argc, char **argv) { flags.file_type = 0xb3; } - void save_omf(const std::string &path, std::vector &segments, bool compress, bool expressload); int set_file_type(const std::string &path, uint16_t file_type, uint32_t aux_type); - save_omf(flags.o, omf_segments, !flags.C, !flags.X); + save_omf(flags.o, omf_segments, flags.omf_flags); set_file_type(flags.o, flags.file_type, flags.aux_type); } diff --git a/omf.cpp b/omf.cpp index a3fcf6d..03f4e80 100644 --- a/omf.cpp +++ b/omf.cpp @@ -17,6 +17,18 @@ #define O_BINARY 0 #endif +enum class endian { +#ifdef _WIN32 + little = 0, + big = 1, + native = little +#else + little = __ORDER_LITTLE_ENDIAN__, + big = __ORDER_BIG_ENDIAN__, + native = __BYTE_ORDER__ +#endif +}; + #pragma pack(push, 1) struct omf_header { uint32_t bytecount = 0; @@ -66,6 +78,87 @@ struct omf_express_header { static_assert(sizeof(omf_header) == 44, "OMF Header not packed"); static_assert(sizeof(omf_express_header) == 48, "OMF Express Header not packed"); + +static void swap(uint8_t &x) {} +static void swap(uint16_t &x) { + #if defined(__GNUC__) + x = __builtin_bswap16(x); + #else + x = (x >> 8) | (x << 8); + #endif +} +static void swap(uint32_t &x) { + #if defined(__GNUC__) + x = __builtin_bswap32(x); + #else + x = ((x & 0xff000000) >> 24) | + ((x & 0x00ff0000) >> 8) | + ((x & 0x0000ff00) << 8) | + ((x & 0x000000ff) << 24); + #endif +} + + +static void to_little(struct omf_header &h) { + if (endian::native != endian::little) { + swap(h.bytecount); + swap(h.reserved_space); + swap(h.length); + swap(h.unused1); + swap(h.lablen); + swap(h.numlen); + swap(h.version); + swap(h.banksize); + swap(h.kind); + swap(h.unused2); + swap(h.org); + swap(h.alignment); + swap(h.numsex); + swap(h.unused3); + swap(h.segnum); + swap(h.entry); + swap(h.dispname); + swap(h.dispdata); + } +} + +static void to_little(struct omf_express_header &h) { + if (endian::native != endian::little) { + swap(h.lconst_mark); + swap(h.lconst_size); + swap(h.reloc_mark); + swap(h.reloc_size); + swap(h.unused1); + swap(h.lablen); + swap(h.numlen); + swap(h.version); + swap(h.banksize); + swap(h.kind); + swap(h.unused2); + swap(h.org); + swap(h.alignment); + swap(h.numsex); + swap(h.unused3); + swap(h.segnum); + swap(h.entry); + swap(h.dispname); + swap(h.dispdata); + } +} + + +static void to_v1(struct omf_header &h) { + + // KIND op value used as-is, no translation. + + h.version = 1; + // byte count -> block count + h.bytecount = (h.bytecount + 511) >> 9; + h.unused1 = h.kind; + h.kind = 0; +} + + void push(std::vector &v, uint8_t x) { v.push_back(x); } @@ -194,7 +287,7 @@ enum { SUPER_INTERSEG36, }; -uint32_t add_relocs(std::vector &data, size_t data_offset, omf::segment &seg, bool compress) { +uint32_t add_relocs(std::vector &data, size_t data_offset, omf::segment &seg, bool compress, bool super) { std::array< optional, 38 > ss; @@ -203,9 +296,9 @@ uint32_t add_relocs(std::vector &data, size_t data_offset, omf::segment for (auto &r : seg.relocs) { - if (r.can_compress()) { + if (compress && r.can_compress()) { - if (compress) { + if (super) { if (r.shift == 0 && r.size == 2) { constexpr int n = SUPER_RELOC2; @@ -267,9 +360,9 @@ uint32_t add_relocs(std::vector &data, size_t data_offset, omf::segment } for (const auto &r : seg.intersegs) { - if (r.can_compress()) { + if (compress && r.can_compress()) { - if (compress) { + if (super) { if (r.shift == 0 && r.size == 3) { constexpr int n = SUPER_INTERSEG1; @@ -384,7 +477,7 @@ void save_bin(const std::string &path, omf::segment &segment, uint32_t org) { } -void save_omf(const std::string &path, std::vector &segments, bool compress, bool expressload) { +void save_omf(const std::string &path, std::vector &segments, unsigned flags) { // expressload doesn't support links to other files. // fortunately, we don't either. @@ -392,6 +485,17 @@ void save_omf(const std::string &path, std::vector &segments, bool std::vector expr_headers; std::vector expr_offsets; + + bool compress = !(flags & OMF_NO_COMPRESS); + bool super = !(flags & OMF_NO_SUPER); + bool expressload = !(flags & OMF_NO_EXPRESS); + bool v1 = flags & OMF_V1; + + if (v1) { + expressload = false; + super = false; + } + int fd; fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); if (fd < 0) { @@ -470,17 +574,13 @@ void save_omf(const std::string &path, std::vector &segments, bool uint32_t reloc_offset = offset + sizeof(omf_header) + data.size(); uint32_t reloc_size = 0; - reloc_size = add_relocs(data, data_offset, s, compress); + reloc_size = add_relocs(data, data_offset, s, compress, super); // 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()); @@ -514,6 +614,17 @@ void save_omf(const std::string &path, std::vector &segments, bool push(expr_headers, s.segname); } + if (v1) to_v1(h); + to_little(h); + + offset += write(fd, &h, sizeof(h)); + offset += write(fd, data.data(), data.size()); + + // version 1 needs 512-byte padding for all but final segment. + if (v1 && &s != &segments.back()) { + static uint8_t zero[512]; + offset += write(fd, zero, 512 - (offset & 511) ); + } } if (expressload) { @@ -553,6 +664,7 @@ void save_omf(const std::string &path, std::vector &segments, bool h.bytecount = data.size() + sizeof(omf_header); + to_little(h); lseek(fd, 0, SEEK_SET); write(fd, &h, sizeof(h)); write(fd, data.data(), data.size()); diff --git a/omf.h b/omf.h index 79d02bf..6437791 100644 --- a/omf.h +++ b/omf.h @@ -5,6 +5,7 @@ #include #include + namespace omf { enum opcode : uint8_t { @@ -78,4 +79,17 @@ namespace omf { } +enum { + // flags + OMF_V1 = 1, + OMF_V2 = 0, + OMF_NO_SUPER = 2, + OMF_NO_COMPRESS = 4, + OMF_NO_EXPRESS = 8 + +}; + +void save_omf(const std::string &path, std::vector &segments, unsigned flags); + + #endif