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