/* Copyright (C) 2015 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */ #include #include #include #include #include #include #define STUB_INFO_MAGIC "StubInfoMagic!!" static _v2_prog_type type; static int type_initialized = 0; static const _v2_prog_type *_check_v2_prog_internal (int pf); const _v2_prog_type *_check_v2_prog(const char *program, int pf) { const _v2_prog_type *prog_type; if (type_initialized && type.stubinfo) free(type.stubinfo); type_initialized = 1; memset(&type, 0, sizeof(type)); if (program) { pf = open(program, O_RDONLY|O_BINARY); if (pf < 0) return &type; } prog_type = _check_v2_prog_internal(pf); if (program) close(pf); if (prog_type) type.valid = 1; return &type; } static const _v2_prog_type *_check_v2_prog_internal (int pf) { unsigned short header[5]; lseek(pf, 0, SEEK_SET); if (read(pf, header, sizeof(header)) != sizeof(header)) return NULL; if (header[0] == 0x010b || header[0] == 0x014c) { unsigned char firstbytes[1]; unsigned long coffhdr[40]; /* Seems to be an unstubbed COFF. See what the first opcode is to determine if it's v1.x or v2 COFF (or an impostor). FIXME: the code here assumes that any COFF that's not a V1 can only be V2. What about other compilers that use COFF? */ type.object_format = _V2_OBJECT_FORMAT_COFF; if (lseek(pf, 2, 1) < 0 || read(pf, coffhdr, sizeof(coffhdr)) != sizeof(coffhdr) || lseek(pf, coffhdr[10 + 5], 0) < 0 || read(pf, firstbytes, 1) != 1) /* scnptr */ /* "Aha! An impostor!" (The Adventure game) */ type.object_format = _V2_OBJECT_FORMAT_UNKNOWN; else if (firstbytes[0] != 0xa3) /* opcode of movl %eax, 0x12345678 (V1) */ type.version.v.major = 2; else type.version.v.major = 1; type.exec_format = _V2_EXEC_FORMAT_COFF; } else if (header[0] == 0x5a4d) /* "MZ" */ { char go32stub[9]; off_t coff_start = (unsigned long)header[2]*512L; off_t exe_start; off_t real_exe_start; type.exec_format = _V2_EXEC_FORMAT_EXE; if (lseek(pf, 0x3c, SEEK_SET) != 0x3c) return NULL; if (read(pf, &real_exe_start, 4) != 4) return NULL; /* Detect if EXE is really a PE-COFF EXE. */ if (real_exe_start != 0) { unsigned long magic; /* This logic should NOT fail if real_exe_start is something preposterously large. The reason is that DJGPP stubbed executables do NOT have a zero dword at offset 3Ch, contrary to what RBIL says. You *have* been warned! */ if (lseek(pf, real_exe_start, SEEK_SET) == real_exe_start && read(pf, &magic, 4) == 4 && magic == 0x00004550) /* PE */ { type.object_format = _V2_OBJECT_FORMAT_PE_COFF; return &type; } } if (header[1]) coff_start += (long)header[1] - 512L; exe_start = (unsigned long)header[4]*16L; if (lseek(pf, exe_start, SEEK_SET) != exe_start) return NULL; if (read(pf, go32stub, 8) != 8) return NULL; go32stub[8] = 0; if (strcmp(go32stub, "go32stub") == 0) { type.version.v.major = 2; type.object_format = _V2_OBJECT_FORMAT_COFF; type.exec_format = _V2_EXEC_FORMAT_STUBCOFF; } else { int stub_offset; char magic[16]; int struct_length; unsigned short coff_id; type.version.v.major = 1; if (lseek(pf, coff_start - 4, SEEK_SET) != coff_start-4) return NULL; if (read(pf, &stub_offset, 4) != 4) return NULL; if (read(pf, &coff_id, 2) != 2) return NULL; if (coff_id == 0x010b || coff_id == 0x014c) { type.object_format = _V2_OBJECT_FORMAT_COFF; type.exec_format = _V2_EXEC_FORMAT_STUBCOFF; } if (lseek(pf, stub_offset, 0) != stub_offset) return NULL; if (read(pf, magic, 16) != 16) return NULL; if (memcmp(STUB_INFO_MAGIC, magic, 16) == 0) { if (read(pf, &struct_length, 4) != 4) return NULL; type.stubinfo = (_v1_stubinfo *)malloc(struct_length); memcpy(type.stubinfo->magic, magic, 16); type.stubinfo->struct_length = struct_length; if (read(pf, type.stubinfo->go32, struct_length - 20) != struct_length - 20) return NULL; type.has_stubinfo = 1; } } } else if (header[0] == 0x2123) /* "#!" */ { type.exec_format = _V2_EXEC_FORMAT_UNIXSCRIPT; } return &type; }