diff --git a/lib/Archive/ArchiveReader.cpp b/lib/Archive/ArchiveReader.cpp new file mode 100644 index 00000000000..b6c389523f4 --- /dev/null +++ b/lib/Archive/ArchiveReader.cpp @@ -0,0 +1,166 @@ +//===- ReadArchive.cpp - Code to read LLVM bytecode from .a files ---------===// +// +// This file implements the ReadArchiveFile interface, which allows a linker to +// read all of the LLVM bytecode files contained in a .a file. This file +// understands the standard system .a file format. This can only handle the .a +// variant prevelant on linux systems so far, but may be extended. See +// information in this source file for more information: +// http://sources.redhat.com/cgi-bin/cvsweb.cgi/src/bfd/archive.c?cvsroot=src +// +//===----------------------------------------------------------------------===// + +#include "llvm/Bytecode/Reader.h" +#include "llvm/Module.h" +#include +#include +#include + +namespace { + struct ar_hdr { + char name[16]; + char date[12]; + char uid[6]; + char gid[6]; + char mode[8]; + char size[10]; + char fmag[2]; // Always equal to '`\n' + }; + + enum ObjectType { + UserObject, // A user .o/.bc file + Unknown, // Unknown file, just ignore it + SVR4LongFilename, // a "//" section used for long file names + }; +} + + +// getObjectType - Determine the type of object that this header represents. +// This is capable of parsing the variety of special sections used for various +// purposes. +static enum ObjectType getObjectType(ar_hdr *H, unsigned Size) { + // Check for sections with special names... + if (!memcmp(H->name, "// ", 16)) + return SVR4LongFilename; + + // Check to see if it looks like an llvm object file... + if (Size >= 4 && !memcmp(H+1, "llvm", 4)) + return UserObject; + + return Unknown; +} + + +static inline bool Error(std::string *ErrorStr, const char *Message) { + if (ErrorStr) *ErrorStr = Message; + return true; +} + +static bool ParseLongFilenameSection(unsigned char *Buffer, unsigned Size, + std::vector &LongFilenames, + std::string *S) { + if (!LongFilenames.empty()) + return Error(S, "archive file contains multiple long filename entries"); + + while (Size) { + // Long filename entries are newline delimited to keep the archive readable. + unsigned char *Ptr = (unsigned char*)memchr(Buffer, '\n', Size); + if (Ptr == 0) + return Error(S, "archive long filename entry doesn't end with newline!"); + assert(*Ptr == '\n'); + + if (Ptr == Buffer) break; // Last entry contains just a newline. + + unsigned char *End = Ptr; + if (End[-1] == '/') --End; // Remove trailing / from name + + LongFilenames.push_back(std::string(Buffer, End)); + Size -= Ptr-Buffer+1; + Buffer = Ptr+1; + } + + return false; +} + + +static bool ReadArchiveBuffer(unsigned char *Buffer, unsigned Length, + std::vector &Objects, + std::string *ErrorStr) { + if (Length < 8 || memcmp(Buffer, "!\n", 8)) + return Error(ErrorStr, "signature incorrect for an archive file!"); + Buffer += 8; Length -= 8; // Skip the magic string. + + std::vector LongFilenames; + + while (Length >= sizeof(ar_hdr)) { + ar_hdr *Hdr = (ar_hdr*)Buffer; + unsigned Size = atoi(Hdr->size); + if (Size+sizeof(ar_hdr) > Length) + return Error(ErrorStr, "invalid record length in archive file!"); + + switch (getObjectType(Hdr, Size)) { + case SVR4LongFilename: + // If this is a long filename section, read all of the file names into the + // LongFilenames vector. + // + if (ParseLongFilenameSection(Buffer+sizeof(ar_hdr), Size, + LongFilenames, ErrorStr)) + return true; + break; + case UserObject: { + Module *M = ParseBytecodeBuffer(Buffer+sizeof(ar_hdr), Size, ErrorStr); + if (!M) return true; + Objects.push_back(M); + break; + } + case Unknown: + std::cerr << "ReadArchiveBuffer: WARNING: Skipping unknown file: "; + std::cerr << std::string(Hdr->name, Hdr->name+sizeof(Hdr->name+1)) <<"\n"; + break; // Just ignore unknown files. + } + + // Round Size up to an even number... + Size = (Size+1)/2*2; + Buffer += sizeof(ar_hdr)+Size; // Move to the next entry + Length -= sizeof(ar_hdr)+Size; + } + + return Length != 0; +} + + +// ReadArchiveFile - Read bytecode files from the specfied .a file, returning +// true on error, or false on success. This does not support reading files from +// standard input. +// +bool ReadArchiveFile(const std::string &Filename, std::vector &Objects, + std::string *ErrorStr) { + int FD = open(Filename.c_str(), O_RDONLY); + if (FD == -1) + return Error(ErrorStr, "Error opening file!"); + + // Stat the file to get its length... + struct stat StatBuf; + if (fstat(FD, &StatBuf) == -1 || StatBuf.st_size == 0) + return Error(ErrorStr, "Error stat'ing file!"); + + // mmap in the file all at once... + int Length = StatBuf.st_size; + unsigned char *Buffer = (unsigned char*)mmap(0, Length, PROT_READ, + MAP_PRIVATE, FD, 0); + if (Buffer == (unsigned char*)MAP_FAILED) + return Error(ErrorStr, "Error mmapping file!"); + + // Parse the archive files we mmap'ped in + bool Result = ReadArchiveBuffer(Buffer, Length, Objects, ErrorStr); + + // Unmmap the archive... + munmap((char*)Buffer, Length); + + if (Result) // Free any loaded objects + while (!Objects.empty()) { + delete Objects.back(); + Objects.pop_back(); + } + + return Result; +} diff --git a/lib/Bytecode/Archive/ArchiveReader.cpp b/lib/Bytecode/Archive/ArchiveReader.cpp new file mode 100644 index 00000000000..b6c389523f4 --- /dev/null +++ b/lib/Bytecode/Archive/ArchiveReader.cpp @@ -0,0 +1,166 @@ +//===- ReadArchive.cpp - Code to read LLVM bytecode from .a files ---------===// +// +// This file implements the ReadArchiveFile interface, which allows a linker to +// read all of the LLVM bytecode files contained in a .a file. This file +// understands the standard system .a file format. This can only handle the .a +// variant prevelant on linux systems so far, but may be extended. See +// information in this source file for more information: +// http://sources.redhat.com/cgi-bin/cvsweb.cgi/src/bfd/archive.c?cvsroot=src +// +//===----------------------------------------------------------------------===// + +#include "llvm/Bytecode/Reader.h" +#include "llvm/Module.h" +#include +#include +#include + +namespace { + struct ar_hdr { + char name[16]; + char date[12]; + char uid[6]; + char gid[6]; + char mode[8]; + char size[10]; + char fmag[2]; // Always equal to '`\n' + }; + + enum ObjectType { + UserObject, // A user .o/.bc file + Unknown, // Unknown file, just ignore it + SVR4LongFilename, // a "//" section used for long file names + }; +} + + +// getObjectType - Determine the type of object that this header represents. +// This is capable of parsing the variety of special sections used for various +// purposes. +static enum ObjectType getObjectType(ar_hdr *H, unsigned Size) { + // Check for sections with special names... + if (!memcmp(H->name, "// ", 16)) + return SVR4LongFilename; + + // Check to see if it looks like an llvm object file... + if (Size >= 4 && !memcmp(H+1, "llvm", 4)) + return UserObject; + + return Unknown; +} + + +static inline bool Error(std::string *ErrorStr, const char *Message) { + if (ErrorStr) *ErrorStr = Message; + return true; +} + +static bool ParseLongFilenameSection(unsigned char *Buffer, unsigned Size, + std::vector &LongFilenames, + std::string *S) { + if (!LongFilenames.empty()) + return Error(S, "archive file contains multiple long filename entries"); + + while (Size) { + // Long filename entries are newline delimited to keep the archive readable. + unsigned char *Ptr = (unsigned char*)memchr(Buffer, '\n', Size); + if (Ptr == 0) + return Error(S, "archive long filename entry doesn't end with newline!"); + assert(*Ptr == '\n'); + + if (Ptr == Buffer) break; // Last entry contains just a newline. + + unsigned char *End = Ptr; + if (End[-1] == '/') --End; // Remove trailing / from name + + LongFilenames.push_back(std::string(Buffer, End)); + Size -= Ptr-Buffer+1; + Buffer = Ptr+1; + } + + return false; +} + + +static bool ReadArchiveBuffer(unsigned char *Buffer, unsigned Length, + std::vector &Objects, + std::string *ErrorStr) { + if (Length < 8 || memcmp(Buffer, "!\n", 8)) + return Error(ErrorStr, "signature incorrect for an archive file!"); + Buffer += 8; Length -= 8; // Skip the magic string. + + std::vector LongFilenames; + + while (Length >= sizeof(ar_hdr)) { + ar_hdr *Hdr = (ar_hdr*)Buffer; + unsigned Size = atoi(Hdr->size); + if (Size+sizeof(ar_hdr) > Length) + return Error(ErrorStr, "invalid record length in archive file!"); + + switch (getObjectType(Hdr, Size)) { + case SVR4LongFilename: + // If this is a long filename section, read all of the file names into the + // LongFilenames vector. + // + if (ParseLongFilenameSection(Buffer+sizeof(ar_hdr), Size, + LongFilenames, ErrorStr)) + return true; + break; + case UserObject: { + Module *M = ParseBytecodeBuffer(Buffer+sizeof(ar_hdr), Size, ErrorStr); + if (!M) return true; + Objects.push_back(M); + break; + } + case Unknown: + std::cerr << "ReadArchiveBuffer: WARNING: Skipping unknown file: "; + std::cerr << std::string(Hdr->name, Hdr->name+sizeof(Hdr->name+1)) <<"\n"; + break; // Just ignore unknown files. + } + + // Round Size up to an even number... + Size = (Size+1)/2*2; + Buffer += sizeof(ar_hdr)+Size; // Move to the next entry + Length -= sizeof(ar_hdr)+Size; + } + + return Length != 0; +} + + +// ReadArchiveFile - Read bytecode files from the specfied .a file, returning +// true on error, or false on success. This does not support reading files from +// standard input. +// +bool ReadArchiveFile(const std::string &Filename, std::vector &Objects, + std::string *ErrorStr) { + int FD = open(Filename.c_str(), O_RDONLY); + if (FD == -1) + return Error(ErrorStr, "Error opening file!"); + + // Stat the file to get its length... + struct stat StatBuf; + if (fstat(FD, &StatBuf) == -1 || StatBuf.st_size == 0) + return Error(ErrorStr, "Error stat'ing file!"); + + // mmap in the file all at once... + int Length = StatBuf.st_size; + unsigned char *Buffer = (unsigned char*)mmap(0, Length, PROT_READ, + MAP_PRIVATE, FD, 0); + if (Buffer == (unsigned char*)MAP_FAILED) + return Error(ErrorStr, "Error mmapping file!"); + + // Parse the archive files we mmap'ped in + bool Result = ReadArchiveBuffer(Buffer, Length, Objects, ErrorStr); + + // Unmmap the archive... + munmap((char*)Buffer, Length); + + if (Result) // Free any loaded objects + while (!Objects.empty()) { + delete Objects.back(); + Objects.pop_back(); + } + + return Result; +} diff --git a/lib/Bytecode/Reader/ArchiveReader.cpp b/lib/Bytecode/Reader/ArchiveReader.cpp new file mode 100644 index 00000000000..b6c389523f4 --- /dev/null +++ b/lib/Bytecode/Reader/ArchiveReader.cpp @@ -0,0 +1,166 @@ +//===- ReadArchive.cpp - Code to read LLVM bytecode from .a files ---------===// +// +// This file implements the ReadArchiveFile interface, which allows a linker to +// read all of the LLVM bytecode files contained in a .a file. This file +// understands the standard system .a file format. This can only handle the .a +// variant prevelant on linux systems so far, but may be extended. See +// information in this source file for more information: +// http://sources.redhat.com/cgi-bin/cvsweb.cgi/src/bfd/archive.c?cvsroot=src +// +//===----------------------------------------------------------------------===// + +#include "llvm/Bytecode/Reader.h" +#include "llvm/Module.h" +#include +#include +#include + +namespace { + struct ar_hdr { + char name[16]; + char date[12]; + char uid[6]; + char gid[6]; + char mode[8]; + char size[10]; + char fmag[2]; // Always equal to '`\n' + }; + + enum ObjectType { + UserObject, // A user .o/.bc file + Unknown, // Unknown file, just ignore it + SVR4LongFilename, // a "//" section used for long file names + }; +} + + +// getObjectType - Determine the type of object that this header represents. +// This is capable of parsing the variety of special sections used for various +// purposes. +static enum ObjectType getObjectType(ar_hdr *H, unsigned Size) { + // Check for sections with special names... + if (!memcmp(H->name, "// ", 16)) + return SVR4LongFilename; + + // Check to see if it looks like an llvm object file... + if (Size >= 4 && !memcmp(H+1, "llvm", 4)) + return UserObject; + + return Unknown; +} + + +static inline bool Error(std::string *ErrorStr, const char *Message) { + if (ErrorStr) *ErrorStr = Message; + return true; +} + +static bool ParseLongFilenameSection(unsigned char *Buffer, unsigned Size, + std::vector &LongFilenames, + std::string *S) { + if (!LongFilenames.empty()) + return Error(S, "archive file contains multiple long filename entries"); + + while (Size) { + // Long filename entries are newline delimited to keep the archive readable. + unsigned char *Ptr = (unsigned char*)memchr(Buffer, '\n', Size); + if (Ptr == 0) + return Error(S, "archive long filename entry doesn't end with newline!"); + assert(*Ptr == '\n'); + + if (Ptr == Buffer) break; // Last entry contains just a newline. + + unsigned char *End = Ptr; + if (End[-1] == '/') --End; // Remove trailing / from name + + LongFilenames.push_back(std::string(Buffer, End)); + Size -= Ptr-Buffer+1; + Buffer = Ptr+1; + } + + return false; +} + + +static bool ReadArchiveBuffer(unsigned char *Buffer, unsigned Length, + std::vector &Objects, + std::string *ErrorStr) { + if (Length < 8 || memcmp(Buffer, "!\n", 8)) + return Error(ErrorStr, "signature incorrect for an archive file!"); + Buffer += 8; Length -= 8; // Skip the magic string. + + std::vector LongFilenames; + + while (Length >= sizeof(ar_hdr)) { + ar_hdr *Hdr = (ar_hdr*)Buffer; + unsigned Size = atoi(Hdr->size); + if (Size+sizeof(ar_hdr) > Length) + return Error(ErrorStr, "invalid record length in archive file!"); + + switch (getObjectType(Hdr, Size)) { + case SVR4LongFilename: + // If this is a long filename section, read all of the file names into the + // LongFilenames vector. + // + if (ParseLongFilenameSection(Buffer+sizeof(ar_hdr), Size, + LongFilenames, ErrorStr)) + return true; + break; + case UserObject: { + Module *M = ParseBytecodeBuffer(Buffer+sizeof(ar_hdr), Size, ErrorStr); + if (!M) return true; + Objects.push_back(M); + break; + } + case Unknown: + std::cerr << "ReadArchiveBuffer: WARNING: Skipping unknown file: "; + std::cerr << std::string(Hdr->name, Hdr->name+sizeof(Hdr->name+1)) <<"\n"; + break; // Just ignore unknown files. + } + + // Round Size up to an even number... + Size = (Size+1)/2*2; + Buffer += sizeof(ar_hdr)+Size; // Move to the next entry + Length -= sizeof(ar_hdr)+Size; + } + + return Length != 0; +} + + +// ReadArchiveFile - Read bytecode files from the specfied .a file, returning +// true on error, or false on success. This does not support reading files from +// standard input. +// +bool ReadArchiveFile(const std::string &Filename, std::vector &Objects, + std::string *ErrorStr) { + int FD = open(Filename.c_str(), O_RDONLY); + if (FD == -1) + return Error(ErrorStr, "Error opening file!"); + + // Stat the file to get its length... + struct stat StatBuf; + if (fstat(FD, &StatBuf) == -1 || StatBuf.st_size == 0) + return Error(ErrorStr, "Error stat'ing file!"); + + // mmap in the file all at once... + int Length = StatBuf.st_size; + unsigned char *Buffer = (unsigned char*)mmap(0, Length, PROT_READ, + MAP_PRIVATE, FD, 0); + if (Buffer == (unsigned char*)MAP_FAILED) + return Error(ErrorStr, "Error mmapping file!"); + + // Parse the archive files we mmap'ped in + bool Result = ReadArchiveBuffer(Buffer, Length, Objects, ErrorStr); + + // Unmmap the archive... + munmap((char*)Buffer, Length); + + if (Result) // Free any loaded objects + while (!Objects.empty()) { + delete Objects.back(); + Objects.pop_back(); + } + + return Result; +}