5 //===----------------------------------------------------------------------===//
7 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
8 // See https://llvm.org/LICENSE.txt for license information.
9 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
11 //===----------------------------------------------------------------------===//
21 /* Given a set of n object files h ('external' object files) and a set of m
22 object files o ('internal' object files),
23 1. Determines r, the subset of h that o depends on, directly or indirectly
24 2. Removes the files in h - r from the file system
25 3. For each external symbol defined in some file in r, rename it in r U o
26 by prefixing it with "__kmp_external_"
28 hide.exe <n> <filenames for h> <filenames for o>
30 Thus, the prefixed symbols become hidden in the sense that they now have a
36 void stop(char *errorMsg
) {
37 printf("%s\n", errorMsg
);
41 // an entry in the symbol table of a .OBJ file
46 unsigned short sectionNum
, type
;
47 char storageClass
, nAux
;
50 class _rstream
: public istrstream
{
55 _rstream(pair
<const char *, streamsize
> p
)
56 : istrstream(p
.first
, p
.second
), buf(p
.first
) {}
57 ~_rstream() { delete[] buf
; }
60 // A stream encapsulating the content of a file or the content of a string,
61 // overriding the >> operator to read various integer types in binary form,
62 // as well as a symbol table entry.
63 class rstream
: public _rstream
{
65 template <class T
> inline rstream
&doRead(T
&x
) {
66 read((char *)&x
, sizeof(T
));
69 static pair
<const char *, streamsize
> getBuf(const char *fileName
) {
70 ifstream
raw(fileName
, ios::binary
| ios::in
);
72 stop("rstream.getBuf: Error opening file");
73 raw
.seekg(0, ios::end
);
74 streampos fileSize
= raw
.tellg();
76 stop("rstream.getBuf: Error reading file");
77 char *buf
= new char[fileSize
];
78 raw
.seekg(0, ios::beg
);
79 raw
.read(buf
, fileSize
);
80 return pair
<const char *, streamsize
>(buf
, fileSize
);
84 // construct from a string
85 rstream(const char *buf
, streamsize size
)
86 : _rstream(pair
<const char *, streamsize
>(buf
, size
)) {}
87 // construct from a file whole content is fully read once to initialize the
88 // content of this stream
89 rstream(const char *fileName
) : _rstream(getBuf(fileName
)) {}
90 rstream
&operator>>(int &x
) { return doRead(x
); }
91 rstream
&operator>>(unsigned &x
) { return doRead(x
); }
92 rstream
&operator>>(short &x
) { return doRead(x
); }
93 rstream
&operator>>(unsigned short &x
) { return doRead(x
); }
94 rstream
&operator>>(Symbol
&e
) {
100 // string table in a .OBJ file
103 map
<string
, unsigned> directory
;
107 // make <directory> from <length> bytes in <data>
108 void makeDirectory(void) {
111 string s
= string(data
+ i
);
112 directory
.insert(make_pair(s
, i
));
116 // initialize <length> and <data> with contents specified by the arguments
117 void init(const char *_data
) {
118 unsigned _length
= *(unsigned *)_data
;
120 if (_length
< sizeof(unsigned) || _length
!= *(unsigned *)_data
)
121 stop("StringTable.init: Invalid symbol table");
122 if (_data
[_length
- 1]) {
123 // to prevent runaway strings, make sure the data ends with a zero
124 data
= new char[length
= _length
+ 1];
127 data
= new char[length
= _length
];
129 *(unsigned *)data
= length
;
130 KMP_MEMCPY(data
+ sizeof(unsigned), _data
+ sizeof(unsigned),
131 length
- sizeof(unsigned));
136 StringTable(rstream
&f
) {
137 // Construct string table by reading from f.
144 if (strSize
< sizeof(unsigned))
145 stop("StringTable: Invalid string table");
146 strData
= new char[strSize
];
147 *(unsigned *)strData
= strSize
;
148 // read the raw data into <strData>
149 f
.read(strData
+ sizeof(unsigned), strSize
- sizeof(unsigned));
152 stop("StringTable: Unexpected EOF");
156 StringTable(const set
<string
> &strings
) {
157 // Construct string table from given strings.
159 set
<string
>::const_iterator it
;
162 // count required size for data
163 for (length
= sizeof(unsigned), it
= strings
.begin(); it
!= strings
.end();
165 size_t l
= (*it
).size();
167 if (l
> (unsigned)0xFFFFFFFF)
168 stop("StringTable: String too long");
171 if (length
> (unsigned)0xFFFFFFFF)
172 stop("StringTable: Symbol table too long");
175 data
= new char[length
];
176 *(unsigned *)data
= length
;
177 // populate data and directory
178 for (p
= data
+ sizeof(unsigned), it
= strings
.begin(); it
!= strings
.end();
180 const string
&str
= *it
;
181 size_t l
= str
.size();
183 directory
.insert(make_pair(str
, p
- data
));
184 KMP_MEMCPY(p
, str
.c_str(), l
);
190 ~StringTable() { delete[] data
; }
191 // Returns encoding for given string based on this string table. Error if
192 // string length is greater than 8 but string is not in the string table
194 __int64
encode(const string
&str
) {
197 if (str
.size() <= 8) {
200 KMP_STRNCPY_S((char *)&r
, sizeof(r
), str
.c_str(), 8);
203 // represented as index into table
204 map
<string
, unsigned>::const_iterator it
= directory
.find(str
);
205 if (it
== directory
.end())
206 stop("StringTable::encode: String now found in string table");
207 ((unsigned *)&r
)[0] = 0;
208 ((unsigned *)&r
)[1] = (*it
).second
;
212 // Returns string represented by x based on this string table. Error if x
213 // references an invalid position in the table--returns the empty string.
214 string
decode(__int64 x
) const {
215 if (*(unsigned *)&x
== 0) {
216 // represented as index into table
217 unsigned &p
= ((unsigned *)&x
)[1];
219 stop("StringTable::decode: Invalid string table lookup");
220 return string(data
+ p
);
223 char *p
= (char *)&x
;
226 for (i
= 0; i
< 8 && p
[i
]; ++i
)
231 void write(ostream
&os
) { os
.write(data
, length
); }
234 // for the named object file, determines the set of defined symbols and the set
235 // of undefined external symbols and writes them to <defined> and <undefined>
237 void computeExternalSymbols(const char *fileName
, set
<string
> *defined
,
238 set
<string
> *undefined
) {
241 unsigned symTabStart
, symNEntries
;
244 f
.seekg(0, ios::end
);
245 fileSize
= f
.tellg();
248 f
>> symTabStart
>> symNEntries
;
249 // seek to the string table
250 f
.seekg(strTabStart
= symTabStart
+ 18 * (size_t)symNEntries
);
252 printf("computeExternalSymbols: fileName='%s', fileSize = %lu, symTabStart "
253 "= %u, symNEntries = %u\n",
254 fileName
, (unsigned long)fileSize
, symTabStart
, symNEntries
);
255 stop("computeExternalSymbols: Unexpected EOF 1");
257 StringTable
stringTable(f
); // read the string table
258 if (f
.tellg() != fileSize
)
259 stop("computeExternalSymbols: Unexpected data after string table");
262 f
.seekg(symTabStart
); // seek to the symbol table
266 for (int i
= 0; i
< symNEntries
; ++i
) {
267 // process each entry
271 stop("computeExternalSymbols: Unexpected EOF 2");
274 stop("computeExternalSymbols: File read error");
275 if (e
.nAux
) { // auxiliary entry: skip
276 f
.seekg(e
.nAux
* 18, ios::cur
);
279 // if symbol is extern and defined in the current file, insert it
280 if (e
.storageClass
== 2)
282 defined
->insert(stringTable
.decode(e
.name
));
284 undefined
->insert(stringTable
.decode(e
.name
));
288 // For each occurrence of an external symbol in the object file named by
289 // by <fileName> that is a member of <hide>, renames it by prefixing
290 // with "__kmp_external_", writing back the file in-place
291 void hideSymbols(char *fileName
, const set
<string
> &hide
) {
292 static const string
prefix("__kmp_external_");
293 set
<string
> strings
; // set of all occurring symbols, appropriately prefixed
296 unsigned symTabStart
, symNEntries
;
298 rstream
in(fileName
);
300 in
.seekg(0, ios::end
);
301 fileSize
= in
.tellg();
304 in
>> symTabStart
>> symNEntries
;
305 in
.seekg(strTabStart
= symTabStart
+ 18 * (size_t)symNEntries
);
307 stop("hideSymbols: Unexpected EOF");
308 StringTable
stringTableOld(in
); // read original string table
310 if (in
.tellg() != fileSize
)
311 stop("hideSymbols: Unexpected data after string table");
313 // compute set of occurring strings with prefix added
314 for (i
= 0; i
< symNEntries
; ++i
) {
317 in
.seekg(symTabStart
+ i
* 18);
319 stop("hideSymbols: Unexpected EOF");
322 stop("hideSymbols: File read error");
325 const string
&s
= stringTableOld
.decode(e
.name
);
326 // if symbol is extern and found in <hide>, prefix and insert into strings,
327 // otherwise, just insert into strings without prefix
329 (e
.storageClass
== 2 && hide
.find(s
) != hide
.end()) ? prefix
+ s
: s
);
332 ofstream
out(fileName
, ios::trunc
| ios::out
| ios::binary
);
334 stop("hideSymbols: Error opening output file");
336 // make new string table from string set
337 StringTable stringTableNew
= StringTable(strings
);
339 // copy input file to output file up to just before the symbol table
341 char *buf
= new char[symTabStart
];
342 in
.read(buf
, symTabStart
);
343 out
.write(buf
, symTabStart
);
346 // copy input symbol table to output symbol table with name translation
347 for (i
= 0; i
< symNEntries
; ++i
) {
350 in
.seekg(symTabStart
+ i
* 18);
352 stop("hideSymbols: Unexpected EOF");
355 stop("hideSymbols: File read error");
356 const string
&s
= stringTableOld
.decode(e
.name
);
357 out
.seekp(symTabStart
+ i
* 18);
358 e
.name
= stringTableNew
.encode(
359 (e
.storageClass
== 2 && hide
.find(s
) != hide
.end()) ? prefix
+ s
: s
);
360 out
.write((char *)&e
, 18);
362 stop("hideSymbols: File write error");
364 // copy auxiliary symbol table entries
366 for (int j
= 1; j
<= nAux
; ++j
) {
368 out
.seekp(symTabStart
+ (i
+ j
) * 18);
369 out
.write((char *)&e
, 18);
374 // output string table
375 stringTableNew
.write(out
);
378 // returns true iff <a> and <b> have no common element
379 template <class T
> bool isDisjoint(const set
<T
> &a
, const set
<T
> &b
) {
380 set
<T
>::const_iterator ita
, itb
;
382 for (ita
= a
.begin(), itb
= b
.begin(); ita
!= a
.end() && itb
!= b
.end();) {
383 const T
&ta
= *ita
, &tb
= *itb
;
394 // PRE: <defined> and <undefined> are arrays with <nTotal> elements where
395 // <nTotal> >= <nExternal>. The first <nExternal> elements correspond to the
396 // external object files and the rest correspond to the internal object files.
397 // POST: file x is said to depend on file y if undefined[x] and defined[y] are
398 // not disjoint. Returns the transitive closure of the set of internal object
399 // files, as a set of file indexes, under the 'depends on' relation, minus the
400 // set of internal object files.
401 set
<int> *findRequiredExternal(int nExternal
, int nTotal
, set
<string
> *defined
,
402 set
<string
> *undefined
) {
403 set
<int> *required
= new set
<int>;
408 for (i
= nTotal
- 1; i
>= nExternal
; --i
)
409 fresh
[cur
].insert(i
);
412 for (set
<int>::iterator it
= fresh
[cur
].begin(); it
!= fresh
[cur
].end();
414 set
<string
> &s
= undefined
[*it
];
416 for (i
= 0; i
< nExternal
; ++i
) {
417 if (required
->find(i
) == required
->end()) {
418 if (!isDisjoint(defined
[i
], s
)) {
419 // found a new qualifying element
421 fresh
[1 - cur
].insert(i
);
433 int main(int argc
, char **argv
) {
434 int nExternal
, nInternal
, i
;
435 set
<string
> *defined
, *undefined
;
436 set
<int>::iterator it
;
439 stop("Please specify a positive integer followed by a list of object "
441 nExternal
= atoi(argv
[1]);
443 stop("Please specify a positive integer followed by a list of object "
445 if (nExternal
+ 2 > argc
)
446 stop("Too few external objects");
447 nInternal
= argc
- nExternal
- 2;
448 defined
= new set
<string
>[argc
- 2];
449 undefined
= new set
<string
>[argc
- 2];
451 // determine the set of defined and undefined external symbols
452 for (i
= 2; i
< argc
; ++i
)
453 computeExternalSymbols(argv
[i
], defined
+ i
- 2, undefined
+ i
- 2);
455 // determine the set of required external files
456 set
<int> *requiredExternal
=
457 findRequiredExternal(nExternal
, argc
- 2, defined
, undefined
);
460 // determine the set of symbols to hide--namely defined external symbols of
461 // the required external files
462 for (it
= requiredExternal
->begin(); it
!= requiredExternal
->end(); ++it
) {
464 set
<string
>::iterator it2
;
465 // We have to insert one element at a time instead of inserting a range
466 // because the insert member function taking a range doesn't exist on
467 // Windows* OS, at least at the time of this writing.
468 for (it2
= defined
[idx
].begin(); it2
!= defined
[idx
].end(); ++it2
)
472 // process the external files--removing those that are not required and hiding
473 // the appropriate symbols in the others
474 for (i
= 0; i
< nExternal
; ++i
)
475 if (requiredExternal
->find(i
) != requiredExternal
->end())
476 hideSymbols(argv
[2 + i
], hide
);
479 // hide the appropriate symbols in the internal files
480 for (i
= nExternal
+ 2; i
< argc
; ++i
)
481 hideSymbols(argv
[i
], hide
);