1 |
60 |
Agner |
/**************************** library.cpp **********************************
|
2 |
|
|
* Author: Agner Fog
|
3 |
|
|
* date created: 2017-11-08
|
4 |
|
|
* Last modified: 2018-03-30
|
5 |
|
|
* Version: 1.10
|
6 |
|
|
* Project: Binary tools for ForwardCom instruction set
|
7 |
|
|
* Description:
|
8 |
|
|
* This module contains code for reading, writing and manipulating function
|
9 |
|
|
* libraries (archives) of the UNIX type
|
10 |
|
|
*
|
11 |
|
|
* Copyright 2017-2020 GNU General Public License http://www.gnu.org/licenses
|
12 |
|
|
*****************************************************************************/
|
13 |
|
|
|
14 |
|
|
#include "stdafx.h"
|
15 |
|
|
|
16 |
|
|
CLibrary::CLibrary() {
|
17 |
|
|
// Constructor
|
18 |
|
|
longNames = 0;
|
19 |
|
|
alignBy = 8;
|
20 |
|
|
}
|
21 |
|
|
|
22 |
|
|
void CLibrary::go() {
|
23 |
|
|
// Do to library whatever the command line says
|
24 |
|
|
|
25 |
|
|
// check libraryOptions and whether an output file is specified
|
26 |
|
|
if (cmd.fileOptions & CMDL_FILE_OUTPUT) {
|
27 |
|
|
// Output is a library file
|
28 |
|
|
if ((cmd.outputFile == 0) && !(cmd.libraryOptions & CMDL_LIBRARY_ADDMEMBER)) {
|
29 |
|
|
err.submit(2503); // Output file name missing
|
30 |
|
|
return;
|
31 |
|
|
}
|
32 |
|
|
// Check extension
|
33 |
|
|
const char * outputFile = cmd.getFilename(cmd.outputFile);
|
34 |
|
|
uint32_t len = (uint32_t)strlen(outputFile);
|
35 |
|
|
if (len < 4 || strncasecmp_(outputFile + len - 3, ".li", 3) != 0) {
|
36 |
|
|
err.submit(1101, outputFile); // Warning wrong extension
|
37 |
|
|
}
|
38 |
|
|
}
|
39 |
|
|
if (err.number()) return;
|
40 |
|
|
|
41 |
|
|
// strip path from member names and search for duplicates
|
42 |
|
|
checkActionList();
|
43 |
|
|
if (err.number()) return;
|
44 |
|
|
|
45 |
|
|
if (dataSize()) {
|
46 |
|
|
// library exists
|
47 |
|
|
// check file type
|
48 |
|
|
if (getFileType() != FILETYPE_LIBRARY) {
|
49 |
|
|
err.submit(ERR_LIBRARY_FILE_TYPE, getFileFormatName(getFileType()));
|
50 |
|
|
return;
|
51 |
|
|
}
|
52 |
|
|
// make list of member names and offsets
|
53 |
|
|
makeMemberList();
|
54 |
|
|
if (err.number()) return;
|
55 |
|
|
}
|
56 |
|
|
|
57 |
|
|
if (cmd.libraryOptions == CMDL_LIBRARY_LISTMEMBERS) {
|
58 |
|
|
// list members. do nothing else
|
59 |
|
|
listMembers();
|
60 |
|
|
return;
|
61 |
|
|
}
|
62 |
|
|
|
63 |
|
|
// do all commands
|
64 |
|
|
runActionList();
|
65 |
|
|
if (err.number()) return;
|
66 |
|
|
|
67 |
|
|
// collect contents of new library
|
68 |
|
|
generateNewLibraryBody();
|
69 |
|
|
if (err.number()) return;
|
70 |
|
|
|
71 |
|
|
// Make library header, symbol table, longnames record, data
|
72 |
|
|
makeBinaryFile();
|
73 |
|
|
if (err.number()) return;
|
74 |
|
|
|
75 |
|
|
if (cmd.fileOptions & CMDL_FILE_OUTPUT) {
|
76 |
|
|
// Write output file
|
77 |
|
|
const char * outputFileName = cmd.getFilename(cmd.outputFile);
|
78 |
|
|
outFile.write(outputFileName);
|
79 |
|
|
}
|
80 |
|
|
}
|
81 |
|
|
|
82 |
|
|
// Check action list for errors
|
83 |
|
|
void CLibrary::checkActionList() {
|
84 |
|
|
uint32_t numCommands = cmd.lcommands.numEntries(); // number of commands on command line
|
85 |
|
|
uint32_t i, j; // loop counters
|
86 |
|
|
const char * fname; // name of object file
|
87 |
|
|
for (i = 0; i < numCommands; i++) { // loop through commands
|
88 |
|
|
if (cmd.lcommands[i].filename) {
|
89 |
|
|
fname = cmd.getFilename(cmd.lcommands[i].filename);
|
90 |
|
|
// strip path from name
|
91 |
|
|
const char * fname2 = removePath(fname);
|
92 |
|
|
if (fname2 != fname) {
|
93 |
|
|
// filename contains path. strip path
|
94 |
|
|
cmd.lcommands[i].value = cmd.fileNameBuffer.pushString(fname2);
|
95 |
|
|
}
|
96 |
|
|
else {
|
97 |
|
|
// filename does not contain path. membername is same as filename
|
98 |
|
|
cmd.lcommands[i].value = cmd.lcommands[i].filename;
|
99 |
|
|
}
|
100 |
|
|
|
101 |
|
|
// search for duplicate names
|
102 |
|
|
for (j = 0; j < i; j++) {
|
103 |
|
|
if (strcmp(fname, cmd.getFilename((uint32_t)cmd.lcommands[j].value)) == 0) {
|
104 |
|
|
// duplicate name found
|
105 |
|
|
if (cmd.lcommands[j].command == CMDL_LIBRARY_DELETEMEMBER && cmd.lcommands[i].command == CMDL_LIBRARY_ADDMEMBER) {
|
106 |
|
|
// delete member before adding new member with same name
|
107 |
|
|
cmd.lcommands[j].command = 0; // ignore delete
|
108 |
|
|
}
|
109 |
|
|
else { // duplicate name not allowed
|
110 |
|
|
err.submit(ERR_DUPLICATE_NAME_COMMANDL, fname); break;
|
111 |
|
|
}
|
112 |
|
|
}
|
113 |
|
|
}
|
114 |
|
|
}
|
115 |
|
|
}
|
116 |
|
|
}
|
117 |
|
|
|
118 |
|
|
// Make list of library member names
|
119 |
|
|
void CLibrary::makeMemberList() {
|
120 |
|
|
// Unix style library. First header at offset 8 = strlen(archiveSignature)
|
121 |
|
|
uint32_t offset = 8;
|
122 |
|
|
SUNIXLibraryHeader * header; // member header
|
123 |
|
|
uint32_t memberSize; // size of member
|
124 |
|
|
const char * memberName = 0; // name of member
|
125 |
|
|
longNames = 0; // no longnames record
|
126 |
|
|
const char * namebuffer = (const char *)cmd.fileNameBuffer.buf(); // filenames buffer
|
127 |
|
|
|
128 |
|
|
// Loop through library headers.
|
129 |
|
|
while (offset + sizeof(SUNIXLibraryHeader) < dataSize()) {
|
130 |
|
|
// Find header
|
131 |
|
|
header = &get<SUNIXLibraryHeader>(offset);
|
132 |
|
|
// Size of member
|
133 |
|
|
memberSize = atoi(header->fileSize);
|
134 |
|
|
if (int32_t(memberSize) < 0 || memberSize + offset + sizeof(SUNIXLibraryHeader) > dataSize()) {
|
135 |
|
|
err.submit(ERR_LIBRARY_FILE_CORRUPT); // Points outside file
|
136 |
|
|
return;
|
137 |
|
|
}
|
138 |
|
|
// member name
|
139 |
|
|
memberName = header->name;
|
140 |
|
|
if (strncmp(memberName, "// ", 3) == 0) {
|
141 |
|
|
// This is the long names member. Remember its position
|
142 |
|
|
longNames = offset + sizeof(SUNIXLibraryHeader);
|
143 |
|
|
longNamesSize = memberSize;
|
144 |
|
|
}
|
145 |
|
|
else if (strncasecmp_(memberName, "/SYMDEF SORTED/", 15) == 0) {
|
146 |
|
|
// This is the symbol list
|
147 |
|
|
//symDef = offset;
|
148 |
|
|
}
|
149 |
|
|
else {
|
150 |
|
|
// Normal member. get name
|
151 |
|
|
if (memberName[0] == '/' && memberName[1] >= '0' && memberName[1] <= '9' && longNames) {
|
152 |
|
|
// name contains index into longNames record
|
153 |
|
|
uint32_t nameIndex = atoi(memberName+1);
|
154 |
|
|
if (nameIndex < longNamesSize) {
|
155 |
|
|
memberName = (char*)buf() + longNames + nameIndex;
|
156 |
|
|
}
|
157 |
|
|
else {
|
158 |
|
|
memberName = "NoName!";
|
159 |
|
|
}
|
160 |
|
|
}
|
161 |
|
|
else {
|
162 |
|
|
// ordinary short name
|
163 |
|
|
// name is terminated by '/'. Replace termination char by 0
|
164 |
|
|
for (int i = 15; i >= 0; i--) {
|
165 |
|
|
if (header->name[i] == '/') {
|
166 |
|
|
header->name[i] = 0; break;
|
167 |
|
|
}
|
168 |
|
|
}
|
169 |
|
|
// Terminate name with max length by overwriting date field, which we are not using
|
170 |
|
|
header->date[0] = 0;
|
171 |
|
|
memberName = header->name;
|
172 |
|
|
}
|
173 |
|
|
// check if name is valid
|
174 |
|
|
if (memberName[0] == 0) memberName = "NoName!";
|
175 |
|
|
|
176 |
|
|
// store member name and offset
|
177 |
|
|
SLibMember libmem;
|
178 |
|
|
libmem.name = cmd.fileNameBuffer.pushString(memberName);
|
179 |
|
|
libmem.oldOffset = offset;
|
180 |
|
|
libmem.newOffset = 0;
|
181 |
|
|
libmem.size = memberSize;
|
182 |
|
|
libmem.action = CMDL_LIBRARY_PRESERVEMEMBER;
|
183 |
|
|
members.push(libmem);
|
184 |
|
|
}
|
185 |
|
|
// get next offset
|
186 |
|
|
offset += sizeof(SUNIXLibraryHeader) + memberSize;
|
187 |
|
|
|
188 |
|
|
// Round up for alignment. Nominal alignment = 8, max alignment = 256
|
189 |
|
|
while ((offset & 0xFF)
|
190 |
|
|
&& offset + sizeof(SUNIXLibraryHeader) < dataSize()
|
191 |
|
|
&& get<uint8_t>(offset) <= ' ') offset++;
|
192 |
|
|
//offset = (offset + alignBy - 1) & ~ alignBy;
|
193 |
|
|
}
|
194 |
|
|
// sort member list alphabetically
|
195 |
|
|
members.sort();
|
196 |
|
|
// check for duplicate names
|
197 |
|
|
for (uint32_t j = 1; j < members.numEntries(); j++) {
|
198 |
|
|
if (strcmp(namebuffer + members[j-1].name, namebuffer + members[j].name) == 0) {
|
199 |
|
|
err.submit(ERR_DUPLICATE_NAME_IN_LIB, namebuffer + members[j].name);
|
200 |
|
|
members[j].action = CMDL_LIBRARY_DELETEMEMBER; // delete duplicate member
|
201 |
|
|
}
|
202 |
|
|
}
|
203 |
|
|
}
|
204 |
|
|
|
205 |
|
|
// Find a module. Return offset
|
206 |
|
|
uint32_t CLibrary::findMember(uint32_t name) {
|
207 |
|
|
// make list of members
|
208 |
|
|
if (members.numEntries() == 0) makeMemberList();
|
209 |
|
|
// make record to search for
|
210 |
|
|
SLibMember memberSearch;
|
211 |
|
|
memberSearch.name = name;
|
212 |
|
|
int32_t i = members.findFirst(memberSearch);
|
213 |
|
|
if (i < 0) return 0; // not found
|
214 |
|
|
return members[i].oldOffset; // return offset
|
215 |
|
|
}
|
216 |
|
|
|
217 |
|
|
|
218 |
|
|
// Run through commands from command line
|
219 |
|
|
void CLibrary::runActionList() {
|
220 |
|
|
uint32_t i; // loop counter
|
221 |
|
|
if (cmd.verbose) {
|
222 |
|
|
// Tell name of library
|
223 |
|
|
uint32_t name = cmd.inputFile;
|
224 |
|
|
if (name == 0) name = cmd.outputFile;
|
225 |
|
|
if (dataSize() == 0) {
|
226 |
|
|
printf("\nBuilding ForwardCom library %s", cmd.getFilename(name));
|
227 |
|
|
}
|
228 |
|
|
else if (cmd.libraryOptions & (CMDL_LIBRARY_ADDMEMBER | CMDL_LIBRARY_DELETEMEMBER)) {
|
229 |
|
|
printf("\nModifying ForwardCom library %s", cmd.getFilename(name));
|
230 |
|
|
}
|
231 |
|
|
else {
|
232 |
|
|
printf("\nForwardCom library %s", cmd.getFilename(name));
|
233 |
|
|
}
|
234 |
|
|
}
|
235 |
|
|
|
236 |
|
|
// loop through commands
|
237 |
|
|
for (i = 0; i < cmd.lcommands.numEntries(); i++) {
|
238 |
|
|
uint32_t command = cmd.lcommands[i].command;
|
239 |
|
|
switch (command) {
|
240 |
|
|
case CMDL_LIBRARY_ADDMEMBER: // add object file to library
|
241 |
|
|
addMember(cmd.lcommands[i].filename, (uint32_t)cmd.lcommands[i].value);
|
242 |
|
|
break;
|
243 |
|
|
case CMDL_LIBRARY_DELETEMEMBER: // delete object file to library
|
244 |
|
|
deleteMember((uint32_t)cmd.lcommands[i].value);
|
245 |
|
|
break;
|
246 |
|
|
case CMDL_LIBRARY_LISTMEMBERS: // list library members
|
247 |
|
|
listMembers();
|
248 |
|
|
break;
|
249 |
|
|
case CMDL_LIBRARY_EXTRACTMEM: // Extract specified object file(s) from library
|
250 |
|
|
extractMember(cmd.lcommands[i].filename, (uint32_t)cmd.lcommands[i].value);
|
251 |
|
|
break;
|
252 |
|
|
case CMDL_LIBRARY_EXTRACTALL: // Extract all object files from library
|
253 |
|
|
extractAllMembers();
|
254 |
|
|
break;
|
255 |
|
|
case 0:
|
256 |
|
|
break;
|
257 |
|
|
default:
|
258 |
|
|
err.submit(ERR_UNKNOWN_OPTION, "?"); // should not occur
|
259 |
|
|
}
|
260 |
|
|
}
|
261 |
|
|
}
|
262 |
|
|
|
263 |
|
|
// Add object file to library member list
|
264 |
|
|
void CLibrary::addMember(uint32_t filename, uint32_t membername) {
|
265 |
|
|
SLibMember libmem;
|
266 |
|
|
libmem.name = membername;
|
267 |
|
|
libmem.oldOffset = 0;
|
268 |
|
|
libmem.newOffset = filename; // store filename temporarily here
|
269 |
|
|
libmem.action = CMDL_LIBRARY_ADDMEMBER;
|
270 |
|
|
libmem.size = 0;
|
271 |
|
|
|
272 |
|
|
// replace existing member with same name
|
273 |
|
|
int32_t m = members.findFirst(libmem);
|
274 |
|
|
if (m >= 0) {
|
275 |
|
|
// existing member with same name found
|
276 |
|
|
members[m] = libmem;
|
277 |
|
|
members[m].action = CMDL_LIBRARY_DELETEMEMBER | CMDL_LIBRARY_ADDMEMBER;
|
278 |
|
|
if (cmd.verbose) {
|
279 |
|
|
printf("\n replacing member %s", cmd.getFilename(membername));
|
280 |
|
|
}
|
281 |
|
|
}
|
282 |
|
|
else {
|
283 |
|
|
if (cmd.verbose) {
|
284 |
|
|
printf("\n adding member %s", cmd.getFilename(membername));
|
285 |
|
|
}
|
286 |
|
|
members.addUnique(libmem); // add to list. keep alphabetical order
|
287 |
|
|
}
|
288 |
|
|
}
|
289 |
|
|
|
290 |
|
|
// Add delete member from library
|
291 |
|
|
void CLibrary::deleteMember(uint32_t membername) {
|
292 |
|
|
SLibMember libmem;
|
293 |
|
|
libmem.name = membername;
|
294 |
|
|
int32_t m = members.findFirst(libmem);
|
295 |
|
|
if (m < 0) {
|
296 |
|
|
err.submit(ERR_MEMBER_NOT_FOUND_DEL, cmd.getFilename(membername));
|
297 |
|
|
return;
|
298 |
|
|
}
|
299 |
|
|
members[m].action = CMDL_LIBRARY_DELETEMEMBER;
|
300 |
|
|
if (cmd.verbose) {
|
301 |
|
|
printf("\n deleting member %s", cmd.getFilename(membername));
|
302 |
|
|
}
|
303 |
|
|
}
|
304 |
|
|
|
305 |
|
|
// Extract member from library
|
306 |
|
|
void CLibrary::extractMember(uint32_t filename, uint32_t membername) {
|
307 |
|
|
// find member
|
308 |
|
|
SLibMember libmem;
|
309 |
|
|
libmem.name = membername;
|
310 |
|
|
int32_t m = members.findFirst(libmem);
|
311 |
|
|
if (m < 0) {
|
312 |
|
|
err.submit(ERR_MEMBER_NOT_FOUND_EXTRACT, cmd.getFilename(membername));
|
313 |
|
|
return;
|
314 |
|
|
}
|
315 |
|
|
uint32_t headerOffset = members[m].oldOffset;
|
316 |
|
|
//uint32_t memberSize = (uint32_t)atoi(header->fileSize);
|
317 |
|
|
uint32_t memberSize = members[m].size;
|
318 |
|
|
if (memberSize + headerOffset + sizeof(SUNIXLibraryHeader) > dataSize()) {
|
319 |
|
|
err.submit(ERR_LIBRARY_FILE_CORRUPT); // Points outside file
|
320 |
|
|
return;
|
321 |
|
|
}
|
322 |
|
|
// file name
|
323 |
|
|
if (filename == 0) filename = membername;
|
324 |
|
|
const char * filenm = cmd.getFilename(filename);
|
325 |
|
|
|
326 |
|
|
// Tell what we are doing
|
327 |
|
|
if (cmd.verbose) {
|
328 |
|
|
if (filename == membername) {
|
329 |
|
|
printf("\nExtracting file %s from library", filenm);
|
330 |
|
|
}
|
331 |
|
|
else {
|
332 |
|
|
printf("\nExtracting library member %s to file %s",
|
333 |
|
|
cmd.getFilename(membername), filenm);
|
334 |
|
|
}
|
335 |
|
|
}
|
336 |
|
|
// extract file
|
337 |
|
|
CFileBuffer memberbuf;
|
338 |
|
|
memberbuf.push(buf() + headerOffset + sizeof(SUNIXLibraryHeader), memberSize);
|
339 |
|
|
// write file
|
340 |
|
|
memberbuf.write(filenm);
|
341 |
|
|
}
|
342 |
|
|
|
343 |
|
|
// Extract all members from library
|
344 |
|
|
void CLibrary::extractAllMembers() {
|
345 |
|
|
uint32_t num = members.numEntries();
|
346 |
|
|
if (num == 0) {
|
347 |
|
|
err.submit(ERR_MEMBER_NOT_FOUND_EXTRACT, "");
|
348 |
|
|
}
|
349 |
|
|
for (uint32_t i = 0; i < num; i++) {
|
350 |
|
|
extractMember(members[i].name, members[i].name);
|
351 |
|
|
}
|
352 |
|
|
}
|
353 |
|
|
|
354 |
|
|
// List all library members
|
355 |
|
|
void CLibrary::listMembers() {
|
356 |
|
|
CDynamicArray<SSymbolEntry> symbolList; // symbol list
|
357 |
|
|
|
358 |
|
|
printf("\nMembers of library %s:", cmd.getFilename(cmd.inputFile));
|
359 |
|
|
uint32_t m, i; // loop counters
|
360 |
|
|
for (m = 0; m < members.numEntries(); m++) {
|
361 |
|
|
if (members[m].name == 0) continue; // has been deleted
|
362 |
|
|
// print member name
|
363 |
|
|
if (cmd.verbose < 2) {
|
364 |
|
|
printf("\n %s", cmd.getFilename(members[m].name));
|
365 |
|
|
}
|
366 |
|
|
else if (cmd.verbose >= 2) {
|
367 |
|
|
printf("\n %s export:", cmd.getFilename(members[m].name));
|
368 |
|
|
// print exported symbol names for this member
|
369 |
|
|
// clear buffers
|
370 |
|
|
memberBuffer.setSize(0); symbolNameBuffer.setSize(0); symbolList.setSize(0);
|
371 |
|
|
// put member into buffer in order to extract symbols
|
372 |
|
|
memberBuffer.push(buf() + members[m].oldOffset + (uint32_t)sizeof(SUNIXLibraryHeader), members[m].size);
|
373 |
|
|
// extract symbols from ELF file
|
374 |
|
|
memberBuffer.listSymbols(&symbolNameBuffer, &symbolList, m, 0, 1);
|
375 |
|
|
// sort alphabetically
|
376 |
|
|
symbolList.sort();
|
377 |
|
|
// list names
|
378 |
|
|
for (i = 0; i < symbolList.numEntries(); i++) {
|
379 |
|
|
printf("\n %s", symbolNameBuffer.getString(symbolList[i].name));
|
380 |
|
|
}
|
381 |
|
|
}
|
382 |
|
|
if (cmd.verbose >= 3) {
|
383 |
|
|
// print imported symbol names for this member
|
384 |
|
|
printf("\n import:");
|
385 |
|
|
// clear buffers
|
386 |
|
|
memberBuffer.setSize(0); symbolNameBuffer.setSize(0); symbolList.setSize(0);
|
387 |
|
|
// put member into buffer in order to extract symbols
|
388 |
|
|
memberBuffer.push(buf() + members[m].oldOffset + (uint32_t)sizeof(SUNIXLibraryHeader), members[m].size);
|
389 |
|
|
// extract symbols from ELF file
|
390 |
|
|
memberBuffer.listSymbols(&symbolNameBuffer, &symbolList, m, 0, 2);
|
391 |
|
|
// sort alphabetically
|
392 |
|
|
symbolList.sort();
|
393 |
|
|
// list names
|
394 |
|
|
for (i = 0; i < symbolList.numEntries(); i++) {
|
395 |
|
|
printf("\n %s", symbolNameBuffer.getString(symbolList[i].name));
|
396 |
|
|
}
|
397 |
|
|
}
|
398 |
|
|
}
|
399 |
|
|
}
|
400 |
|
|
|
401 |
|
|
|
402 |
|
|
// Generate data contents of new library from old one with possible additions and deletions
|
403 |
|
|
void CLibrary::generateNewLibraryBody() {
|
404 |
|
|
uint32_t m; // member number
|
405 |
|
|
SUNIXLibraryHeader header; // new member header
|
406 |
|
|
|
407 |
|
|
// loop through member list
|
408 |
|
|
for (m = 0; m < members.numEntries(); m++) {
|
409 |
|
|
if (members[m].name && members[m].action != 0 && members[m].action != CMDL_LIBRARY_DELETEMEMBER) {
|
410 |
|
|
if (members[m].oldOffset && members[m].action == CMDL_LIBRARY_PRESERVEMEMBER) {
|
411 |
|
|
// preserve existing member
|
412 |
|
|
SUNIXLibraryHeader * phead = &get<SUNIXLibraryHeader>(members[m].oldOffset);
|
413 |
|
|
uint32_t size = atoi(phead->fileSize);
|
414 |
|
|
if (sizeof(SUNIXLibraryHeader) + size + members[m].oldOffset > dataSize()) {
|
415 |
|
|
err.submit(ERR_LIBRARY_FILE_CORRUPT); return;
|
416 |
|
|
}
|
417 |
|
|
// put header and file into buffer
|
418 |
|
|
members[m].newOffset = dataBuffer.push(buf() + members[m].oldOffset, size + (uint32_t)sizeof(SUNIXLibraryHeader));
|
419 |
|
|
}
|
420 |
|
|
else if (members[m].action & CMDL_LIBRARY_ADDMEMBER) {
|
421 |
|
|
// get member from file
|
422 |
|
|
// filename is temporarily stored in newOffset
|
423 |
|
|
if (members[m].newOffset == 0 || (members[m].newOffset >= cmd.fileNameBuffer.dataSize())) {
|
424 |
|
|
members[m].newOffset = members[m].name;
|
425 |
|
|
}
|
426 |
|
|
const char * filename = cmd.getFilename(members[m].newOffset);
|
427 |
|
|
memberBuffer.setSize(0); // clear memberBuffer first
|
428 |
|
|
memberBuffer.read(filename);
|
429 |
|
|
if (err.number()) return;
|
430 |
|
|
// check file type
|
431 |
|
|
if (memberBuffer.getFileType() != FILETYPE_FWC) {
|
432 |
|
|
err.submit(ERR_LIBRARY_MEMBER_TYPE, filename, getFileFormatName(memberBuffer.getFileType()));
|
433 |
|
|
return;
|
434 |
|
|
}
|
435 |
|
|
// remove path from file name
|
436 |
|
|
const char * membername = removePath(filename);
|
437 |
|
|
// make member header
|
438 |
|
|
memset(&header, ' ', sizeof(header));
|
439 |
|
|
// date
|
440 |
|
|
sprintf(header.date, "%llu ", (unsigned long long)time(0));
|
441 |
|
|
// User and group id
|
442 |
|
|
header.userID[0] = '0';
|
443 |
|
|
header.groupID[0] = '0';
|
444 |
|
|
// File mode
|
445 |
|
|
memcpy(header.fileMode, "100666", 6);
|
446 |
|
|
// Name
|
447 |
|
|
uint32_t namelength = (uint32_t)strlen(membername);
|
448 |
|
|
if (namelength < 16) {
|
449 |
|
|
memcpy(header.name, membername, namelength);
|
450 |
|
|
header.name[namelength] = '/'; // terminate with '/'
|
451 |
|
|
}
|
452 |
|
|
else {
|
453 |
|
|
// cannot save name now, wait until longnames record is made
|
454 |
|
|
members[m].name = cmd.fileNameBuffer.pushString(membername);
|
455 |
|
|
}
|
456 |
|
|
// Size
|
457 |
|
|
sprintf(header.fileSize, "%u", memberBuffer.dataSize());
|
458 |
|
|
members[m].size = memberBuffer.dataSize();
|
459 |
|
|
// End
|
460 |
|
|
header.headerEnd[0] = '`'; header.headerEnd[1] = '\n';
|
461 |
|
|
// remove terminating zeroes left by sprintf
|
462 |
|
|
char * p = (char *)&header;
|
463 |
|
|
for (uint32_t i = 0; i < sizeof(header); i++) {
|
464 |
|
|
if (p[i] == 0) p[i] = ' ';
|
465 |
|
|
}
|
466 |
|
|
// put header and file into buffer
|
467 |
|
|
members[m].newOffset = dataBuffer.push(&header, (uint32_t)sizeof(header));
|
468 |
|
|
dataBuffer.push(memberBuffer.buf(), memberBuffer.dataSize());
|
469 |
|
|
}
|
470 |
|
|
}
|
471 |
|
|
// align next member
|
472 |
|
|
dataBuffer.align(alignBy);
|
473 |
|
|
}
|
474 |
|
|
}
|
475 |
|
|
|
476 |
|
|
// Make library header, symbol table, longnames record, data
|
477 |
|
|
void CLibrary::makeBinaryFile() {
|
478 |
|
|
// make signature
|
479 |
|
|
outFile.push(archiveSignature, 8);
|
480 |
|
|
|
481 |
|
|
// make symbol list and longnames record
|
482 |
|
|
CMemoryBuffer longNamesBuf; // list of long member names
|
483 |
|
|
CDynamicArray<SSymbolEntry> symbolList; // symbol list, part of symdefSorted
|
484 |
|
|
uint32_t m; // member number
|
485 |
|
|
uint32_t i; // loop counter
|
486 |
|
|
|
487 |
|
|
// loop through members to generate longnames and symbol list
|
488 |
|
|
for (m = 0; m < members.numEntries(); m++) {
|
489 |
|
|
if (members[m].action & (CMDL_LIBRARY_PRESERVEMEMBER | CMDL_LIBRARY_ADDMEMBER)) {
|
490 |
|
|
// get member header
|
491 |
|
|
SUNIXLibraryHeader * phead = &dataBuffer.get<SUNIXLibraryHeader>(members[m].newOffset);
|
492 |
|
|
// member name
|
493 |
|
|
const char * name = cmd.getFilename(members[m].name);
|
494 |
|
|
if (strlen(name) > 15) {
|
495 |
|
|
// put name in longnames record
|
496 |
|
|
uint32_t longnameos = longNamesBuf.pushString(name);
|
497 |
|
|
sprintf(phead->name, "/%u", longnameos);
|
498 |
|
|
phead->name[strlen(phead->name)] = ' '; // remove terminating zero
|
499 |
|
|
}
|
500 |
|
|
// put member into buffer in order to extract symbols
|
501 |
|
|
memberBuffer.setSize(0);
|
502 |
|
|
memberBuffer.push(dataBuffer.buf() + members[m].newOffset + (uint32_t)sizeof(SUNIXLibraryHeader), members[m].size);
|
503 |
|
|
// extract public symbols from ELF file
|
504 |
|
|
memberBuffer.listSymbols(&symbolNameBuffer, &symbolList, m, 0, 1);
|
505 |
|
|
}
|
506 |
|
|
}
|
507 |
|
|
|
508 |
|
|
// sort symbol list and check for duplicate symbol names
|
509 |
|
|
checkDuplicateSymbols(symbolList);
|
510 |
|
|
|
511 |
|
|
// calculate size of symbol list
|
512 |
|
|
uint32_t symbolListSize = (uint32_t)sizeof(SUNIXLibraryHeader) + symbolList.numEntries()*8 + 8 + symbolNameBuffer.dataSize();
|
513 |
|
|
symbolListSize = (symbolListSize + alignBy - 1) & - alignBy; // align
|
514 |
|
|
// calculate size of longnames record
|
515 |
|
|
uint32_t longnamesSize = 0;
|
516 |
|
|
if (longNamesBuf.dataSize() > 1) { // longnames record needed
|
517 |
|
|
longnamesSize = sizeof(SUNIXLibraryHeader) + longNamesBuf.dataSize();
|
518 |
|
|
longnamesSize = (longnamesSize + alignBy - 1) & - alignBy; // align
|
519 |
|
|
}
|
520 |
|
|
// offset to first normal member
|
521 |
|
|
uint32_t firstMemberOffset = 8 + symbolListSize + longnamesSize;
|
522 |
|
|
|
523 |
|
|
// make Mach-O style symbol list
|
524 |
|
|
// put member addresses into symbol list
|
525 |
|
|
for (i = 0; i < symbolList.numEntries(); i++) {
|
526 |
|
|
m = symbolList[i].member;
|
527 |
|
|
if (m < members.numEntries()) {
|
528 |
|
|
symbolList[i].member = members[m].newOffset + firstMemberOffset;
|
529 |
|
|
}
|
530 |
|
|
else symbolList[i].member = 0; // should not occur
|
531 |
|
|
}
|
532 |
|
|
// make header
|
533 |
|
|
SUNIXLibraryHeader header;
|
534 |
|
|
memset(&header, ' ', sizeof(header));
|
535 |
|
|
memcpy(header.name, "/SYMDEF SORTED/", 15);
|
536 |
|
|
sprintf(header.date, "%llu ", (unsigned long long)time(0));
|
537 |
|
|
header.userID[0] = '0';
|
538 |
|
|
header.groupID[0] = '0';
|
539 |
|
|
memcpy(header.fileMode, "100666", 6);
|
540 |
|
|
sprintf(header.fileSize, "%u", symbolListSize - (uint32_t)sizeof(SUNIXLibraryHeader));
|
541 |
|
|
header.headerEnd[0] = '`'; header.headerEnd[1] = '\n';
|
542 |
|
|
// remove terminating zeroes
|
543 |
|
|
char * p = (char*)&header;
|
544 |
|
|
for (i = 0; i < sizeof(header); i++) {
|
545 |
|
|
if (p[i] == 0) p[i] = ' ';
|
546 |
|
|
}
|
547 |
|
|
// save header
|
548 |
|
|
outFile.push(&header, (uint32_t)sizeof(header));
|
549 |
|
|
uint32_t n = symbolList.numEntries()*8; // length of list
|
550 |
|
|
outFile.push(&n, 4);
|
551 |
|
|
// symbol list
|
552 |
|
|
for (i = 0; i < symbolList.numEntries(); i++) {
|
553 |
|
|
outFile.push(&symbolList[i].name, 4);
|
554 |
|
|
outFile.push(&symbolList[i].member, 4);
|
555 |
|
|
}
|
556 |
|
|
// length of string table
|
557 |
|
|
n = symbolNameBuffer.dataSize();
|
558 |
|
|
outFile.push(&n, 4);
|
559 |
|
|
// string table
|
560 |
|
|
outFile.push(symbolNameBuffer.buf(), n);
|
561 |
|
|
// align
|
562 |
|
|
outFile.align(alignBy);
|
563 |
|
|
|
564 |
|
|
// Make longnames record if needed
|
565 |
|
|
if (longnamesSize) {
|
566 |
|
|
memcpy(header.name, "// ", 16);
|
567 |
|
|
sprintf(header.fileSize, "%u", longNamesBuf.dataSize());
|
568 |
|
|
header.fileSize[strlen(header.fileSize)] = ' '; // remove terminating zero
|
569 |
|
|
outFile.push(&header, (uint32_t)sizeof(header));
|
570 |
|
|
outFile.push(longNamesBuf.buf(), longNamesBuf.dataSize());
|
571 |
|
|
outFile.align(alignBy);
|
572 |
|
|
}
|
573 |
|
|
|
574 |
|
|
// Insert all regular members
|
575 |
|
|
outFile.push(dataBuffer.buf(), dataBuffer.dataSize());
|
576 |
|
|
}
|
577 |
|
|
|
578 |
|
|
// Check if symbollist contains duplicate names
|
579 |
|
|
void CLibrary::checkDuplicateSymbols(CDynamicArray<SSymbolEntry> & symbolList) {
|
580 |
|
|
uint32_t i, j; // loop counters
|
581 |
|
|
|
582 |
|
|
// sort symbol list
|
583 |
|
|
symbolList.sort();
|
584 |
|
|
|
585 |
|
|
// check symbol list for duplicates
|
586 |
|
|
for (i = 1; i < symbolList.numEntries(); i++) {
|
587 |
|
|
if (symbolList[i-1] == symbolList[i] && !(symbolList[i-1].st_bind & STB_WEAK) && !(symbolList[i].st_bind & STB_WEAK)) {
|
588 |
|
|
// make a list of modules containing this symbol name
|
589 |
|
|
CMemoryBuffer moduleNames;
|
590 |
|
|
j = i - 1;
|
591 |
|
|
while (j < symbolList.numEntries() && symbolList[j] == symbolList[i]) {
|
592 |
|
|
if (j >= i) moduleNames.push(", ", 2);
|
593 |
|
|
uint32_t m = symbolList[j].member;
|
594 |
|
|
const char * mname = "?";
|
595 |
|
|
if (m < members.numEntries()) {
|
596 |
|
|
mname = cmd.getFilename(members[m].name);
|
597 |
|
|
}
|
598 |
|
|
moduleNames.push(mname, (uint32_t)strlen(mname));
|
599 |
|
|
j++;
|
600 |
|
|
}
|
601 |
|
|
moduleNames.pushString("");
|
602 |
|
|
const char * symbolname = symbolNameBuffer.getString(symbolList[i].name);
|
603 |
|
|
err.submit(ERR_DUPLICATE_SYMBOL_IN_LIB, symbolname, (char*)moduleNames.buf());
|
604 |
|
|
i = j - 1;
|
605 |
|
|
}
|
606 |
|
|
}
|
607 |
|
|
}
|
608 |
|
|
|
609 |
|
|
// get name of a library member
|
610 |
|
|
const char * CLibrary::getMemberName(uint32_t memberOffset) {
|
611 |
|
|
if (memberOffset >= dataSize()) return "unknown?";
|
612 |
|
|
char * namebuf = (char *)cmd.fileNameBuffer.buf();
|
613 |
|
|
SUNIXLibraryHeader * phead = (SUNIXLibraryHeader *)(buf() + memberOffset);
|
614 |
|
|
if (phead->name[0] == '/') {
|
615 |
|
|
// long name
|
616 |
|
|
uint32_t namei = atoi(phead->name+1);
|
617 |
|
|
if (longNames == 0) findLongNames();
|
618 |
|
|
// offset into longNames
|
619 |
|
|
uint32_t os = longNames + namei;
|
620 |
|
|
if (longNames == 0 || namei >= longNamesSize) return "unknown?";
|
621 |
|
|
return (char*)buf() + os;
|
622 |
|
|
}
|
623 |
|
|
// short name. replace terminating '/' by zero
|
624 |
|
|
uint32_t nm = cmd.fileNameBuffer.push(phead->name, 16);
|
625 |
|
|
namebuf += nm;
|
626 |
|
|
for (int i = 0; i < 16; i++) {
|
627 |
|
|
if (namebuf[i] == '/') namebuf[i] = 0;
|
628 |
|
|
}
|
629 |
|
|
namebuf[15] = 0; // make sure string ends even if '/' is missing
|
630 |
|
|
return namebuf;
|
631 |
|
|
}
|
632 |
|
|
|
633 |
|
|
// get size of a library member
|
634 |
|
|
uint32_t CLibrary::getMemberSize(uint32_t memberOffset) {
|
635 |
|
|
if (memberOffset >= dataSize()) return 0;
|
636 |
|
|
SUNIXLibraryHeader * phead = (SUNIXLibraryHeader *)(buf() + memberOffset);
|
637 |
|
|
uint32_t size = atoi(phead->fileSize);
|
638 |
|
|
return size;
|
639 |
|
|
}
|
640 |
|
|
|
641 |
|
|
// Find longNames record
|
642 |
|
|
void CLibrary::findLongNames() {
|
643 |
|
|
// find longNames record
|
644 |
|
|
uint32_t offset = 8;
|
645 |
|
|
SUNIXLibraryHeader * header;
|
646 |
|
|
uint32_t memberSize;
|
647 |
|
|
// Loop through library headers.
|
648 |
|
|
while (offset + sizeof(SUNIXLibraryHeader) < dataSize()) {
|
649 |
|
|
// Find header
|
650 |
|
|
header = &get<SUNIXLibraryHeader>(offset);
|
651 |
|
|
// Size of member
|
652 |
|
|
memberSize = atoi(header->fileSize);
|
653 |
|
|
if (int32_t(memberSize) < 0 || memberSize + offset + sizeof(SUNIXLibraryHeader) > dataSize()) {
|
654 |
|
|
err.submit(ERR_LIBRARY_FILE_CORRUPT); // Points outside file
|
655 |
|
|
return;
|
656 |
|
|
}
|
657 |
|
|
// member name
|
658 |
|
|
if (strncmp(header->name, "// ", 3) == 0) {
|
659 |
|
|
// This is the long names member. Remember its position
|
660 |
|
|
longNames = offset + sizeof(SUNIXLibraryHeader);
|
661 |
|
|
longNamesSize = memberSize;
|
662 |
|
|
return;
|
663 |
|
|
}
|
664 |
|
|
// get next offset
|
665 |
|
|
offset += sizeof(SUNIXLibraryHeader) + memberSize;
|
666 |
|
|
|
667 |
|
|
// Round up for alignment. Nominal alignment = 4, max alignment = 256
|
668 |
|
|
while ((offset & 0xFF)
|
669 |
|
|
&& offset + sizeof(SUNIXLibraryHeader) < dataSize()
|
670 |
|
|
&& get<uint8_t>(offset) <= ' ') offset++;
|
671 |
|
|
}
|
672 |
|
|
}
|
673 |
|
|
|
674 |
|
|
|
675 |
|
|
// Find exported symbol in library
|
676 |
|
|
// The return value is a file offset to the library member containing the symbol,
|
677 |
|
|
// or zero if not found
|
678 |
|
|
uint32_t CLibrary::findSymbol(const char * name) {
|
679 |
|
|
uint32_t memberSize;
|
680 |
|
|
uint32_t offset = 8;
|
681 |
|
|
SUNIXLibraryHeader * symdefHead = (SUNIXLibraryHeader *)(buf() + offset);
|
682 |
|
|
// expect symbol table as first record
|
683 |
|
|
while (strncasecmp_(symdefHead->name, "/SYMDEF SORTED/", 15) != 0) {
|
684 |
|
|
// not found. search whole library
|
685 |
|
|
memberSize = atoi(symdefHead->fileSize);
|
686 |
|
|
offset += memberSize + (uint32_t)sizeof(SUNIXLibraryHeader);
|
687 |
|
|
if (offset + (uint32_t)sizeof(SUNIXLibraryHeader) >= dataSize()) {
|
688 |
|
|
err.submit(ERR_NO_SYMTAB_IN_LIB); // library has no correct symbol table
|
689 |
|
|
return 0;
|
690 |
|
|
}
|
691 |
|
|
symdefHead = (SUNIXLibraryHeader *)(buf() + offset);
|
692 |
|
|
}
|
693 |
|
|
memberSize = atoi(symdefHead->fileSize);
|
694 |
|
|
// pointer to start of list
|
695 |
|
|
offset += (uint32_t)sizeof(SUNIXLibraryHeader);
|
696 |
|
|
uint32_t * listp = (uint32_t *)(buf() + offset);
|
697 |
|
|
uint32_t symlistlen = listp[0]; // length of symbol list
|
698 |
|
|
// string table
|
699 |
|
|
char * stringtab = (char *)(buf() + offset + symlistlen + 8);
|
700 |
|
|
// string table length
|
701 |
|
|
uint32_t stringtablen = *(uint32_t *)(stringtab-4);
|
702 |
|
|
// check integrity
|
703 |
|
|
if ((uint64_t)symlistlen + stringtablen + 8 > memberSize) {
|
704 |
|
|
err.submit(ERR_ELF_STRING_TABLE); return 0;
|
705 |
|
|
}
|
706 |
|
|
// binary search for name
|
707 |
|
|
uint32_t a = 0; // start of search interval
|
708 |
|
|
uint32_t b = symlistlen >> 3; // number of symbols
|
709 |
|
|
uint32_t c = 0; // middle of search interval
|
710 |
|
|
uint32_t nameindex; // index into string table
|
711 |
|
|
while (a < b) { // binary search loop:
|
712 |
|
|
c = (a + b) / 2;
|
713 |
|
|
nameindex = listp[1+2*c]; // index to name of symbol number c
|
714 |
|
|
if (nameindex >= stringtablen) { // check integrity
|
715 |
|
|
err.submit(ERR_ELF_STRING_TABLE); return 0;
|
716 |
|
|
}
|
717 |
|
|
if (strcmp(stringtab + nameindex, name) < 0) { // compare strings
|
718 |
|
|
a = c + 1;
|
719 |
|
|
}
|
720 |
|
|
else {
|
721 |
|
|
b = c;
|
722 |
|
|
}
|
723 |
|
|
}
|
724 |
|
|
nameindex = listp[1+2*a]; // index to name of symbol
|
725 |
|
|
if (a == symlistlen >> 3 || strcmp(stringtab + nameindex, name)) {
|
726 |
|
|
return 0; // not found
|
727 |
|
|
}
|
728 |
|
|
uint32_t memberIndex = listp[2+2*a]; // index to member containing symbol
|
729 |
|
|
if (memberIndex + (uint32_t)sizeof(SUNIXLibraryHeader) > dataSize()) {
|
730 |
|
|
err.submit(ERR_LIBRARY_FILE_CORRUPT); // out of range
|
731 |
|
|
return 0;
|
732 |
|
|
}
|
733 |
|
|
return memberIndex;
|
734 |
|
|
}
|
735 |
|
|
|
736 |
|
|
// check if this is a ForwardCom library
|
737 |
|
|
bool CLibrary::isForwardCom() {
|
738 |
|
|
uint32_t offset = 8;
|
739 |
|
|
SUNIXLibraryHeader * symdefHead = (SUNIXLibraryHeader *)(buf() + offset);
|
740 |
|
|
// expect symbol table as first record
|
741 |
|
|
return (strncasecmp_(symdefHead->name, "/SYMDEF SORTED/", 15) == 0);
|
742 |
|
|
}
|
743 |
|
|
|
744 |
|
|
|
745 |
|
|
// remove path from file name
|
746 |
|
|
const char * removePath(const char * filename) {
|
747 |
|
|
int p;
|
748 |
|
|
const char pathsep1 = '/'; // separator in file path
|
749 |
|
|
#if defined (_WIN32) || defined (__WINDOWS__)
|
750 |
|
|
const char pathsep2 = '\\', pathsep3 = ':'; // additional separators in Windows
|
751 |
|
|
#else
|
752 |
|
|
const char pathsep2 = pathsep1, pathsep3 = pathsep1;
|
753 |
|
|
#endif
|
754 |
|
|
// find last '/' or other path separator in object file name
|
755 |
|
|
for (p = (int)strlen(filename) - 1; p >= 0; p--) {
|
756 |
|
|
if (filename[p] == pathsep1 || filename[p] == pathsep2 || filename[p] == pathsep3) break;
|
757 |
|
|
}
|
758 |
|
|
if (p >= 0) {
|
759 |
|
|
// filename contains path. strip path
|
760 |
|
|
filename += p + 1;
|
761 |
|
|
}
|
762 |
|
|
if (filename[0] == 0) filename = "unknown?";
|
763 |
|
|
return filename;
|
764 |
|
|
}
|
765 |
|
|
|
766 |
|
|
// make library from CELF modules during relinking
|
767 |
|
|
void CLibrary::addELF(CELF & elf) {
|
768 |
|
|
SLibMember member; // entry into members list
|
769 |
|
|
SUNIXLibraryHeader header; // new member header
|
770 |
|
|
zeroAllMembers(member);
|
771 |
|
|
zeroAllMembers(header);
|
772 |
|
|
member.name = elf.moduleName;
|
773 |
|
|
member.action = CMDL_LIBRARY_ADDMEMBER;
|
774 |
|
|
member.size = elf.dataSize();
|
775 |
|
|
sprintf(header.fileSize, "%u", elf.dataSize());
|
776 |
|
|
const char * membername = cmd.getFilename(elf.moduleName);
|
777 |
|
|
uint32_t namelength = (uint32_t)strlen(membername);
|
778 |
|
|
if (namelength < 16) {
|
779 |
|
|
memcpy(header.name, membername, namelength);
|
780 |
|
|
header.name[namelength] = '/'; // terminate with '/'
|
781 |
|
|
}
|
782 |
|
|
// put header and file into buffer
|
783 |
|
|
member.newOffset = dataBuffer.push(&header, (uint32_t)sizeof(header));
|
784 |
|
|
dataBuffer.push(elf.buf(), elf.dataSize());
|
785 |
|
|
members.push(member);
|
786 |
|
|
}
|
787 |
|
|
|
788 |
|
|
// make a library for internal use during relinking
|
789 |
|
|
void CLibrary::makeInternalLibrary() {
|
790 |
|
|
makeBinaryFile(); // make library file, but don't write to disk
|
791 |
|
|
*this << outFile; // transfer to own object as if it had been read from disk
|
792 |
|
|
members.setSize(0); // reset members list
|
793 |
|
|
makeMemberList(); // update internal list
|
794 |
|
|
}
|