1212
1313#include " llvm/ToolDrivers/llvm-dlltool/DlltoolDriver.h"
1414#include " llvm/ADT/StringSwitch.h"
15+ #include " llvm/Object/Archive.h"
1516#include " llvm/Object/COFF.h"
1617#include " llvm/Object/COFFImportFile.h"
1718#include " llvm/Object/COFFModuleDefinition.h"
@@ -158,6 +159,169 @@ bool parseModuleDefinition(StringRef DefFileName, MachineTypes Machine,
158159 return true ;
159160}
160161
162+ int printError (llvm::Error E, Twine File) {
163+ if (!E)
164+ return 0 ;
165+ handleAllErrors (std::move (E), [&](const llvm::ErrorInfoBase &EIB) {
166+ llvm::errs () << " error opening " << File << " : " << EIB.message () << " \n " ;
167+ });
168+ return 1 ;
169+ }
170+
171+ template <typename Callable>
172+ int forEachCoff (object::Archive &Archive, StringRef Name, Callable Callback) {
173+ Error Err = Error::success ();
174+ for (auto &C : Archive.children (Err)) {
175+ Expected<StringRef> NameOrErr = C.getName ();
176+ if (!NameOrErr)
177+ return printError (NameOrErr.takeError (), Name);
178+ StringRef Name = *NameOrErr;
179+
180+ Expected<MemoryBufferRef> ChildMB = C.getMemoryBufferRef ();
181+ if (!ChildMB)
182+ return printError (ChildMB.takeError (), Name);
183+
184+ if (identify_magic (ChildMB->getBuffer ()) == file_magic::coff_object) {
185+ auto Obj = object::COFFObjectFile::create (*ChildMB);
186+ if (!Obj)
187+ return printError (Obj.takeError (), Name);
188+ if (!Callback (*Obj->get (), Name))
189+ return 1 ;
190+ }
191+ }
192+ if (Err)
193+ return printError (std::move (Err), Name);
194+ return 0 ;
195+ }
196+
197+ // To find the named of the imported DLL from an import library, we can either
198+ // inspect the object files that form the import table entries, or we could
199+ // just look at the archive member names, for MSVC style import libraries.
200+ // Looking at the archive member names doesn't work for GNU style import
201+ // libraries though, while inspecting the import table entries works for
202+ // both. (MSVC style import libraries contain a couple regular object files
203+ // for the header/trailers.)
204+ //
205+ // This implementation does the same as GNU dlltool does; look at the
206+ // content of ".idata$7" sections, or for MSVC style libraries, look
207+ // at ".idata$6$" sections.
208+ //
209+ // For GNU style import libraries, there are also other data chunks in sections
210+ // named ".idata$7" (entries to the IAT or ILT); these are distinguished
211+ // by seeing that they contain relocations. (They also look like an empty
212+ // string when looking for null termination.)
213+ //
214+ // Alternatively, we could do things differently - look for any .idata$2
215+ // section; this would be import directory entries. At offset 0xc in them
216+ // there is the RVA of the import DLL name; look for a relocation at this
217+ // spot and locate the symbol that it points at. That symbol may either
218+ // be within the same object file (in the case of MSVC style import libraries)
219+ // or another object file (in the case of GNU import libraries).
220+ bool identifyImportName (const COFFObjectFile &Obj, StringRef ObjName,
221+ std::vector<StringRef> &Names, bool IsMsStyleImplib) {
222+ StringRef TargetName = IsMsStyleImplib ? " .idata$6" : " .idata$7" ;
223+ for (const auto &S : Obj.sections ()) {
224+ Expected<StringRef> NameOrErr = S.getName ();
225+ if (!NameOrErr) {
226+ printError (NameOrErr.takeError (), ObjName);
227+ return false ;
228+ }
229+ StringRef Name = *NameOrErr;
230+ if (Name != TargetName)
231+ continue ;
232+
233+ // GNU import libraries contain .idata$7 section in the per function
234+ // objects too, but they contain relocations.
235+ if (!IsMsStyleImplib && !S.relocations ().empty ())
236+ continue ;
237+
238+ Expected<StringRef> ContentsOrErr = S.getContents ();
239+ if (!ContentsOrErr) {
240+ printError (ContentsOrErr.takeError (), ObjName);
241+ return false ;
242+ }
243+ StringRef Contents = *ContentsOrErr;
244+ Contents = Contents.substr (0 , Contents.find (' \0 ' ));
245+ if (Contents.empty ())
246+ continue ;
247+ Names.push_back (Contents);
248+ return true ;
249+ }
250+ return true ;
251+ }
252+
253+ bool objContainsSymbol (const COFFObjectFile &Obj, StringRef ObjName,
254+ StringRef SymbolName, bool &Contains) {
255+ Contains = false ;
256+ for (const auto &S : Obj.symbols ()) {
257+ Expected<StringRef> NameOrErr = S.getName ();
258+ if (!NameOrErr) {
259+ printError (NameOrErr.takeError (), ObjName);
260+ return false ;
261+ }
262+ StringRef Name = *NameOrErr;
263+ if (Name == SymbolName) {
264+ Contains = true ;
265+ return true ;
266+ }
267+ }
268+ return true ;
269+ }
270+
271+ int doIdentify (StringRef File, bool IdentifyStrict) {
272+ ErrorOr<std::unique_ptr<MemoryBuffer>> MaybeBuf = MemoryBuffer::getFile (
273+ File, /* IsText=*/ false , /* RequiredNullTerminator=*/ false );
274+ if (!MaybeBuf)
275+ return printError (errorCodeToError (MaybeBuf.getError ()), File);
276+ if (identify_magic (MaybeBuf.get ()->getBuffer ()) != file_magic::archive) {
277+ llvm::errs () << File << " is not a library\n " ;
278+ return 1 ;
279+ }
280+
281+ std::unique_ptr<MemoryBuffer> B = std::move (MaybeBuf.get ());
282+ Error Err = Error::success ();
283+ object::Archive Archive (B->getMemBufferRef (), Err);
284+ if (Err)
285+ return printError (std::move (Err), B->getBufferIdentifier ());
286+
287+ bool IsMsStyleImplib = false ;
288+ if (forEachCoff (Archive, B->getBufferIdentifier (),
289+ [&](const COFFObjectFile &Obj, StringRef ObjName) -> bool {
290+ if (IsMsStyleImplib)
291+ return true ;
292+ bool Contains;
293+ if (!objContainsSymbol (
294+ Obj, ObjName, " __NULL_IMPORT_DESCRIPTOR" , Contains))
295+ return false ;
296+ if (Contains)
297+ IsMsStyleImplib = true ;
298+ return true ;
299+ }))
300+ return 1 ;
301+
302+ std::vector<StringRef> Names;
303+ if (forEachCoff (Archive, B->getBufferIdentifier (),
304+ [&](const COFFObjectFile &Obj, StringRef ObjName) -> bool {
305+ return identifyImportName (Obj, ObjName, Names,
306+ IsMsStyleImplib);
307+ }))
308+ return 1 ;
309+
310+ if (Names.empty ()) {
311+ llvm::errs () << " No DLL import name found in " << File << " \n " ;
312+ return 1 ;
313+ }
314+ if (Names.size () > 1 && IdentifyStrict) {
315+ llvm::errs () << File << " contains imports for two or more DLLs\n " ;
316+ return 1 ;
317+ }
318+
319+ for (StringRef S : Names)
320+ llvm::outs () << S << " \n " ;
321+
322+ return 0 ;
323+ }
324+
161325} // namespace
162326
163327int llvm::dlltoolDriverMain (llvm::ArrayRef<const char *> ArgsArr) {
@@ -173,7 +337,8 @@ int llvm::dlltoolDriverMain(llvm::ArrayRef<const char *> ArgsArr) {
173337
174338 // Handle when no input or output is specified
175339 if (Args.hasArgNoClaim (OPT_INPUT) ||
176- (!Args.hasArgNoClaim (OPT_d) && !Args.hasArgNoClaim (OPT_l))) {
340+ (!Args.hasArgNoClaim (OPT_d) && !Args.hasArgNoClaim (OPT_l) &&
341+ !Args.hasArgNoClaim (OPT_I))) {
177342 Table.printHelp (outs (), " llvm-dlltool [options] file..." , " llvm-dlltool" ,
178343 false );
179344 llvm::outs ()
@@ -185,6 +350,11 @@ int llvm::dlltoolDriverMain(llvm::ArrayRef<const char *> ArgsArr) {
185350 llvm::errs () << " ignoring unknown argument: " << Arg->getAsString (Args)
186351 << " \n " ;
187352
353+ if (Args.hasArg (OPT_I)) {
354+ return doIdentify (Args.getLastArg (OPT_I)->getValue (),
355+ Args.hasArg (OPT_identify_strict));
356+ }
357+
188358 if (!Args.hasArg (OPT_d)) {
189359 llvm::errs () << " no definition file specified\n " ;
190360 return 1 ;
0 commit comments