Skip to content

[llvm-dlltool] Implement the --identify option #127465

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 145 additions & 1 deletion llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "llvm/ToolDrivers/llvm-dlltool/DlltoolDriver.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/COFFModuleDefinition.h"
Expand Down Expand Up @@ -158,6 +159,143 @@ bool parseModuleDefinition(StringRef DefFileName, MachineTypes Machine,
return true;
}

int printError(llvm::Error E, Twine File) {
if (!E)
return 0;
handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) {
llvm::errs() << "error opening " << File << ": " << EIB.message() << "\n";
});
return 1;
}

template <typename Callable>
int forEachCoff(object::Archive &Archive, StringRef Name, Callable Callback) {
Error Err = Error::success();
for (auto &C : Archive.children(Err)) {
Expected<StringRef> NameOrErr = C.getName();
if (!NameOrErr)
return printError(NameOrErr.takeError(), Name);
StringRef Name = *NameOrErr;

Expected<MemoryBufferRef> ChildMB = C.getMemoryBufferRef();
if (!ChildMB)
return printError(ChildMB.takeError(), Name);

if (identify_magic(ChildMB->getBuffer()) == file_magic::coff_object) {
auto Obj = object::COFFObjectFile::create(*ChildMB);
if (!Obj)
return printError(Obj.takeError(), Name);
if (!Callback(*Obj->get(), Name))
return 1;
}
}
if (Err)
return printError(std::move(Err), Name);
return 0;
}

// To find the named of the imported DLL from an import library, we can either
// inspect the object files that form the import table entries, or we could
// just look at the archive member names, for MSVC style import libraries.
// Looking at the archive member names doesn't work for GNU style import
// libraries though, while inspecting the import table entries works for
// both. (MSVC style import libraries contain a couple regular object files
// for the header/trailers.)
//
// This implementation does the same as GNU dlltool does; look at the
// content of ".idata$7" sections, or for MSVC style libraries, look
// at ".idata$6" sections.
//
// For GNU style import libraries, there are also other data chunks in sections
// named ".idata$7" (entries to the IAT or ILT); these are distinguished
// by seeing that they contain relocations. (They also look like an empty
// string when looking for null termination.)
//
// Alternatively, we could do things differently - look for any .idata$2
// section; this would be import directory entries. At offset 0xc in them
// there is the RVA of the import DLL name; look for a relocation at this
// spot and locate the symbol that it points at. That symbol may either
// be within the same object file (in the case of MSVC style import libraries)
// or another object file (in the case of GNU import libraries).
bool identifyImportName(const COFFObjectFile &Obj, StringRef ObjName,
std::vector<StringRef> &Names, bool IsMsStyleImplib) {
StringRef TargetName = IsMsStyleImplib ? ".idata$6" : ".idata$7";
for (const auto &S : Obj.sections()) {
Expected<StringRef> NameOrErr = S.getName();
if (!NameOrErr) {
printError(NameOrErr.takeError(), ObjName);
return false;
}
StringRef Name = *NameOrErr;
if (Name != TargetName)
continue;

// GNU import libraries contain .idata$7 section in the per function
// objects too, but they contain relocations.
if (!IsMsStyleImplib && !S.relocations().empty())
continue;

Expected<StringRef> ContentsOrErr = S.getContents();
if (!ContentsOrErr) {
printError(ContentsOrErr.takeError(), ObjName);
return false;
}
StringRef Contents = *ContentsOrErr;
Contents = Contents.substr(0, Contents.find('\0'));
if (Contents.empty())
continue;
Names.push_back(Contents);
return true;
}
return true;
}

int doIdentify(StringRef File, bool IdentifyStrict) {
ErrorOr<std::unique_ptr<MemoryBuffer>> MaybeBuf = MemoryBuffer::getFile(
File, /*IsText=*/false, /*RequiredNullTerminator=*/false);
if (!MaybeBuf)
return printError(errorCodeToError(MaybeBuf.getError()), File);
if (identify_magic(MaybeBuf.get()->getBuffer()) != file_magic::archive) {
llvm::errs() << File << " is not a library\n";
return 1;
}

std::unique_ptr<MemoryBuffer> B = std::move(MaybeBuf.get());
Error Err = Error::success();
object::Archive Archive(B->getMemBufferRef(), Err);
if (Err)
return printError(std::move(Err), B->getBufferIdentifier());

bool IsMsStyleImplib = false;
for (const auto &S : Archive.symbols()) {
if (S.getName() == "__NULL_IMPORT_DESCRIPTOR") {
IsMsStyleImplib = true;
break;
}
}
std::vector<StringRef> Names;
if (forEachCoff(Archive, B->getBufferIdentifier(),
[&](const COFFObjectFile &Obj, StringRef ObjName) -> bool {
return identifyImportName(Obj, ObjName, Names,
IsMsStyleImplib);
}))
return 1;

if (Names.empty()) {
llvm::errs() << "No DLL import name found in " << File << "\n";
return 1;
}
if (Names.size() > 1 && IdentifyStrict) {
llvm::errs() << File << "contains imports for two or more DLLs\n";
return 1;
}

for (StringRef S : Names)
llvm::outs() << S << "\n";

return 0;
}

} // namespace

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

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

if (Args.hasArg(OPT_I)) {
return doIdentify(Args.getLastArg(OPT_I)->getValue(),
Args.hasArg(OPT_identify_strict));
}

if (!Args.hasArg(OPT_d)) {
llvm::errs() << "no definition file specified\n";
return 1;
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/ToolDrivers/llvm-dlltool/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ def k_alias: Flag<["--"], "kill-at">, Alias<k>;
def no_leading_underscore: Flag<["--"], "no-leading-underscore">,
HelpText<"Don't add leading underscores on symbols">;

def I: JoinedOrSeparate<["-"], "I">, HelpText<"Identify DLL name from import library">;
def I_long : JoinedOrSeparate<["--"], "identify">, Alias<I>;

def identify_strict : Flag<["--"], "identify-strict">, HelpText<"Error out if the --identify option detects more than one DLL">;

//==============================================================================
// The flags below do nothing. They are defined only for dlltool compatibility.
//==============================================================================
Expand Down
133 changes: 133 additions & 0 deletions llvm/test/tools/llvm-dlltool/Inputs/gnu_foo_lib_h.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
--- !COFF
header:
Machine: IMAGE_FILE_MACHINE_I386
Characteristics: [ IMAGE_FILE_LINE_NUMS_STRIPPED, IMAGE_FILE_32BIT_MACHINE ]
sections:
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: ''
- Name: .data
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
Alignment: 4
SectionData: ''
- Name: .bss
Characteristics: [ IMAGE_SCN_CNT_UNINITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
Alignment: 4
SectionData: ''
- Name: '.idata$2'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
Alignment: 4
SectionData: '0000000000000000000000000000000000000000'
SizeOfRawData: 20
Relocations:
- VirtualAddress: 0
SymbolName: '.idata$4'
Type: IMAGE_REL_I386_DIR32NB
- VirtualAddress: 12
SymbolName: __foo_lib_iname
Type: IMAGE_REL_I386_DIR32NB
- VirtualAddress: 16
SymbolName: '.idata$5'
Type: IMAGE_REL_I386_DIR32NB
- Name: '.idata$5'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
Alignment: 4
SectionData: ''
- Name: '.idata$4'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
Alignment: 4
SectionData: ''
symbols:
- Name: .file
Value: 0
SectionNumber: -2
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_FILE
File: fake
- Name: hname
Value: 0
SectionNumber: 6
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- Name: fthunk
Value: 0
SectionNumber: 5
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- Name: .text
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 0
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 0
- Name: .data
Value: 0
SectionNumber: 2
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 0
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 0
- Name: .bss
Value: 0
SectionNumber: 3
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 0
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 0
- Name: '.idata$2'
Value: 0
SectionNumber: 4
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 20
NumberOfRelocations: 3
NumberOfLinenumbers: 0
CheckSum: 0
Number: 0
- Name: '.idata$4'
Value: 0
SectionNumber: 6
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- Name: '.idata$5'
Value: 0
SectionNumber: 5
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- Name: __head_foo_lib
Value: 0
SectionNumber: 4
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: __foo_lib_iname
Value: 0
SectionNumber: 0
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
...
Loading