blob: bb9aef549a995777fb29cefd77ad76e8d440c733 [file] [log] [blame]
Brian Gaeke4fa9fd32003-10-10 18:47:08 +00001//===-- tools/llvm-ar/llvm-ar.cpp - LLVM archive librarian utility --------===//
Tanya Lattner14baebf2003-08-28 15:22:38 +00002//
Brian Gaeke4fa9fd32003-10-10 18:47:08 +00003// Builds up standard unix archive files (.a) containing LLVM bytecode.
Tanya Lattner14baebf2003-08-28 15:22:38 +00004//
5//===----------------------------------------------------------------------===//
Brian Gaeke4fa9fd32003-10-10 18:47:08 +00006
Tanya Lattner14baebf2003-08-28 15:22:38 +00007#include "Support/CommandLine.h"
8#include "llvm/Bytecode/Reader.h"
9#include "llvm/Module.h"
Tanya Lattner14baebf2003-08-28 15:22:38 +000010#include <string>
11#include <iostream>
12#include <fstream>
13#include <vector>
14#include <sys/stat.h>
Brian Gaeke4fa9fd32003-10-10 18:47:08 +000015#include <cstdio>
Tanya Lattner14baebf2003-08-28 15:22:38 +000016#include <sys/types.h>
17#include <fcntl.h>
18#include <unistd.h>
19#include <sys/mman.h>
20
Tanya Lattner14baebf2003-08-28 15:22:38 +000021using std::string;
22using std::vector;
23using 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
30namespace {
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
65cl::opt<bool> SymbolTable ("s", cl::desc("Generate an archive symbol table"));
66
67//Archive name
68cl::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
74cl::list<string> Members(cl::ConsumeAfter, cl::desc("<archive members>..."));
75
76
77static 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
Misha Brukmancf00c4a2003-10-10 17:57:28 +000090// Store cumulative offset (file size + header size).
Tanya Lattner14baebf2003-08-28 15:22:38 +000091// 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//
103bool 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] = '/';
Tanya Lattnere3df92e2003-09-13 03:18:56 +0000112 Hdr.name[1] = '\0';
Tanya Lattner14baebf2003-08-28 15:22:38 +0000113
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
Misha Brukmancf00c4a2003-10-10 17:57:28 +0000206 //Update offsets write symbol table to archive.
Tanya Lattner14baebf2003-08-28 15:22:38 +0000207 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//
241bool 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
Misha Brukman8d2ff132003-09-23 17:27:02 +0000277 sprintf(Hdr.date,"%d", (int)StatBuf.st_mtime);
Tanya Lattner14baebf2003-08-28 15:22:38 +0000278
279 //file member mode in OCTAL
280 sprintf(Hdr.mode,"%d", StatBuf.st_mode);
Tanya Lattner14baebf2003-08-28 15:22:38 +0000281
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
Tanya Lattner14baebf2003-08-28 15:22:38 +0000288 //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//
316void 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
358int 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}