From 559139fce64d685708c283397033bf152afd00a3 Mon Sep 17 00:00:00 2001 From: Oxore Date: Sun, 15 Sep 2024 00:12:25 +0300 Subject: Add initial COFF support (coff2bin, readcoff) --- src/readcoff.cpp | 251 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 src/readcoff.cpp (limited to 'src/readcoff.cpp') diff --git a/src/readcoff.cpp b/src/readcoff.cpp new file mode 100644 index 0000000..f9398c4 --- /dev/null +++ b/src/readcoff.cpp @@ -0,0 +1,251 @@ +/* SPDX-License-Identifier: Unlicense + */ + +#include "coff_image.h" +#include "data_buffer.h" + +#define OPTPARSE_IMPLEMENTATION +#define OPTPARSE_API static +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#pragma GCC diagnostic ignored "-Wshadow" +#endif +#include "optparse/optparse.h" +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#include +#include +#include +#include +#include + +static bool g_print_header = false; +static bool g_print_optional_header = false; +static bool g_print_section_headers = false; + +static void PrintCoffHeader(FILE *output_stream, const COFF::FileHeader &header) +{ + fprintf(output_stream, "COFF Header:\n"); + fprintf(output_stream, " Magic: %02x %02x (0x%x as big endian)\n", + (header.magic >> 8) & 0xff, header.magic & 0xff, header.magic); + fprintf(output_stream, " Number of section headers: %u\n", header.nsections); + // TODO proper time with year, month, day, hour, minute and second + fprintf(output_stream, " Time and date: %u\n", header.timedate); + fprintf(output_stream, " Symbol table file offset: %u (0x%x)\n", + header.symtable_offset, header.symtable_offset); + fprintf(output_stream, " Number of symbols: %u (0x%x)\n", + header.nsymbols, header.nsymbols); + fprintf(output_stream, " Optional header size, bytes: %u (0x%x)\n", + header.optional_header_nbytes, header.optional_header_nbytes); + // TODO Print detailed flags information + fprintf(output_stream, " Flags: 0x%x\n", header.flags); +} + +static void PrintOptionalHeader(FILE *output_stream, const COFF::OptionalHeader &header) +{ + fprintf(output_stream, "Optional Header:\n"); + fprintf(output_stream, " Magic: %02x %02x (0x%x as big endian)\n", + (header.magic >> 8) & 0xff, header.magic & 0xff, header.magic); + fprintf(output_stream, " Version: 0x%04x\n", header.version); + fprintf(output_stream, " Size of text, bytes: %u\n", header.tsize); + fprintf(output_stream, " Size of initialized data, bytes: %u\n", header.dsize); + fprintf(output_stream, " Size of uninitialized data, bytes: %u\n", header.bsize); + fprintf(output_stream, " Program entry point: %u (0x%x)\n", + header.entry, header.entry); + fprintf(output_stream, " Base address of text: %u (0x%x)\n", + header.text_start, header.text_start); + fprintf(output_stream, " Base address of daata: %u (0x%x)\n", + header.data_start, header.data_start); +} + +static const char *SectionTypeStr(COFF::SectionType type) +{ + switch (type) { + case COFF::SectionType::Regular: + return "Regular ARL"; + case COFF::SectionType::Dummy: + return "Dubby R"; + case COFF::SectionType::NoLoad: + return "NoLoad AR"; + case COFF::SectionType::Grouped: + return "Grouped"; + case COFF::SectionType::Padding: + return "Padding L"; + case COFF::SectionType::Copy: + return "Cppy A L"; + case COFF::SectionType::Text: + return "Text ARL"; + case COFF::SectionType::Data: + return "Data ARL"; + case COFF::SectionType::Bss: + return "BSS ARL"; + case COFF::SectionType::Org: + return "Org ARL"; + case COFF::SectionType::Info: + return "Info"; + case COFF::SectionType::Overlay: + return "Ovsrlay R"; + case COFF::SectionType::Lib: + return "Lib ARL"; + } + return "Unknwon"; +} + +static void PrintSectionHeader(FILE *output_stream, size_t index, const COFF::SectionHeader &header) +{ + fprintf(output_stream, + " [%2zu] %-8s ""%08x " "%08x " "%08x " "%08x " "%08x " "\n", + index, + header.name, + header.paddr, + header.vaddr, + header.size, + header.section_offset, + header.reloc_offset); + fprintf(output_stream, + " %08x " " %04x %04x %08x " "%s\n", + header.lineno_offset, + header.nreloc, + header.nlineno, + header.flags, + SectionTypeStr(static_cast(header.flags & 0x8ff))); +} + +static int ReadCoff(FILE *input_stream, FILE *output_stream) +{ + auto input = DataBuffer::FromStream(input_stream); + const size_t input_size = input.occupied_size; + if (input_size == 0) { + fprintf(stderr, "DataBuffer::FromStream(input, input_stream): " + "Error: No data has been read\n"); + return EXIT_FAILURE; + } + const COFF::Image coff(static_cast(input)); + if (!coff.IsValid()) { + fprintf(stderr, "Error: COFF image is not valid: %s\n", coff.Error()); + return EXIT_FAILURE; + } + const auto &file_header = coff.FileHeader(); + if (g_print_header) { + PrintCoffHeader(output_stream, file_header); + } + if (g_print_optional_header && coff.HasOptionalHeader()) { + PrintOptionalHeader(output_stream, coff.OptionalHeader()); + } + if (g_print_section_headers && file_header.nsections) { + fprintf(output_stream, "Section headers:\n"); + fprintf(output_stream, + " [Nr] Name PhysAddr VirtAddr Size DatOffst RelOffst\n" + " LnNoOfft NReloc NLineNo Flags Type Alloc/Reloc/Load\n"); + for (size_t i = 0; i < file_header.nsections; i++) { + PrintSectionHeader(output_stream, i, coff.GetSectionHeader(i)); + } + } + return EXIT_SUCCESS; +} + +static void PrintUsage(FILE *s, const char *argv0) +{ + // Please, keep all lines in 80 columns range when printed. + fprintf(s, + "Usage: %s [options] \n" + "Options:\n" + " -H, --help Show this message.\n" + " -a --all Equivalent to: -h -o -S\n" + " -h --file-header Display the COFF file header\n" + " -o --optional-header Display the optional COFF file header\n" + " -S --section-headers Display the sections' header\n" + " --sections An alias for --section-headers\n" + " -e --headers Equivalent to: -h -o -S\n" + " COFF file with the machine code to extract\n" + " ('-' means stdin).\n" + , argv0); +} + +int main(int, char* argv[]) +{ + struct optparse_long longopts[] = { + {"help", 'H', OPTPARSE_NONE}, + {"all", 'a', OPTPARSE_NONE}, + {"file-header", 'h', OPTPARSE_NONE}, + {"optional-header", 'o', OPTPARSE_NONE}, + {"section-headers", 'S', OPTPARSE_NONE}, + {"sections", 0x80, OPTPARSE_NONE}, + {"headers", 'e', OPTPARSE_NONE}, + {}, + }; + const char *input_file_name = nullptr; + struct optparse options; + optparse_init(&options, argv); + // Parse opts + int option; + while ((option = optparse_long(&options, longopts, NULL)) != -1) { + switch (option) { + case 'H': + PrintUsage(stdout, argv[0]); + return EXIT_SUCCESS; + break; + case 'h': + g_print_header = true; + break; + case 'o': + g_print_optional_header = true; + break; + case 0x80: + case 'S': + g_print_section_headers = true; + break; + case 'a': + case 'e': + g_print_header = true; + g_print_optional_header = true; + g_print_section_headers = true; + break; + case '?': + fprintf(stderr, "main: optparse_long: Error: \"%s\"\n", options.errmsg); + return EXIT_FAILURE; + } + } + // Parse input file name + char *arg; + while ((arg = optparse_arg(&options))) { + if (input_file_name == nullptr) { + input_file_name = arg; + } else { + fprintf(stderr, "error: too many free arguments provided\n"); + return EXIT_FAILURE; + } + } + // Open the files + FILE *input_stream = nullptr; + FILE *output_stream = stdout; + if (input_file_name) { + if (0 == strcmp(input_file_name, "-")) { + input_stream = stdin; + } else { + input_stream = fopen(input_file_name, "r"); + } + if (input_stream == nullptr) { + const int err = errno; + fprintf(stderr, "main: fopen(\"%s\", \"r\"): Error (%d): \"%s\"\n", input_file_name, err, strerror(err)); + return EXIT_FAILURE; + } + } else { + fprintf(stderr, "main: Error: no input file name specified, see usage below.\n"); + PrintUsage(stderr, argv[0]); + return EXIT_FAILURE; + } + if (!g_print_header && !g_print_optional_header && !g_print_section_headers) { + fprintf(stderr, "main: Error: no display options specified, see usage below.\n"); + PrintUsage(stdout, argv[0]); + return EXIT_FAILURE; + } + // Run the program + const int ret = ReadCoff(input_stream, output_stream); + fclose(output_stream); + fclose(input_stream); + return ret; +} -- cgit v1.2.3