Skip to content

Commit dcc08a1

Browse files
authored
[llvm-dlltool] Implement the --identify option (#127465)
This option prints the name of the DLL that gets imported, when linking against an import library. This is implemented using the same strategy as GNU dlltool does; looking for the contents of .idata$6 or .idata$7 chunks. The right section name to check for is chosen by identifying whether the library is GNU or LLVM style. In the case of GNU import libraries, the DLL name is in an .idata$7 chunk. However there are also other chunks with that section name (for entries for the IAT or ILT); identify these by looking for whether a chunk contains relocations. Alternatively, one could also just look for .idata$2 chunks, look for relocations at the right offset, and locate data at the symbol that the relocation points at (which may be in the same or in another object file).
1 parent 74c6111 commit dcc08a1

File tree

9 files changed

+697
-1
lines changed

9 files changed

+697
-1
lines changed

llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
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,143 @@ 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+
int doIdentify(StringRef File, bool IdentifyStrict) {
254+
ErrorOr<std::unique_ptr<MemoryBuffer>> MaybeBuf = MemoryBuffer::getFile(
255+
File, /*IsText=*/false, /*RequiredNullTerminator=*/false);
256+
if (!MaybeBuf)
257+
return printError(errorCodeToError(MaybeBuf.getError()), File);
258+
if (identify_magic(MaybeBuf.get()->getBuffer()) != file_magic::archive) {
259+
llvm::errs() << File << " is not a library\n";
260+
return 1;
261+
}
262+
263+
std::unique_ptr<MemoryBuffer> B = std::move(MaybeBuf.get());
264+
Error Err = Error::success();
265+
object::Archive Archive(B->getMemBufferRef(), Err);
266+
if (Err)
267+
return printError(std::move(Err), B->getBufferIdentifier());
268+
269+
bool IsMsStyleImplib = false;
270+
for (const auto &S : Archive.symbols()) {
271+
if (S.getName() == "__NULL_IMPORT_DESCRIPTOR") {
272+
IsMsStyleImplib = true;
273+
break;
274+
}
275+
}
276+
std::vector<StringRef> Names;
277+
if (forEachCoff(Archive, B->getBufferIdentifier(),
278+
[&](const COFFObjectFile &Obj, StringRef ObjName) -> bool {
279+
return identifyImportName(Obj, ObjName, Names,
280+
IsMsStyleImplib);
281+
}))
282+
return 1;
283+
284+
if (Names.empty()) {
285+
llvm::errs() << "No DLL import name found in " << File << "\n";
286+
return 1;
287+
}
288+
if (Names.size() > 1 && IdentifyStrict) {
289+
llvm::errs() << File << "contains imports for two or more DLLs\n";
290+
return 1;
291+
}
292+
293+
for (StringRef S : Names)
294+
llvm::outs() << S << "\n";
295+
296+
return 0;
297+
}
298+
161299
} // namespace
162300

163301
int llvm::dlltoolDriverMain(llvm::ArrayRef<const char *> ArgsArr) {
@@ -173,7 +311,8 @@ int llvm::dlltoolDriverMain(llvm::ArrayRef<const char *> ArgsArr) {
173311

174312
// Handle when no input or output is specified
175313
if (Args.hasArgNoClaim(OPT_INPUT) ||
176-
(!Args.hasArgNoClaim(OPT_d) && !Args.hasArgNoClaim(OPT_l))) {
314+
(!Args.hasArgNoClaim(OPT_d) && !Args.hasArgNoClaim(OPT_l) &&
315+
!Args.hasArgNoClaim(OPT_I))) {
177316
Table.printHelp(outs(), "llvm-dlltool [options] file...", "llvm-dlltool",
178317
false);
179318
llvm::outs()
@@ -185,6 +324,11 @@ int llvm::dlltoolDriverMain(llvm::ArrayRef<const char *> ArgsArr) {
185324
llvm::errs() << "ignoring unknown argument: " << Arg->getAsString(Args)
186325
<< "\n";
187326

327+
if (Args.hasArg(OPT_I)) {
328+
return doIdentify(Args.getLastArg(OPT_I)->getValue(),
329+
Args.hasArg(OPT_identify_strict));
330+
}
331+
188332
if (!Args.hasArg(OPT_d)) {
189333
llvm::errs() << "no definition file specified\n";
190334
return 1;

llvm/lib/ToolDrivers/llvm-dlltool/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ def k_alias: Flag<["--"], "kill-at">, Alias<k>;
2121
def no_leading_underscore: Flag<["--"], "no-leading-underscore">,
2222
HelpText<"Don't add leading underscores on symbols">;
2323

24+
def I: JoinedOrSeparate<["-"], "I">, HelpText<"Identify DLL name from import library">;
25+
def I_long : JoinedOrSeparate<["--"], "identify">, Alias<I>;
26+
27+
def identify_strict : Flag<["--"], "identify-strict">, HelpText<"Error out if the --identify option detects more than one DLL">;
28+
2429
//==============================================================================
2530
// The flags below do nothing. They are defined only for dlltool compatibility.
2631
//==============================================================================
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
--- !COFF
2+
header:
3+
Machine: IMAGE_FILE_MACHINE_I386
4+
Characteristics: [ IMAGE_FILE_LINE_NUMS_STRIPPED, IMAGE_FILE_32BIT_MACHINE ]
5+
sections:
6+
- Name: .text
7+
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
8+
Alignment: 4
9+
SectionData: ''
10+
- Name: .data
11+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
12+
Alignment: 4
13+
SectionData: ''
14+
- Name: .bss
15+
Characteristics: [ IMAGE_SCN_CNT_UNINITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
16+
Alignment: 4
17+
SectionData: ''
18+
- Name: '.idata$2'
19+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
20+
Alignment: 4
21+
SectionData: '0000000000000000000000000000000000000000'
22+
SizeOfRawData: 20
23+
Relocations:
24+
- VirtualAddress: 0
25+
SymbolName: '.idata$4'
26+
Type: IMAGE_REL_I386_DIR32NB
27+
- VirtualAddress: 12
28+
SymbolName: __foo_lib_iname
29+
Type: IMAGE_REL_I386_DIR32NB
30+
- VirtualAddress: 16
31+
SymbolName: '.idata$5'
32+
Type: IMAGE_REL_I386_DIR32NB
33+
- Name: '.idata$5'
34+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
35+
Alignment: 4
36+
SectionData: ''
37+
- Name: '.idata$4'
38+
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
39+
Alignment: 4
40+
SectionData: ''
41+
symbols:
42+
- Name: .file
43+
Value: 0
44+
SectionNumber: -2
45+
SimpleType: IMAGE_SYM_TYPE_NULL
46+
ComplexType: IMAGE_SYM_DTYPE_NULL
47+
StorageClass: IMAGE_SYM_CLASS_FILE
48+
File: fake
49+
- Name: hname
50+
Value: 0
51+
SectionNumber: 6
52+
SimpleType: IMAGE_SYM_TYPE_NULL
53+
ComplexType: IMAGE_SYM_DTYPE_NULL
54+
StorageClass: IMAGE_SYM_CLASS_STATIC
55+
- Name: fthunk
56+
Value: 0
57+
SectionNumber: 5
58+
SimpleType: IMAGE_SYM_TYPE_NULL
59+
ComplexType: IMAGE_SYM_DTYPE_NULL
60+
StorageClass: IMAGE_SYM_CLASS_STATIC
61+
- Name: .text
62+
Value: 0
63+
SectionNumber: 1
64+
SimpleType: IMAGE_SYM_TYPE_NULL
65+
ComplexType: IMAGE_SYM_DTYPE_NULL
66+
StorageClass: IMAGE_SYM_CLASS_STATIC
67+
SectionDefinition:
68+
Length: 0
69+
NumberOfRelocations: 0
70+
NumberOfLinenumbers: 0
71+
CheckSum: 0
72+
Number: 0
73+
- Name: .data
74+
Value: 0
75+
SectionNumber: 2
76+
SimpleType: IMAGE_SYM_TYPE_NULL
77+
ComplexType: IMAGE_SYM_DTYPE_NULL
78+
StorageClass: IMAGE_SYM_CLASS_STATIC
79+
SectionDefinition:
80+
Length: 0
81+
NumberOfRelocations: 0
82+
NumberOfLinenumbers: 0
83+
CheckSum: 0
84+
Number: 0
85+
- Name: .bss
86+
Value: 0
87+
SectionNumber: 3
88+
SimpleType: IMAGE_SYM_TYPE_NULL
89+
ComplexType: IMAGE_SYM_DTYPE_NULL
90+
StorageClass: IMAGE_SYM_CLASS_STATIC
91+
SectionDefinition:
92+
Length: 0
93+
NumberOfRelocations: 0
94+
NumberOfLinenumbers: 0
95+
CheckSum: 0
96+
Number: 0
97+
- Name: '.idata$2'
98+
Value: 0
99+
SectionNumber: 4
100+
SimpleType: IMAGE_SYM_TYPE_NULL
101+
ComplexType: IMAGE_SYM_DTYPE_NULL
102+
StorageClass: IMAGE_SYM_CLASS_STATIC
103+
SectionDefinition:
104+
Length: 20
105+
NumberOfRelocations: 3
106+
NumberOfLinenumbers: 0
107+
CheckSum: 0
108+
Number: 0
109+
- Name: '.idata$4'
110+
Value: 0
111+
SectionNumber: 6
112+
SimpleType: IMAGE_SYM_TYPE_NULL
113+
ComplexType: IMAGE_SYM_DTYPE_NULL
114+
StorageClass: IMAGE_SYM_CLASS_STATIC
115+
- Name: '.idata$5'
116+
Value: 0
117+
SectionNumber: 5
118+
SimpleType: IMAGE_SYM_TYPE_NULL
119+
ComplexType: IMAGE_SYM_DTYPE_NULL
120+
StorageClass: IMAGE_SYM_CLASS_STATIC
121+
- Name: __head_foo_lib
122+
Value: 0
123+
SectionNumber: 4
124+
SimpleType: IMAGE_SYM_TYPE_NULL
125+
ComplexType: IMAGE_SYM_DTYPE_NULL
126+
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
127+
- Name: __foo_lib_iname
128+
Value: 0
129+
SectionNumber: 0
130+
SimpleType: IMAGE_SYM_TYPE_NULL
131+
ComplexType: IMAGE_SYM_DTYPE_NULL
132+
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
133+
...

0 commit comments

Comments
 (0)