/* Copyright (c) 2013, Alexey Frunze All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. */ /*****************************************************************************/ /* */ /* MIPS ELF to RetroBSD a.out convertor with support for MIPS icache */ /* */ /*****************************************************************************/ #include #include #include #include #include #include typedef unsigned char uchar, uint8; typedef signed char schar, int8; typedef unsigned short ushort, uint16; typedef short int16; #if UINT_MAX >= 0xFFFFFFFF typedef unsigned uint32; typedef int int32; #else typedef unsigned long uint32; typedef long int32; #endif typedef unsigned uint; typedef unsigned long ulong; typedef long long longlong; typedef unsigned long long ulonglong; #if ULONG_MAX >= 0xFFFFFFFFFFFFFFFFULL typedef unsigned long uint64; typedef long int64; #else typedef unsigned long long uint64; typedef long long int64; #endif #define C_ASSERT(expr) extern char CAssertExtern[(expr)?1:-1] C_ASSERT(CHAR_BIT == 8); C_ASSERT(sizeof(uint16) == 2); C_ASSERT(sizeof(uint32) == 4); C_ASSERT(sizeof(uint64) == 8); C_ASSERT(sizeof(size_t) >= 4); #pragma pack(push,1) typedef struct { uint8 e_ident[16]; uint16 e_type; uint16 e_machine; uint32 e_version; uint32 e_entry; uint32 e_phoff; uint32 e_shoff; uint32 e_flags; uint16 e_ehsize; uint16 e_phentsize; uint16 e_phnum; uint16 e_shentsize; uint16 e_shnum; uint16 e_shstrndx; } Elf32Hdr; typedef struct { uint32 sh_name; uint32 sh_type; uint32 sh_flags; uint32 sh_addr; uint32 sh_offset; uint32 sh_size; uint32 sh_link; uint32 sh_info; uint32 sh_addralign; uint32 sh_entsize; } Elf32SectHdr; typedef struct { uint32 a_magic; /* magic number */ #define OMAGIC 0407 /* old impure format */ uint32 a_text; /* size of text segment */ uint32 a_data; /* size of initialized data */ uint32 a_bss; /* size of uninitialized data */ uint32 a_reltext; /* size of text relocation info */ uint32 a_reldata; /* size of data relocation info */ uint32 a_syms; /* size of symbol table */ uint32 a_entry; /* entry point */ } AoutHdr; #pragma pack(pop) C_ASSERT(sizeof(Elf32Hdr) == 52); C_ASSERT(sizeof(Elf32SectHdr) == 40); C_ASSERT(sizeof(AoutHdr) == 32); typedef struct { const char* Name; uint32 FileOffs; uint32 Addr; uint32 Size; uint32 Flags; uint32 Flags2; } tSection; tSection* Sections = NULL; uint SectionCnt = 0; char* SectNames = NULL; uint32 EntryPointAddr = 0; FILE* ElfFile = NULL; FILE* AoutFile = NULL; const char* AoutName = NULL; void error(char* format, ...) { va_list vl; va_start(vl, format); if (ElfFile) fclose(ElfFile); if (AoutFile) fclose(AoutFile); if (AoutName != NULL) remove(AoutName); puts(""); vprintf(format, vl); va_end(vl); exit(-1); } int SectAddrCompare(const void* pa, const void* pb) { const tSection *p1 = pa, *p2 = pb; if (p1->Addr < p2->Addr) return -1; else if (p1->Addr > p2->Addr) return +1; return 0; } void WriteZeroes(FILE* f, uint32 size) { static const char zeroes[1024]; while (size) { uint32 sz; if (size > sizeof zeroes) sz = sizeof zeroes; else sz = size; if (fwrite(zeroes, 1, sz, f) != sz) error("Can't write file\n"); size -= sz; } } void CopyFileData(FILE* fto, FILE* ffrom, uint32 size) { char buf[1024]; while (size) { uint32 sz; if (size > sizeof buf) sz = sizeof buf; else sz = size; if (fread(buf, 1, sz, ffrom) != sz) error("Can't read file\n"); if (fwrite(buf, 1, sz, fto) != sz) error("Can't write file\n"); size -= sz; } } int main(int argc, char** argv) { Elf32Hdr elfHdr; Elf32SectHdr sectHdr; uint idx; int unsupported = 0; int verbose = 0; AoutHdr aoutHdr; uint32 addr; uint32 endAddr = 0, codeEndAddr, bssStartAddr; if (argc > 1 && !strcmp(argv[1], "-v")) { verbose = 1; argc--; argv++; } if (argc != 3 || !(ElfFile = fopen(argv[1], "rb")) || !(AoutFile = fopen(AoutName = argv[2], "wb"))) error("Usage:\n ice2aout [-v] \n"); if (fread(&elfHdr, 1, sizeof elfHdr, ElfFile) != sizeof elfHdr) error("Can't read file\n"); if (memcmp(elfHdr.e_ident, "\x7F""ELF", 4)) error("Not an ELF file\n"); if (elfHdr.e_ident[6] != 1) error("Not a v1 ELF file\n"); if (elfHdr.e_ehsize != sizeof elfHdr) error("Unexpected ELF header size\n"); if (elfHdr.e_shentsize != sizeof sectHdr) error("Unexpected ELF section size\n"); if (elfHdr.e_ident[4] != 1) error("Not a 32-bit file\n"); if (elfHdr.e_ident[5] != 1) error("Not a little-endian file\n"); if (elfHdr.e_type != 2) error("Not an executable file\n"); if (elfHdr.e_machine != 8) error("Not a MIPS executable\n"); if (fseek(ElfFile, elfHdr.e_shoff + elfHdr.e_shstrndx * sizeof sectHdr, SEEK_SET)) error("Can't read file\n"); if (fread(§Hdr, 1, sizeof sectHdr, ElfFile) != sizeof sectHdr) error("Can't read file\n"); if ((SectNames = malloc(sectHdr.sh_size)) == NULL) error("Out of memory\n"); if (fseek(ElfFile, sectHdr.sh_offset, SEEK_SET)) error("Can't read file\n"); if (fread(SectNames, 1, sectHdr.sh_size, ElfFile) != sectHdr.sh_size) error("Can't read file\n"); if ((Sections = calloc(1, (elfHdr.e_shnum + 1) * sizeof(tSection))) == NULL) error("Out of memory\n"); for (idx = 0; idx < elfHdr.e_shnum; idx++) { const char* name = ""; if (fseek(ElfFile, elfHdr.e_shoff + idx * sizeof sectHdr, SEEK_SET)) error("Can't read file\n"); if (fread(§Hdr, 1, sizeof sectHdr, ElfFile) != sizeof sectHdr) error("Can't read file\n"); if (sectHdr.sh_type == 0) memset(§Hdr, 0, sizeof sectHdr); if (sectHdr.sh_name) name = SectNames + sectHdr.sh_name; unsupported |= (!strcmp(name, ".dynsym") || !strcmp(name, ".dynstr") || !strcmp(name, ".dynamic") || !strcmp(name, ".hash") || !strcmp(name, ".got") || !strcmp(name, ".plt") || sectHdr.sh_type == 5 || // SHT_HASH sectHdr.sh_type == 6 || // SHT_DYNAMIC sectHdr.sh_type == 11); // SHT_DYNSYM // Keep only allocatable sections of non-zero size if ((sectHdr.sh_flags & 2) && sectHdr.sh_size) // SHF_ALLOC and size > 0 { Sections[SectionCnt].FileOffs = 0; Sections[SectionCnt].Name = name; Sections[SectionCnt].Addr = sectHdr.sh_addr; Sections[SectionCnt].Size = sectHdr.sh_size; Sections[SectionCnt].Flags = (sectHdr.sh_flags & 1) | ((sectHdr.sh_flags & 4) >> 1); // bit0=Writable,bit1=eXecutable if (sectHdr.sh_type == 1) // SHT_PROGBITS { Sections[SectionCnt].FileOffs = sectHdr.sh_offset; } SectionCnt++; } } EntryPointAddr = elfHdr.e_entry; // Sort sections by address as we'll need them in order // and without gaps inbetween qsort(Sections, SectionCnt, sizeof Sections[0], &SectAddrCompare); if (verbose) { printf(" # XAW VirtAddr FileOffs Size Name\n"); for (idx = 0; idx < SectionCnt; idx++) { tSection* p = &Sections[idx]; printf("%2u %c%c%c 0x%08lX 0x%08lX %10lu %s\n", idx, "-X"[(p->Flags / 2) & 1], "-A"[1], "-W"[(p->Flags / 1) & 1], (ulong)p->Addr, (ulong)p->FileOffs, (ulong)p->Size, p->Name); } printf("Entry: 0x%08lX\n", (ulong)EntryPointAddr); puts(""); } if (unsupported) error("Dynamically linked or unsupported type of executable\n"); // Write an empty a.out header at first, it will be updated later memset(&aoutHdr, 0, sizeof aoutHdr); if (fwrite(&aoutHdr, 1, sizeof aoutHdr, AoutFile) != sizeof aoutHdr) error("Can't write file\n"); #define USER_DATA_START 0x7F008000 #define MAXMEM (96*1024) #define USER_DATA_END (USER_DATA_START + MAXMEM) if (verbose) printf("Phase 1: Processing sections in the range 0x%08lX ... 0x%08lX ...\n", (ulong)USER_DATA_START, (ulong)USER_DATA_END - 1); addr = USER_DATA_START; // Copy non-cached sections for (idx = 0; idx < SectionCnt; idx++) { tSection* p = &Sections[idx]; if (p->Addr + p->Size >= p->Addr && p->Addr >= USER_DATA_START && p->Addr + p->Size <= USER_DATA_END) { if (verbose) printf("Copying %s ...\n", p->Name); if (idx && (Sections[idx - 1].Flags2 & 1)) { if (p->Addr < Sections[idx - 1].Addr + Sections[idx - 1].Size) error("Sections must not intersect in memory\n"); if ((p->Flags & 2) && !(Sections[idx - 1].Flags & 2)) // executable after non-executable error("Code sections must precede data sections in memory\n"); } if ((p->Flags & 2) && !p->FileOffs) // executable and initialized to all zeroes error("Code sections must not be initialized to all zeroes\n"); // If this section has code/data in it, if it's not initialized to all zeroes... if (p->FileOffs) { // Fill inter-section gaps (and .bss-like sections that aren't at the end) // with zeroes. This lets me order sections more flexibly and yet make // sure they all are properly initialized. if (addr < p->Addr) { WriteZeroes(AoutFile, p->Addr - addr); addr = p->Addr; } // Copy section if (fseek(ElfFile, p->FileOffs, SEEK_SET)) error("Can't read file\n"); CopyFileData(AoutFile, ElfFile, p->Size); addr += p->Size; } p->Flags2 |= 1; // section has been processed endAddr = p->Addr + p->Size; } else { if (verbose) printf("Skipping %s ...\n", p->Name); } } if (endAddr == 0) error("There are no copiable sections in this range\n"); if (verbose) printf("Phase 2: Processing sections outside the range 0x%08lX ... 0x%08lX ...\n", (ulong)USER_DATA_START, (ulong)USER_DATA_END - 1); // Append cached section(s) for (idx = 0; idx < SectionCnt; idx++) { tSection* p = &Sections[idx]; if (p->Addr + p->Size >= p->Addr && (p->Addr >= USER_DATA_END || p->Addr + p->Size <= USER_DATA_START)) { if (verbose) printf("Copying %s ...\n", p->Name); // Copy section if (p->FileOffs) { if (fseek(ElfFile, p->FileOffs, SEEK_SET)) error("Can't read file\n"); CopyFileData(AoutFile, ElfFile, p->Size); } p->Flags2 |= 1; // section has been processed } } // Make sure no section has been left unprocessed for (idx = 0; idx < SectionCnt; idx++) { tSection* p = &Sections[idx]; if (!(p->Flags2 & 1)) error("Not all sections have been processed, e.g. %s hasn't\n", p->Name); } // Update a.out header aoutHdr.a_magic = OMAGIC; aoutHdr.a_entry = EntryPointAddr; codeEndAddr = endAddr; for (idx = SectionCnt - 1; idx != (uint)-1; idx--) { tSection* p = &Sections[idx]; if (p->Addr + p->Size >= p->Addr && p->Addr >= USER_DATA_START && p->Addr + p->Size <= USER_DATA_END) { // While not executable, keep going, executable sections are first if (!(p->Flags & 2)) codeEndAddr = p->Addr; else break; } } bssStartAddr = endAddr; for (idx = SectionCnt - 1; idx != (uint)-1; idx--) { tSection* p = &Sections[idx]; if (p->Addr + p->Size >= p->Addr && p->Addr >= USER_DATA_START && p->Addr + p->Size <= USER_DATA_END) { // While initialized to all zeroes, keep going if (!p->FileOffs) bssStartAddr = p->Addr; else break; } } aoutHdr.a_text = codeEndAddr - USER_DATA_START; aoutHdr.a_data = bssStartAddr - codeEndAddr; aoutHdr.a_bss = endAddr - bssStartAddr; if (fseek(AoutFile, 0, SEEK_SET)) error("Can't write file\n"); if (fwrite(&aoutHdr, 1, sizeof aoutHdr, AoutFile) != sizeof aoutHdr) error("Can't write file\n"); if (fclose(AoutFile)) error("Can't write file\n"); fclose(ElfFile); if (verbose) { printf("a.out header:\n" " text size: %lu\n" " data size: %lu\n" " bss size: %lu\n", (ulong)aoutHdr.a_text, (ulong)aoutHdr.a_data, (ulong)aoutHdr.a_bss); printf("Done\n"); } return 0; }