diff options
author | Chris Lattner <sabre@nondot.org> | 2003-04-19 21:45:34 +0000 |
---|---|---|
committer | Chris Lattner <sabre@nondot.org> | 2003-04-19 21:45:34 +0000 |
commit | 968cfd0b6e5aa4eac98c748fafd145889b4c7b83 (patch) | |
tree | e833df581ab710ce989f67614b79a219849a4102 /lib/Archive/ArchiveReader.cpp | |
parent | f6099df1948d7a12cae2c29a51e5cae8f4f3e8b7 (diff) |
Initial support for reading standard .a files
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@5820 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Archive/ArchiveReader.cpp')
-rw-r--r-- | lib/Archive/ArchiveReader.cpp | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/lib/Archive/ArchiveReader.cpp b/lib/Archive/ArchiveReader.cpp new file mode 100644 index 0000000000..b6c389523f --- /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 <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> + +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<std::string> &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<Module*> &Objects, + std::string *ErrorStr) { + if (Length < 8 || memcmp(Buffer, "!<arch>\n", 8)) + return Error(ErrorStr, "signature incorrect for an archive file!"); + Buffer += 8; Length -= 8; // Skip the magic string. + + std::vector<std::string> 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<Module*> &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; +} |