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