Initial support for reading standard .a files
authorChris Lattner <sabre@nondot.org>
Sat, 19 Apr 2003 21:45:34 +0000 (21:45 +0000)
committerChris Lattner <sabre@nondot.org>
Sat, 19 Apr 2003 21:45:34 +0000 (21:45 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@5820 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Archive/ArchiveReader.cpp [new file with mode: 0644]
lib/Bytecode/Archive/ArchiveReader.cpp [new file with mode: 0644]
lib/Bytecode/Reader/ArchiveReader.cpp [new file with mode: 0644]

diff --git a/lib/Archive/ArchiveReader.cpp b/lib/Archive/ArchiveReader.cpp
new file mode 100644 (file)
index 0000000..b6c3895
--- /dev/null
@@ -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;
+}
diff --git a/lib/Bytecode/Archive/ArchiveReader.cpp b/lib/Bytecode/Archive/ArchiveReader.cpp
new file mode 100644 (file)
index 0000000..b6c3895
--- /dev/null
@@ -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;
+}
diff --git a/lib/Bytecode/Reader/ArchiveReader.cpp b/lib/Bytecode/Reader/ArchiveReader.cpp
new file mode 100644 (file)
index 0000000..b6c3895
--- /dev/null
@@ -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;
+}