blob: c1ec7ecafe20529005bf18c50bf6319c6bf0693b [file] [log] [blame]
Tanya Lattner14baebf2003-08-28 15:22:38 +00001//===----------------------------------------------------------------------===//
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
24using std::string;
25using std::vector;
26using 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
33namespace {
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
68cl::opt<bool> SymbolTable ("s", cl::desc("Generate an archive symbol table"));
69
70//Archive name
71cl::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
77cl::list<string> Members(cl::ConsumeAfter, cl::desc("<archive members>..."));
78
79
80static 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
Misha Brukmancf00c4a2003-10-10 17:57:28 +000093// Store cumulative offset (file size + header size).
Tanya Lattner14baebf2003-08-28 15:22:38 +000094// 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//
106bool 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] = '/';
Tanya Lattnere3df92e2003-09-13 03:18:56 +0000115 Hdr.name[1] = '\0';
Tanya Lattner14baebf2003-08-28 15:22:38 +0000116
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
Misha Brukmancf00c4a2003-10-10 17:57:28 +0000209 //Update offsets write symbol table to archive.
Tanya Lattner14baebf2003-08-28 15:22:38 +0000210 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//
244bool 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
Misha Brukman8d2ff132003-09-23 17:27:02 +0000280 sprintf(Hdr.date,"%d", (int)StatBuf.st_mtime);
Tanya Lattner14baebf2003-08-28 15:22:38 +0000281
282 //file member mode in OCTAL
283 sprintf(Hdr.mode,"%d", StatBuf.st_mode);
Tanya Lattner14baebf2003-08-28 15:22:38 +0000284
285 //add our header trailer
286 memcpy(Hdr.fmag,ARFMAG,sizeof(ARFMAG));
287
288 //write header to archive file
289 ArchiveFile.write((char*)&Hdr, sizeof(Hdr));
290
Tanya Lattner14baebf2003-08-28 15:22:38 +0000291 //open Member file for reading and copy to buffer
292 int FD = open(Member.c_str(),O_RDONLY);
293 if (FD == -1) {
294 std::cerr << "Error opening file!\n";
295 return false;
296 }
297
298 unsigned char *buf = (unsigned char*)mmap(0, Length,PROT_READ,
299 MAP_PRIVATE, FD, 0);
300
301 //check if mmap failed
302 if (buf == (unsigned char*)MAP_FAILED) {
303 std::cerr << "Error mmapping file!\n";
304 return false;
305 }
306
307 //write to archive file
308 ArchiveFile.write((char*)buf,Length);
309
310 // Unmmap the memberfile
311 munmap((char*)buf, Length);
312
313 return true;
314}
315
316
317// CreateArchive - Generates archive with or without symbol table.
318//
319void CreateArchive() {
320
321 //Create archive file for output.
322 std::ofstream ArchiveFile(Archive.c_str());
323
324 //Check for errors opening or creating archive file.
325 if(!ArchiveFile.is_open() || ArchiveFile.bad() ) {
326 std::cerr << "Error opening Archive File\n";
327 exit(1);
328 }
329
330 //Write magic string to archive.
331 ArchiveFile << ARMAG;
332
333 //If the '-s' option was specified, generate symbol table.
334 if(SymbolTable) {
335 cout << "Symbol Table Start: " << ArchiveFile.tellp() << "\n";
336 if(!WriteSymbolTable(ArchiveFile)) {
337 std::cerr << "Error creating symbol table. Exiting program.";
338 exit(1);
339 }
340 cout << "Symbol Table End: " << ArchiveFile.tellp() << "\n";
341 }
342 //Loop over all member files, and add to the archive.
343 for(unsigned i=0; i<Members.size(); ++i) {
344 if(ArchiveFile.tellp() % 2 != 0)
345 ArchiveFile << ARFMAG;
346
347 cout << "Member File Start: " << ArchiveFile.tellp() << "\n";
348
349 if(AddMemberToArchive(Members[i],ArchiveFile) != true) {
350 std::cerr << "Error adding file to archive. Exiting program.";
351 exit(1);
352 }
353 cout << "Member File End: " << ArchiveFile.tellp() << "\n";
354 }
355
356 //Close archive file.
357 ArchiveFile.close();
358}
359
360
361int main(int argc, char **argv) {
362
363 //Parse Command line options
364 cl::ParseCommandLineOptions(argc, argv, " llvm-ar\n");
365
366 //Create archive!
367 CreateArchive();
368
369 return 0;
370}