Path: blob/main/contrib/llvm-project/openmp/runtime/src/extractExternal.cpp
35258 views
/*1* extractExternal.cpp2*/34//===----------------------------------------------------------------------===//5//6// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.7// See https://llvm.org/LICENSE.txt for license information.8// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception9//10//===----------------------------------------------------------------------===//1112#include <fstream>13#include <iostream>14#include <map>15#include <set>16#include <stdlib.h>17#include <string>18#include <strstream>1920/* Given a set of n object files h ('external' object files) and a set of m21object files o ('internal' object files),221. Determines r, the subset of h that o depends on, directly or indirectly232. Removes the files in h - r from the file system243. For each external symbol defined in some file in r, rename it in r U o25by prefixing it with "__kmp_external_"26Usage:27hide.exe <n> <filenames for h> <filenames for o>2829Thus, the prefixed symbols become hidden in the sense that they now have a30special prefix.31*/3233using namespace std;3435void stop(char *errorMsg) {36printf("%s\n", errorMsg);37exit(1);38}3940// an entry in the symbol table of a .OBJ file41class Symbol {42public:43__int64 name;44unsigned value;45unsigned short sectionNum, type;46char storageClass, nAux;47};4849class _rstream : public istrstream {50private:51const char *buf;5253protected:54_rstream(pair<const char *, streamsize> p)55: istrstream(p.first, p.second), buf(p.first) {}56~_rstream() { delete[] buf; }57};5859// A stream encapsulating the content of a file or the content of a string,60// overriding the >> operator to read various integer types in binary form,61// as well as a symbol table entry.62class rstream : public _rstream {63private:64template <class T> inline rstream &doRead(T &x) {65read((char *)&x, sizeof(T));66return *this;67}68static pair<const char *, streamsize> getBuf(const char *fileName) {69ifstream raw(fileName, ios::binary | ios::in);70if (!raw.is_open())71stop("rstream.getBuf: Error opening file");72raw.seekg(0, ios::end);73streampos fileSize = raw.tellg();74if (fileSize < 0)75stop("rstream.getBuf: Error reading file");76char *buf = new char[fileSize];77raw.seekg(0, ios::beg);78raw.read(buf, fileSize);79return pair<const char *, streamsize>(buf, fileSize);80}8182public:83// construct from a string84rstream(const char *buf, streamsize size)85: _rstream(pair<const char *, streamsize>(buf, size)) {}86// construct from a file whole content is fully read once to initialize the87// content of this stream88rstream(const char *fileName) : _rstream(getBuf(fileName)) {}89rstream &operator>>(int &x) { return doRead(x); }90rstream &operator>>(unsigned &x) { return doRead(x); }91rstream &operator>>(short &x) { return doRead(x); }92rstream &operator>>(unsigned short &x) { return doRead(x); }93rstream &operator>>(Symbol &e) {94read((char *)&e, 18);95return *this;96}97};9899// string table in a .OBJ file100class StringTable {101private:102map<string, unsigned> directory;103size_t length;104char *data;105106// make <directory> from <length> bytes in <data>107void makeDirectory(void) {108unsigned i = 4;109while (i < length) {110string s = string(data + i);111directory.insert(make_pair(s, i));112i += s.size() + 1;113}114}115// initialize <length> and <data> with contents specified by the arguments116void init(const char *_data) {117unsigned _length = *(unsigned *)_data;118119if (_length < sizeof(unsigned) || _length != *(unsigned *)_data)120stop("StringTable.init: Invalid symbol table");121if (_data[_length - 1]) {122// to prevent runaway strings, make sure the data ends with a zero123data = new char[length = _length + 1];124data[_length] = 0;125} else {126data = new char[length = _length];127}128*(unsigned *)data = length;129KMP_MEMCPY(data + sizeof(unsigned), _data + sizeof(unsigned),130length - sizeof(unsigned));131makeDirectory();132}133134public:135StringTable(rstream &f) {136// Construct string table by reading from f.137streampos s;138unsigned strSize;139char *strData;140141s = f.tellg();142f >> strSize;143if (strSize < sizeof(unsigned))144stop("StringTable: Invalid string table");145strData = new char[strSize];146*(unsigned *)strData = strSize;147// read the raw data into <strData>148f.read(strData + sizeof(unsigned), strSize - sizeof(unsigned));149s = f.tellg() - s;150if (s < strSize)151stop("StringTable: Unexpected EOF");152init(strData);153delete[] strData;154}155StringTable(const set<string> &strings) {156// Construct string table from given strings.157char *p;158set<string>::const_iterator it;159size_t s;160161// count required size for data162for (length = sizeof(unsigned), it = strings.begin(); it != strings.end();163++it) {164size_t l = (*it).size();165166if (l > (unsigned)0xFFFFFFFF)167stop("StringTable: String too long");168if (l > 8) {169length += l + 1;170if (length > (unsigned)0xFFFFFFFF)171stop("StringTable: Symbol table too long");172}173}174data = new char[length];175*(unsigned *)data = length;176// populate data and directory177for (p = data + sizeof(unsigned), it = strings.begin(); it != strings.end();178++it) {179const string &str = *it;180size_t l = str.size();181if (l > 8) {182directory.insert(make_pair(str, p - data));183KMP_MEMCPY(p, str.c_str(), l);184p[l] = 0;185p += l + 1;186}187}188}189~StringTable() { delete[] data; }190// Returns encoding for given string based on this string table. Error if191// string length is greater than 8 but string is not in the string table192// -- returns 0.193__int64 encode(const string &str) {194__int64 r;195196if (str.size() <= 8) {197// encoded directly198((char *)&r)[7] = 0;199KMP_STRNCPY_S((char *)&r, sizeof(r), str.c_str(), 8);200return r;201} else {202// represented as index into table203map<string, unsigned>::const_iterator it = directory.find(str);204if (it == directory.end())205stop("StringTable::encode: String now found in string table");206((unsigned *)&r)[0] = 0;207((unsigned *)&r)[1] = (*it).second;208return r;209}210}211// Returns string represented by x based on this string table. Error if x212// references an invalid position in the table--returns the empty string.213string decode(__int64 x) const {214if (*(unsigned *)&x == 0) {215// represented as index into table216unsigned &p = ((unsigned *)&x)[1];217if (p >= length)218stop("StringTable::decode: Invalid string table lookup");219return string(data + p);220} else {221// encoded directly222char *p = (char *)&x;223int i;224225for (i = 0; i < 8 && p[i]; ++i)226;227return string(p, i);228}229}230void write(ostream &os) { os.write(data, length); }231};232233// for the named object file, determines the set of defined symbols and the set234// of undefined external symbols and writes them to <defined> and <undefined>235// respectively236void computeExternalSymbols(const char *fileName, set<string> *defined,237set<string> *undefined) {238streampos fileSize;239size_t strTabStart;240unsigned symTabStart, symNEntries;241rstream f(fileName);242243f.seekg(0, ios::end);244fileSize = f.tellg();245246f.seekg(8);247f >> symTabStart >> symNEntries;248// seek to the string table249f.seekg(strTabStart = symTabStart + 18 * (size_t)symNEntries);250if (f.eof()) {251printf("computeExternalSymbols: fileName='%s', fileSize = %lu, symTabStart "252"= %u, symNEntries = %u\n",253fileName, (unsigned long)fileSize, symTabStart, symNEntries);254stop("computeExternalSymbols: Unexpected EOF 1");255}256StringTable stringTable(f); // read the string table257if (f.tellg() != fileSize)258stop("computeExternalSymbols: Unexpected data after string table");259260f.clear();261f.seekg(symTabStart); // seek to the symbol table262263defined->clear();264undefined->clear();265for (int i = 0; i < symNEntries; ++i) {266// process each entry267Symbol e;268269if (f.eof())270stop("computeExternalSymbols: Unexpected EOF 2");271f >> e;272if (f.fail())273stop("computeExternalSymbols: File read error");274if (e.nAux) { // auxiliary entry: skip275f.seekg(e.nAux * 18, ios::cur);276i += e.nAux;277}278// if symbol is extern and defined in the current file, insert it279if (e.storageClass == 2)280if (e.sectionNum)281defined->insert(stringTable.decode(e.name));282else283undefined->insert(stringTable.decode(e.name));284}285}286287// For each occurrence of an external symbol in the object file named by288// by <fileName> that is a member of <hide>, renames it by prefixing289// with "__kmp_external_", writing back the file in-place290void hideSymbols(char *fileName, const set<string> &hide) {291static const string prefix("__kmp_external_");292set<string> strings; // set of all occurring symbols, appropriately prefixed293streampos fileSize;294size_t strTabStart;295unsigned symTabStart, symNEntries;296int i;297rstream in(fileName);298299in.seekg(0, ios::end);300fileSize = in.tellg();301302in.seekg(8);303in >> symTabStart >> symNEntries;304in.seekg(strTabStart = symTabStart + 18 * (size_t)symNEntries);305if (in.eof())306stop("hideSymbols: Unexpected EOF");307StringTable stringTableOld(in); // read original string table308309if (in.tellg() != fileSize)310stop("hideSymbols: Unexpected data after string table");311312// compute set of occurring strings with prefix added313for (i = 0; i < symNEntries; ++i) {314Symbol e;315316in.seekg(symTabStart + i * 18);317if (in.eof())318stop("hideSymbols: Unexpected EOF");319in >> e;320if (in.fail())321stop("hideSymbols: File read error");322if (e.nAux)323i += e.nAux;324const string &s = stringTableOld.decode(e.name);325// if symbol is extern and found in <hide>, prefix and insert into strings,326// otherwise, just insert into strings without prefix327strings.insert(328(e.storageClass == 2 && hide.find(s) != hide.end()) ? prefix + s : s);329}330331ofstream out(fileName, ios::trunc | ios::out | ios::binary);332if (!out.is_open())333stop("hideSymbols: Error opening output file");334335// make new string table from string set336StringTable stringTableNew = StringTable(strings);337338// copy input file to output file up to just before the symbol table339in.seekg(0);340char *buf = new char[symTabStart];341in.read(buf, symTabStart);342out.write(buf, symTabStart);343delete[] buf;344345// copy input symbol table to output symbol table with name translation346for (i = 0; i < symNEntries; ++i) {347Symbol e;348349in.seekg(symTabStart + i * 18);350if (in.eof())351stop("hideSymbols: Unexpected EOF");352in >> e;353if (in.fail())354stop("hideSymbols: File read error");355const string &s = stringTableOld.decode(e.name);356out.seekp(symTabStart + i * 18);357e.name = stringTableNew.encode(358(e.storageClass == 2 && hide.find(s) != hide.end()) ? prefix + s : s);359out.write((char *)&e, 18);360if (out.fail())361stop("hideSymbols: File write error");362if (e.nAux) {363// copy auxiliary symbol table entries364int nAux = e.nAux;365for (int j = 1; j <= nAux; ++j) {366in >> e;367out.seekp(symTabStart + (i + j) * 18);368out.write((char *)&e, 18);369}370i += nAux;371}372}373// output string table374stringTableNew.write(out);375}376377// returns true iff <a> and <b> have no common element378template <class T> bool isDisjoint(const set<T> &a, const set<T> &b) {379set<T>::const_iterator ita, itb;380381for (ita = a.begin(), itb = b.begin(); ita != a.end() && itb != b.end();) {382const T &ta = *ita, &tb = *itb;383if (ta < tb)384++ita;385else if (tb < ta)386++itb;387else388return false;389}390return true;391}392393// PRE: <defined> and <undefined> are arrays with <nTotal> elements where394// <nTotal> >= <nExternal>. The first <nExternal> elements correspond to the395// external object files and the rest correspond to the internal object files.396// POST: file x is said to depend on file y if undefined[x] and defined[y] are397// not disjoint. Returns the transitive closure of the set of internal object398// files, as a set of file indexes, under the 'depends on' relation, minus the399// set of internal object files.400set<int> *findRequiredExternal(int nExternal, int nTotal, set<string> *defined,401set<string> *undefined) {402set<int> *required = new set<int>;403set<int> fresh[2];404int i, cur = 0;405bool changed;406407for (i = nTotal - 1; i >= nExternal; --i)408fresh[cur].insert(i);409do {410changed = false;411for (set<int>::iterator it = fresh[cur].begin(); it != fresh[cur].end();412++it) {413set<string> &s = undefined[*it];414415for (i = 0; i < nExternal; ++i) {416if (required->find(i) == required->end()) {417if (!isDisjoint(defined[i], s)) {418// found a new qualifying element419required->insert(i);420fresh[1 - cur].insert(i);421changed = true;422}423}424}425}426fresh[cur].clear();427cur = 1 - cur;428} while (changed);429return required;430}431432int main(int argc, char **argv) {433int nExternal, nInternal, i;434set<string> *defined, *undefined;435set<int>::iterator it;436437if (argc < 3)438stop("Please specify a positive integer followed by a list of object "439"filenames");440nExternal = atoi(argv[1]);441if (nExternal <= 0)442stop("Please specify a positive integer followed by a list of object "443"filenames");444if (nExternal + 2 > argc)445stop("Too few external objects");446nInternal = argc - nExternal - 2;447defined = new set<string>[argc - 2];448undefined = new set<string>[argc - 2];449450// determine the set of defined and undefined external symbols451for (i = 2; i < argc; ++i)452computeExternalSymbols(argv[i], defined + i - 2, undefined + i - 2);453454// determine the set of required external files455set<int> *requiredExternal =456findRequiredExternal(nExternal, argc - 2, defined, undefined);457set<string> hide;458459// determine the set of symbols to hide--namely defined external symbols of460// the required external files461for (it = requiredExternal->begin(); it != requiredExternal->end(); ++it) {462int idx = *it;463set<string>::iterator it2;464// We have to insert one element at a time instead of inserting a range465// because the insert member function taking a range doesn't exist on466// Windows* OS, at least at the time of this writing.467for (it2 = defined[idx].begin(); it2 != defined[idx].end(); ++it2)468hide.insert(*it2);469}470471// process the external files--removing those that are not required and hiding472// the appropriate symbols in the others473for (i = 0; i < nExternal; ++i)474if (requiredExternal->find(i) != requiredExternal->end())475hideSymbols(argv[2 + i], hide);476else477remove(argv[2 + i]);478// hide the appropriate symbols in the internal files479for (i = nExternal + 2; i < argc; ++i)480hideSymbols(argv[i], hide);481return 0;482}483484485