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