diff --git a/src/tools/Jamfile b/src/tools/Jamfile index 75a6c94107..0c966c0281 100644 --- a/src/tools/Jamfile +++ b/src/tools/Jamfile @@ -42,6 +42,9 @@ BuildPlatformMain mkindex : mkindex.cpp : $(HOST_LIBBE) ; BuildPlatformMain rmattr : rmattr.cpp : $(HOST_LIBBE) ; +BuildPlatformMain set_haiku_revision : set_haiku_revision.cpp + : $(HOST_LIBSTDC++) ; + BuildPlatformMain settype : settype.cpp : $(HOST_LIBBE) ; BuildPlatformMain setversion : setversion.cpp : $(HOST_LIBBE) ; diff --git a/src/tools/set_haiku_revision.cpp b/src/tools/set_haiku_revision.cpp new file mode 100644 index 0000000000..d2e99e793e --- /dev/null +++ b/src/tools/set_haiku_revision.cpp @@ -0,0 +1,596 @@ +/* + * Copyright 2007, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + + +using std::string; +using std::max; +using std::min; + + +// #pragma mark - ELF definitions + + +// types +typedef uint32_t Elf32_Addr; +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Off; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Word; + +// e_ident indices +#define EI_MAG0 0 +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_PAD 7 +#define EI_NIDENT 16 + +// object file header +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +// e_ident EI_CLASS and EI_DATA values +#define ELFCLASSNONE 0 +#define ELFCLASS32 1 +#define ELFCLASS64 2 +#define ELFDATANONE 0 +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +// program header +typedef struct { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +// p_type +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDIR 6 +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff + +// section header +typedef struct { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +// sh_type values +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + +// special section indexes +#define SHN_UNDEF 0 + +static const uint32_t kMaxELFHeaderSize = sizeof(Elf32_Ehdr) + 32; +static const char kELFFileMagic[4] = { 0x7f, 'E', 'L', 'F' }; + + +// #pragma mark - Usage + +// usage +static const char *kUsage = +"Usage: %s \n" +"\n" +"Finds the haiku revision section in ELF object file and replaces the\n" +"writes the number given by into the first 32 bits of the\n" +"section.\n" +; + +// command line args +static int sArgc; +static const char *const *sArgv; + +// print_usage +void +print_usage(bool error) +{ + // get nice program name + const char *programName = (sArgc > 0 ? sArgv[0] : "resattr"); + if (const char *lastSlash = strrchr(programName, '/')) + programName = lastSlash + 1; + + // print usage + fprintf((error ? stderr : stdout), kUsage, programName); +} + +// print_usage_and_exit +static +void +print_usage_and_exit(bool error) +{ + print_usage(error); + exit(error ? 1 : 0); +} + + +// #pragma mark - Exception + + +class Exception { +public: + // constructor + Exception() + : fError(errno), + fDescription() + { + } + + // constructor + Exception(const char* format,...) + : fError(errno), + fDescription() + { + va_list args; + va_start(args, format); + SetTo(errno, format, args); + va_end(args); + } + + // constructor + Exception(int error) + : fError(error), + fDescription() + { + } + + // constructor + Exception(int error, const char* format,...) + : fError(error), + fDescription() + { + va_list args; + va_start(args, format); + SetTo(error, format, args); + va_end(args); + } + + // copy constructor + Exception(const Exception& exception) + : fError(exception.fError), + fDescription(exception.fDescription) + { + } + + // destructor + ~Exception() + { + } + + // SetTo + void SetTo(int error, const char* format, va_list arg) + { + char buffer[2048]; + vsprintf(buffer, format, arg); + fError = error; + fDescription = buffer; + } + + // Error + int Error() const + { + return fError; + } + + // Description + const string& Description() const + { + return fDescription; + } + +private: + int fError; + string fDescription; +}; + + +// #pragma mark - ELFObject + + +struct SectionInfo { + uint32_t type; + uint32_t offset; + uint32_t size; + const char* name; +}; + + +class ELFObject { +public: + ELFObject() + : fFD(-1), + fSectionHeaderStrings(NULL), + fSectionHeaderStringsLength(0) + { + } + + ~ELFObject() + { + Unset(); + } + + void Unset() + { + if (fFD >= 0) { + close(fFD); + fFD = -1; + } + + delete[] fSectionHeaderStrings; + fSectionHeaderStrings = NULL; + } + + void SetTo(const char* fileName) + { + Unset(); + + // open the file + fFD = open(fileName, O_RDWR); + if (fFD < 0) + throw Exception("Failed to open \"%s\"", fileName); + + // get the file size + fFileSize = FileSize(); + if (fFileSize < 0) + throw Exception("Failed to get the file size."); + + // read ELF header + Elf32_Ehdr fileHeader; + Read(0, &fileHeader, sizeof(Elf32_Ehdr), "Failed to read ELF header."); + + // check data encoding (endianess) + switch (fileHeader.e_ident[EI_DATA]) { + case ELFDATA2LSB: + fHostEndianess = (htonl(1) != 1); + break; + case ELFDATA2MSB: + fHostEndianess = (htonl(1) == 1); + break; + default: + case ELFDATANONE: + throw Exception(EIO, "Unsupported ELF data encoding."); + break; + } + + // get the header values + fELFHeaderSize = GetUInt16(fileHeader.e_ehsize); + fSectionHeaderTableOffset = GetUInt32(fileHeader.e_shoff); + fSectionHeaderSize = GetUInt16(fileHeader.e_shentsize); + fSectionHeaderCount = GetUInt16(fileHeader.e_shnum); + bool hasSectionHeaderTable = (fSectionHeaderTableOffset != 0); + + // check the sanity of the header values + // ELF header size + if (fELFHeaderSize < sizeof(Elf32_Ehdr) || fELFHeaderSize > kMaxELFHeaderSize) { + throw Exception(EIO, + "Invalid ELF header: invalid ELF header size: %lu.", + fELFHeaderSize); + } + + // section header table offset and entry count/size + uint32_t sectionHeaderTableSize = 0; + if (hasSectionHeaderTable) { + if (fSectionHeaderTableOffset < fELFHeaderSize + || fSectionHeaderTableOffset > fFileSize) { + throw Exception(EIO, "Invalid ELF header: invalid section " + "header table offset: %lu.", + fSectionHeaderTableOffset); + } + sectionHeaderTableSize = fSectionHeaderSize * fSectionHeaderCount; + if (fSectionHeaderSize < sizeof(Elf32_Shdr) + || fSectionHeaderTableOffset + sectionHeaderTableSize + > fFileSize) { + throw Exception(EIO, "Invalid ELF header: section header " + "table exceeds file: %lu.", + fSectionHeaderTableOffset + + sectionHeaderTableSize); + } + + + // load section header string section + uint16_t sectionHeaderStringSectionIndex + = GetUInt16(fileHeader.e_shstrndx); + if (sectionHeaderStringSectionIndex != SHN_UNDEF) { + if (sectionHeaderStringSectionIndex >= fSectionHeaderCount) { + throw Exception(EIO, "Invalid ELF header: invalid section " + "header string section index: %lu.", + sectionHeaderStringSectionIndex); + } + + // get the section info + SectionInfo info; + if (_ReadSectionHeader(sectionHeaderStringSectionIndex, + info)) { + fSectionHeaderStrings = new char[info.size + 1]; + Read(info.offset, fSectionHeaderStrings, info.size, + "Failed to read section header string section."); + fSectionHeaderStringsLength = info.size; + // null-terminate to be on the safe side + fSectionHeaderStrings[info.size] = '\0'; + } + } + } + } + + bool FindSectionByName(const char* name, SectionInfo& foundInfo) + { + // can't find the section by name without section names + if (!fSectionHeaderStrings) + return false; + + // iterate through the section headers + for (int32_t i = 0; i < (int32_t)fSectionHeaderCount; i++) { + SectionInfo info; + if (_ReadSectionHeader(i, info)) { +//printf("section %3d: offset: %7d, size: %7d, name: %s\n", i, info.offset, info.size, info.name); + if (strcmp(info.name, name) == 0) { + foundInfo = info; + return true; + } + } + } + + return false; + } + + void Read(off_t position, void* buffer, size_t size, + const char *errorMessage = NULL) + { + if (lseek(fFD, position, SEEK_SET) < 0) + throw Exception(errorMessage); + + ssize_t bytesRead = read(fFD, buffer, size); + if (bytesRead < 0) + throw Exception(errorMessage); + + if ((size_t)bytesRead != size) { + if (errorMessage) { + throw Exception("%s Read too few bytes (%d/%d).", + errorMessage, (int)bytesRead, (int)size); + } else { + throw Exception("Read too few bytes (%ld/%lu).", + (int)bytesRead, (int)size); + } + } + } + + void Write(off_t position, const void* buffer, size_t size, + const char *errorMessage = NULL) + { + if (lseek(fFD, position, SEEK_SET) < 0) + throw Exception(errorMessage); + + ssize_t bytesWritten = write(fFD, buffer, size); + if (bytesWritten < 0) + throw Exception(errorMessage); + + if ((size_t)bytesWritten != size) { + if (errorMessage) { + throw Exception("%s Wrote too few bytes (%d/%d).", + errorMessage, (int)bytesWritten, (int)size); + } else { + throw Exception("Wrote too few bytes (%ld/%lu).", + (int)bytesWritten, (int)size); + } + } + } + + off_t FileSize() + { + off_t currentPos = lseek(fFD, 0, SEEK_END); + if (currentPos < 0) + return -1; + + return lseek(fFD, currentPos, SEEK_SET); + } + + // GetInt16 + int16_t GetInt16(int16_t value) + { + return (fHostEndianess ? value : _SwapUInt16(value)); + } + + // GetUInt16 + uint16_t GetUInt16(uint16_t value) + { + return (fHostEndianess ? value : _SwapUInt16(value)); + } + + // GetInt32 + int32_t GetInt32(int32_t value) + { + return (fHostEndianess ? value : _SwapUInt32(value)); + } + + // GetUInt32 + uint32_t GetUInt32(uint32_t value) + { + return (fHostEndianess ? value : _SwapUInt32(value)); + } + +private: + bool _ReadSectionHeader(int index, SectionInfo& info) + { + uint32_t shOffset = fSectionHeaderTableOffset + + index * fSectionHeaderSize; + Elf32_Shdr sectionHeader; + Read(shOffset, §ionHeader, sizeof(Elf32_Shdr), + "Failed to read ELF section header."); + + // get the header values + uint32_t type = GetUInt32(sectionHeader.sh_type); + uint32_t offset = GetUInt32(sectionHeader.sh_offset); + uint32_t size = GetUInt32(sectionHeader.sh_size); + uint32_t nameIndex = GetUInt32(sectionHeader.sh_name); + + // check the values + // SHT_NULL marks the header unused, + if (type == SHT_NULL) + return false; + + // SHT_NOBITS sections take no space in the file + if (type != SHT_NOBITS) { + if (offset < fELFHeaderSize || offset > fFileSize) { + throw Exception(EIO, "Invalid ELF section header: " + "invalid section offset: %lu.", offset); + } + uint32_t sectionEnd = offset + size; + if (sectionEnd > fFileSize) { + throw Exception(EIO, "Invalid ELF section header: " + "section exceeds file: %lu.", sectionEnd); + } + } + + // get name, if we have a string section + if (fSectionHeaderStrings) { + if (nameIndex >= (uint32_t)fSectionHeaderStringsLength) { + throw Exception(EIO, "Invalid ELF section header: " + "invalid name index: %lu.", nameIndex); + } + info.name = fSectionHeaderStrings + nameIndex; + } else { + info.name = ""; + } + + info.type = type; + info.offset = offset; + info.size = size; + + + return true; + } + + // _SwapUInt16 + static inline uint16_t _SwapUInt16(uint16_t value) + { + return ((value & 0xff) << 8) | (value >> 8); + } + + // _SwapUInt32 + static inline uint32_t _SwapUInt32(uint32_t value) + { + return ((uint32_t)_SwapUInt16(value & 0xffff) << 16) + | _SwapUInt16(uint16_t(value >> 16)); + } + +private: + int fFD; + bool fHostEndianess; + off_t fFileSize; + uint32_t fELFHeaderSize; + uint32_t fSectionHeaderTableOffset; + uint32_t fSectionHeaderSize; + uint32_t fSectionHeaderCount; + char* fSectionHeaderStrings; + int fSectionHeaderStringsLength; +}; + + +// main +int +main(int argc, const char* const* argv) +{ + sArgc = argc; + sArgv = argv; + + if (argc < 3) + print_usage_and_exit(true); + + // parameters + const char* fileName = argv[1]; + const char* revisionString = argv[2]; + uint32_t revision = atol(revisionString); + + try { + ELFObject elfObject; + elfObject.SetTo(fileName); + + // find haiku revision section + SectionInfo info; + if (!elfObject.FindSectionByName("_haiku_revision", info)) { + fprintf(stderr, "haiku revision section not found\n"); + exit(1); + } + + // convert revision number to object endianess and write to section + uint32_t revisionBuffer = elfObject.GetUInt32(revision); + elfObject.Write(info.offset, &revisionBuffer, sizeof(revisionBuffer), + "Failed to write revision."); + + } catch (Exception exception) { + if (exception.Description() == "") { + fprintf(stderr, "%s\n", strerror(exception.Error())); + } else { + fprintf(stderr, "%s: %s\n", exception.Description().c_str(), + strerror(exception.Error())); + } + exit(1); + } + + return 0; +}