//////////////////////////////////////////////////////////////////////////////// // // OpenHantek // dsoextractfw.c // Copyright (C) 2008 Oleg Khudyakov // prcoder@potrebitel.ru // Copyright (C) 2010, 2011 Oliver Haag // oliver.haag@gmail.com // // This program is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free // Software Foundation, either version 3 of the License, or (at your option) // any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // // You should have received a copy of the GNU General Public License along with // this program. If not, see . // //////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include int extractFirmware(const char *filenameDriver, const char *filenameFirmware, const char *filenameLoader); int writeIntelHex(const char *filename, unsigned char *data, unsigned int length); static const char *filenameEndFirmware = "-firmware.hex"; static const char *filenameEndLoader = "-loader.hex"; static const char *filenameEndDriver = "1.sys"; static const char *nameTarget = "pei-i386"; static const char *nameSection = ".data"; static const char *nameSymbolFirmware = "_firmware"; static const char *nameSymbolLoader = "_loader"; /// \brief Parse commandline arguments. /// \return 0 on success, negative on error. int main(int argc, char **argv) { char *filenameDriver, *filenameFirmware, *filenameLoader; char *charPointer; int prefixLength; if(argc < 2) { fprintf(stderr, "Usage: %s [] []\n", argv[0]); return -1; } filenameDriver = argv[1]; prefixLength = strlen(filenameDriver) - strlen(filenameEndDriver); if(argc < 3) { // Guess correct filename for firmware filenameFirmware = malloc(prefixLength + strlen(filenameEndFirmware) + 1); memcpy(filenameFirmware, filenameDriver, prefixLength); strcpy(filenameFirmware + prefixLength, filenameEndFirmware); // Convert filename to lowercase charPointer = strrchr(filenameFirmware, '/'); if(charPointer == NULL) charPointer = filenameFirmware; for(; *charPointer != 0; charPointer++) *charPointer = tolower(*charPointer); } else { filenameFirmware = argv[2]; } if(argc < 4) { // Guess correct filename for loader filenameLoader = malloc(prefixLength + strlen(filenameEndLoader) + 1); memcpy(filenameLoader, filenameDriver, prefixLength); strcpy(filenameLoader + prefixLength, filenameEndLoader); // Convert filename to lowercase charPointer = strrchr(filenameLoader, '/'); if(charPointer == NULL) charPointer = filenameLoader; for(; *charPointer != 0; charPointer++) *charPointer = tolower(*charPointer); } else { filenameFirmware = argv[3]; } return extractFirmware(filenameDriver, filenameFirmware, filenameLoader); } /// \brief Extract firmware and loader data from original driver file. /// \param filenameDriver Name of the original driver file. /// \param filenameFirmware Name of the file where the firmware should be saved. /// \param filenameDriver Name of the file where the loader should be saved. /// \return 0 on success, negative on error. int extractFirmware(const char *filenameDriver, const char *filenameFirmware, const char *filenameLoader) { bfd *bfdDriver; asection *sectionData; asymbol **symbols; unsigned int symbolCount; unsigned currentSymbol; const char *symbolName; bfd_size_type offsetFirmware = 0, offsetLoader = 0; bfd_size_type lengthFirmware = 0, lengthLoader = 0; unsigned char *bufferFirmware, *bufferLoader; // Initialize bfd and open driver file bfd_init(); bfdDriver = bfd_openr(filenameDriver, nameTarget); if(!bfdDriver) { bfd_perror("Error opening file"); return -1; } if(!bfd_check_format(bfdDriver, bfd_object)) { bfd_perror("bfd_check_format"); bfd_close(bfdDriver); return -2; } // Search for the data section for(sectionData = bfdDriver->sections; sectionData != NULL; sectionData = sectionData->next) if(strcmp(sectionData->name, nameSection) == 0) break; if(sectionData == NULL) { fprintf(stderr, "Section %s not found\n", nameSection); return -3; } printf("Section %s found (starting at 0x%04lx, %li bytes)\n", nameSection, (unsigned long int) sectionData->filepos, (long int) sectionData->size); // Search for the symbols we want symbols = malloc(bfd_get_symtab_upper_bound(bfdDriver)); symbolCount = bfd_canonicalize_symtab(bfdDriver, symbols); for(currentSymbol = 0; currentSymbol < symbolCount; currentSymbol++) { symbolName = bfd_asymbol_name(symbols[currentSymbol]); if(strcmp(symbolName, nameSymbolFirmware) == 0) offsetFirmware = symbols[currentSymbol]->value; if(strcmp(symbolName, nameSymbolLoader) == 0) offsetLoader = symbols[currentSymbol]->value; } free(symbols); // Calculate position in section and length offsetFirmware -= sectionData->filepos; offsetLoader -= sectionData->filepos; lengthFirmware = offsetLoader - offsetFirmware; lengthLoader = sectionData->size - lengthFirmware; printf("Symbol %s found (offset 0x%04lx, %li bytes)\n", nameSymbolFirmware, (unsigned long int) offsetFirmware, (long int) lengthFirmware); printf("Symbol %s found (offset 0x%04lx, %li bytes)\n", nameSymbolLoader, (unsigned long int) offsetLoader, (long int) lengthLoader); // Extract data bufferFirmware = malloc(lengthFirmware); bufferLoader = malloc(lengthLoader); if (bufferFirmware == NULL || bufferLoader == NULL) { fprintf(stderr, "Can't allocate memory\n"); bfd_close(bfdDriver); return -4; } if(!bfd_get_section_contents(bfdDriver, sectionData, bufferFirmware, offsetFirmware, lengthFirmware)) { bfd_perror("Can't get firmware contents"); bfd_close(bfdDriver); return -5; } if(!bfd_get_section_contents(bfdDriver, sectionData, bufferLoader, offsetLoader, lengthLoader)) { bfd_perror("Can't get loader contents"); bfd_close(bfdDriver); return -6; } printf("Saving firmware as %s\n", filenameFirmware); writeIntelHex(filenameFirmware, bufferFirmware, lengthFirmware); free(bufferFirmware); printf("Saving loader as %s\n", filenameLoader); writeIntelHex(filenameLoader, bufferLoader, lengthLoader); free(bufferLoader); bfd_close(bfdDriver); return 0; } /// \brief Save data to a file in intel hex format. /// \param filename Name of the output file. /// \param data Pointer to the binary data that should be stored. /// \param length Size of the data that should be stored. /// \return 0 on success, negative on error. int writeIntelHex(const char *filename, unsigned char *data, unsigned int length) { FILE *file; unsigned char crc, eof; unsigned int dataIndex, byteIndex, byteCount; unsigned char *dataPointer; file = fopen(filename, "wt"); if(!file) { fprintf(stderr, "Can't open %s for writing\n", filename); fclose(file); return -1; } for(dataIndex = 0; dataIndex < length; dataIndex += 22) { eof = -1; // Always check for End of File Record dataPointer = data + dataIndex; // Start code and byte count byteCount = *dataPointer; fprintf(file, ":%02X", byteCount); if(byteCount != 0) eof = 0; crc = -*dataPointer++; dataPointer++; // Address fprintf(file, "%04X", *(unsigned short *) dataPointer); if(*(unsigned short *) dataPointer != 0) eof = 0; crc -= *dataPointer++; crc -= *dataPointer++; // Record type fprintf(file, "%02X", *dataPointer); if(*dataPointer != 0x01) eof = 0; crc -= *dataPointer++; // Data for(byteIndex = 0; byteIndex < byteCount; byteIndex++) { fprintf(file, "%02X", *dataPointer); crc -= *dataPointer++; } // CRC fprintf(file, "%02X\n", crc); if(eof) break; } fclose(file); return 0; }