bb9aef549a995777fb29cefd77ad76e8d440c733
[oota-llvm.git] / tools / llvm-ar / llvm-ar.cpp
1 //===-- tools/llvm-ar/llvm-ar.cpp - LLVM archive librarian utility --------===//
2 //
3 // Builds up standard unix archive files (.a) containing LLVM bytecode.
4 //
5 //===----------------------------------------------------------------------===//
6
7 #include "Support/CommandLine.h"
8 #include "llvm/Bytecode/Reader.h"
9 #include "llvm/Module.h"
10 #include <string>
11 #include <iostream>
12 #include <fstream>
13 #include <vector>
14 #include <sys/stat.h>
15 #include <cstdio>
16 #include <sys/types.h> 
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <sys/mman.h>
20
21 using std::string;
22 using std::vector;
23 using std::cout;
24
25
26 #define  ARFMAG    "\n"      /* header trailer string */ 
27 #define  ARMAG   "!<arch>\n"  /* magic string */ 
28 #define  SARMAG  8            /* length of magic string */ 
29
30 namespace {
31
32   // Each file member is preceded by a file member header. Which is
33   // of the following format:
34   //
35   // char ar_name[16]  - '/' terminated file member name. 
36   //                     If the file name does not fit, a dummy name is used.
37   // char ar_date[12]  - file date in decimal
38   // char ar_uid[6]    - User id of file owner in decimal.
39   // char ar_gid[6]    - Group ID file belongs to in decimal.
40   // char ar_mode[8]   - File mode in octal.
41   // char ar_size[10]  - Size of file in decimal.
42   // char ar_fmag[2]   - Trailer of header file, a newline.
43   struct ar_hdr {
44     char name[16];
45     char date[12];
46     char uid[6];
47     char gid[6];
48     char mode[8];
49     char size[10];
50     char fmag[2]; 
51     void init() {
52       memset(name,' ',16);
53       memset(date,' ',12);
54       memset(uid,' ',6);
55       memset(gid,' ',6);
56       memset(mode,' ',8);
57       memset(size,' ',10);
58       memset(fmag,' ',2);
59     }
60   };
61 }
62
63 //Option to generate symbol table or not
64 //running llvm-ar -s is the same as ranlib
65 cl::opt<bool> SymbolTable ("s", cl::desc("Generate an archive symbol table"));
66
67 //Archive name
68 cl::opt<string> Archive (cl::Positional, cl::desc("<archive file>"), 
69                          cl::Required);
70
71 //For now we require one or more member files, this should change so
72 //we can just run llvm-ar -s on an archive to generate the symbol
73 //table
74 cl::list<string> Members(cl::ConsumeAfter, cl::desc("<archive members>..."));
75
76
77 static inline bool Error(std::string *ErrorStr, const char *Message) {
78   if (ErrorStr) *ErrorStr = Message;
79   return true;
80 }
81
82
83 // WriteSymbolTable - Writes symbol table to ArchiveFile, return false
84 // on errors. Also returns by reference size of symbol table.
85 //
86 // Overview of method:
87 // 1) Generate the header for the symbol table. This is a normal
88 //    archive member header, but it has a zero length name.
89 // 2) For each archive member file, stat the file and parse the bytecode
90 //    Store cumulative offset (file size + header size).
91 // 3) Loop over all the symbols for the current member file, 
92 //    add offset entry to offset vector, and add symbol name to its vector.
93 //    Note: The symbol name vector is a vector of chars to speed up calculating
94 //    the total size of the symbol table.
95 // 4) Update offset vector once we know the total size of symbol table. This is
96 //    because the symbol table appears before all archive member file contents.
97 //    We add the size of magic string, and size of symbol table to each offset.
98 // 5) If the new updated offset it not even, we add 1 byte to offset because
99 //    a newline will be inserted when writing member files. This adjustment is
100 //    cummulative (ie. each time we have an odd offset we add 1 to total adjustment).
101 // 6) Lastly, write symbol table to file.
102 //
103 bool WriteSymbolTable(std::ofstream &ArchiveFile) {
104  
105   //Create header for symbol table. This is essentially an empty header with the
106   //name set to a '/' to indicate its a symbol table.
107   ar_hdr Hdr;
108   Hdr.init();
109
110   //Name of symbol table is '/'
111   Hdr.name[0] = '/';
112   Hdr.name[1] = '\0';
113   
114   //Set the header trailer to a newline
115   memcpy(Hdr.fmag,ARFMAG,sizeof(ARFMAG));
116
117   
118   //Write header to archive file
119   ArchiveFile.write((char*)&Hdr, sizeof(Hdr));
120   
121
122   unsigned memoff = 0;  //Keep Track of total size of files added to archive
123   vector<unsigned> offsets; //Vector of offsets into archive file
124   vector<char> names; //Vector of characters that are the symbol names. 
125
126   //Loop over archive member files, parse bytecode, and generate symbol table.
127   for(unsigned i=0; i<Members.size(); ++i) { 
128     
129     //Open Member file for reading and copy to buffer
130     int FD = open(Members[i].c_str(),O_RDONLY);
131     
132     //Check for errors opening the file.
133     if (FD == -1) {
134       std::cerr << "Error opening file!\n";
135       return false;
136     }
137
138     //Stat the file to get its size.
139     struct stat StatBuf;
140     if (stat(Members[i].c_str(), &StatBuf) == -1 || StatBuf.st_size == 0) {
141       std::cerr << "Error stating file\n";
142       return false;
143     }
144
145     //Size of file
146     unsigned Length = StatBuf.st_size;
147     
148     //Read in file into a buffer.
149     unsigned char *buf = (unsigned char*)mmap(0, Length,PROT_READ,
150                                               MAP_PRIVATE, FD, 0);
151   
152     //Check if mmap failed.
153     if (buf == (unsigned char*)MAP_FAILED) {
154       std::cerr << "Error mmapping file!\n";
155       return false;
156     }
157     
158     //Parse the bytecode file and get all the symbols.
159     string ErrorStr;
160     Module *M = ParseBytecodeBuffer(buf,Length,Members[i],&ErrorStr);
161     
162     //Check for errors parsing bytecode.
163     //if(ErrorStr) {
164     //std::cerr << "Error Parsing Bytecode\n";
165     //return false;
166     //}
167
168     //Loop over function names and global vars and add to symbol maps
169     for(Module::iterator I = M->begin(), E=M->end(); I != E; ++I) {
170       
171       //get function name
172       string NM = ((Function*)I)->getName();
173             
174       //Loop over the characters in the name and add to symbol name vector
175       for(unsigned i=0; i<NM.size(); ++i)
176         names.push_back(NM[i]);
177
178       //Each symbol is null terminated.
179       names.push_back('\0');
180
181       //Add offset to vector of offsets
182       offsets.push_back(memoff);
183     }
184
185     memoff += Length + sizeof(Hdr);
186   }
187
188   //Determine how large our symbol table is.
189   unsigned symbolTableSize = sizeof(Hdr) + 4 + 4*(offsets.size()) + names.size();
190   cout << "Symbol Table Size: " << symbolTableSize << "\n";
191
192   //Number of symbols should be in network byte order as well
193   char num[4];
194   unsigned temp = offsets.size();
195   num[0] = (temp >> 24) & 255;
196   num[1] = (temp >> 16) & 255;
197   num[2] = (temp >> 8) & 255;
198   num[3] = temp & 255;
199
200   //Write number of symbols to archive file
201   ArchiveFile.write(num,4);
202
203   //Adjustment to offset to start files on even byte boundaries
204   unsigned adjust = 0;
205   
206   //Update offsets write symbol table to archive.
207   for(unsigned i=0; i<offsets.size(); ++i) {
208     char output[4];
209     offsets[i] = offsets[i] + symbolTableSize + SARMAG;
210     offsets[i] += adjust;
211     if((offsets[i] % 2 != 0)) {
212       adjust++;
213       offsets[i] += adjust;
214     }
215     
216     cout << "Offset: " << offsets[i] << "\n";
217     output[0] = (offsets[i] >> 24) & 255;
218     output[1] = (offsets[i] >> 16) & 255;
219     output[2] = (offsets[i] >> 8) & 255;
220     output[3] = offsets[i] & 255;
221     ArchiveFile.write(output,4);
222   }
223
224
225   //Write out symbol name vector.
226   for(unsigned i=0; i<names.size(); ++i)
227     ArchiveFile << names[i];
228
229   return true;
230 }
231
232 // AddMemberToArchive - Writes member file to archive. Returns false on errors.
233 // 
234 // Overview of method: 
235 // 1) Open file, and stat it.  
236 // 2) Fill out header using stat information. If name is longer then 15 
237 //    characters, use "dummy" name.
238 // 3) Write header and file contents to disk.
239 // 4) Keep track of total offset into file, and insert a newline if it is odd.
240 //
241 bool AddMemberToArchive(string Member, std::ofstream &ArchiveFile) {
242   
243   ar_hdr Hdr; //Header for archive member file.
244   
245   //stat the file to get info
246   struct stat StatBuf;
247   if (stat(Member.c_str(), &StatBuf) == -1 || StatBuf.st_size == 0)
248     cout << "ERROR\n";
249
250   //fill in header
251   
252   //set name to white spaces
253   memset(Hdr.name,' ', sizeof(Hdr.name));
254
255   //check the size of the name, if less than 15, we can copy it directly
256   //otherwise we give it a dummy name for now
257   if(Member.length() < 16)
258     memcpy(Hdr.name,Member.c_str(),Member.length());
259   else
260     memcpy(Hdr.name, "Dummy", 5);
261
262   //terminate name with forward slash
263   Hdr.name[15] = '/';
264
265   //file member size in decimal
266   unsigned Length = StatBuf.st_size;
267   sprintf(Hdr.size,"%d", Length);
268   cout << "Size: " << Length << "\n";
269
270   //file member user id in decimal
271   sprintf(Hdr.uid, "%d", StatBuf.st_uid);
272
273   //file member group id in decimal
274   sprintf(Hdr.gid, "%d", StatBuf.st_gid);
275
276   //file member date in decimal
277   sprintf(Hdr.date,"%d", (int)StatBuf.st_mtime);
278   
279   //file member mode in OCTAL
280   sprintf(Hdr.mode,"%d", StatBuf.st_mode);
281  
282   //add our header trailer
283   memcpy(Hdr.fmag,ARFMAG,sizeof(ARFMAG));
284
285   //write header to archive file
286   ArchiveFile.write((char*)&Hdr, sizeof(Hdr));
287   
288   //open Member file for reading and copy to buffer
289   int FD = open(Member.c_str(),O_RDONLY);
290   if (FD == -1) {
291     std::cerr << "Error opening file!\n";
292     return false;
293   }
294
295   unsigned char *buf = (unsigned char*)mmap(0, Length,PROT_READ,
296                           MAP_PRIVATE, FD, 0);
297   
298   //check if mmap failed
299   if (buf == (unsigned char*)MAP_FAILED) {
300     std::cerr << "Error mmapping file!\n";
301     return false;
302   }
303
304   //write to archive file
305   ArchiveFile.write((char*)buf,Length);
306     
307   // Unmmap the memberfile
308   munmap((char*)buf, Length);
309
310   return true;
311 }
312
313
314 // CreateArchive - Generates archive with or without symbol table.
315 //
316 void CreateArchive() {
317   
318   //Create archive file for output.
319   std::ofstream ArchiveFile(Archive.c_str());
320   
321   //Check for errors opening or creating archive file.
322   if(!ArchiveFile.is_open() || ArchiveFile.bad() ) {
323     std::cerr << "Error opening Archive File\n";
324     exit(1);
325   }
326
327   //Write magic string to archive.
328   ArchiveFile << ARMAG;
329
330   //If the '-s' option was specified, generate symbol table.
331   if(SymbolTable) {
332     cout << "Symbol Table Start: " << ArchiveFile.tellp() << "\n";
333     if(!WriteSymbolTable(ArchiveFile)) {
334       std::cerr << "Error creating symbol table. Exiting program.";
335       exit(1);
336     }
337     cout << "Symbol Table End: " << ArchiveFile.tellp() << "\n";
338   }
339   //Loop over all member files, and add to the archive.
340   for(unsigned i=0; i<Members.size(); ++i) {
341     if(ArchiveFile.tellp() % 2 != 0)
342       ArchiveFile << ARFMAG;
343
344     cout << "Member File Start: " << ArchiveFile.tellp() << "\n";
345
346     if(AddMemberToArchive(Members[i],ArchiveFile) != true) {
347       std::cerr << "Error adding file to archive. Exiting program.";
348       exit(1);
349     }
350     cout << "Member File End: " << ArchiveFile.tellp() << "\n";
351   }
352   
353   //Close archive file.
354   ArchiveFile.close();
355 }
356
357
358 int main(int argc, char **argv) {
359
360   //Parse Command line options
361   cl::ParseCommandLineOptions(argc, argv, " llvm-ar\n");
362
363   //Create archive!
364   CreateArchive();
365
366   return 0;
367 }