Skip to content

Commit

Permalink
[PDB] Fix bug when using multiple PCH header objects with the same name.
Browse files Browse the repository at this point in the history
A common pattern in Windows is to have all your precompiled headers
use an object named stdafx.obj.  If you've got a project with many
different static libs, you might use a separate PCH for each one of
these.

During the final link step, a file from A might reference the PCH
object from A, but it will have the same name (stdafx.obj) as any
other PCH from another project.  The only difference will be the
path.  For example, A might be A/stdafx.obj while B is B/stdafx.obj.

The existing algorithm checks only the filename that was passed on
the command line (or stored in archive), but this is insufficient in
the case where relative paths are used, because depending on the
command line object file / library order, it might find the wrong
PCH object first resulting in a signature mismatch.

The fix here is to simply check whether the absolute path of the
PCH object (which is stored in the input obj file for the file that
references the PCH) *ends with* the full relative path of whatever
is specified on the command line (or is in the archive).

Differential Revision: https://reviews.llvm.org/D66431

llvm-svn: 374442
  • Loading branch information
Zachary Turner committed Oct 10, 2019
1 parent 3620e8f commit 02c5386
Show file tree
Hide file tree
Showing 9 changed files with 50 additions and 22 deletions.
26 changes: 9 additions & 17 deletions lld/COFF/PDB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,16 +514,15 @@ static bool equals_path(StringRef path1, StringRef path2) {
return path1.equals(path2);
#endif
}

// Find by name an OBJ provided on the command line
static ObjFile *findObjByName(StringRef fileNameOnly) {
SmallString<128> currentPath;

static ObjFile *findObjWithPrecompSignature(StringRef fileNameOnly,
uint32_t precompSignature) {
for (ObjFile *f : ObjFile::instances) {
StringRef currentFileName = sys::path::filename(f->getName());

// Compare based solely on the file name (link.exe behavior)
if (equals_path(currentFileName, fileNameOnly))
if (f->pchSignature.hasValue() &&
f->pchSignature.getValue() == precompSignature &&
equals_path(fileNameOnly, currentFileName))
return f;
}
return nullptr;
Expand Down Expand Up @@ -560,22 +559,15 @@ Expected<const CVIndexMap &> PDBLinker::aquirePrecompObj(ObjFile *file) {

// link.exe requires that a precompiled headers object must always be provided
// on the command-line, even if that's not necessary.
auto precompFile = findObjByName(precompFileName);
auto precompFile =
findObjWithPrecompSignature(precompFileName, precomp.Signature);
if (!precompFile)
return createFileError(
precompFileName.str(),
make_error<pdb::PDBError>(pdb::pdb_error_code::external_cmdline_ref));
precomp.getPrecompFilePath().str(),
make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));

addObjFile(precompFile, &indexMap);

if (!precompFile->pchSignature)
fatal(precompFile->getName() + " is not a precompiled headers object");

if (precomp.getSignature() != precompFile->pchSignature.getValueOr(0))
return createFileError(
precomp.getPrecompFilePath().str(),
make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));

return indexMap;
}

Expand Down
Binary file added lld/test/COFF/Inputs/precompa/precomp.obj
Binary file not shown.
Binary file added lld/test/COFF/Inputs/precompa/useprecomp.obj
Binary file not shown.
Binary file added lld/test/COFF/Inputs/precompb/precomp.obj
Binary file not shown.
Binary file added lld/test/COFF/Inputs/precompb/useprecomp.obj
Binary file not shown.
36 changes: 36 additions & 0 deletions lld/test/COFF/precomp-link-samename.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
RUN: lld-link %S/Inputs/precompb/useprecomp.obj %S/Inputs/precompa/precomp.obj %S/Inputs/precompb/precomp.obj \
RUN: %S/Inputs/precompa/useprecomp.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe \
RUN: /summary | FileCheck %s -check-prefix SUMMARY

RUN: llvm-pdbutil dump -types %t.pdb | FileCheck %s


CHECK: Types (TPI Stream)
CHECK-NOT: LF_PRECOMP
CHECK-NOT: LF_ENDPRECOMP


SUMMARY: Summary
SUMMARY-NEXT: --------------------------------------------------------------------------------
SUMMARY-NEXT: 4 Input OBJ files (expanded from all cmd-line inputs)
SUMMARY-NEXT: 0 PDB type server dependencies
SUMMARY-NEXT: 2 Precomp OBJ dependencies

// precompa/precomp.cpp
#include "precomp.h"

// precompa/useprecomp.cpp
#include "precomp.h"
int main(int argc, char **argv) { return 0; }

// precompa/precomp.h
int precompa_symbol = 42;

// precompb/precomp.cpp
#include "precomp.h"

// precompb/useprecomp.cpp
#include "precomp.h"

// precompb/precomp.h
int precompb_symbol = 142;
4 changes: 2 additions & 2 deletions lld/test/COFF/precomp-link.test
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-invalid.obj %S/Inputs/pr
RUN: not lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf 2>&1 | FileCheck %s -check-prefix FAILURE-MISSING-PRECOMPOBJ

FAILURE: warning: Cannot use debug info for '{{.*}}precomp-invalid.obj' [LNK4099]
FAILURE-NEXT: failed to load reference '{{.*}}precomp.obj': The signature does not match; the file(s) might be out of date.
FAILURE-NEXT: failed to load reference '{{.*}}precomp.obj': No matching precompiled header could be located.

FAILURE-MISSING-PRECOMPOBJ: warning: Cannot use debug info for '{{.*}}precomp-a.obj' [LNK4099]
FAILURE-MISSING-PRECOMPOBJ-NEXT: failed to load reference '{{.*}}precomp.obj': The path to this file must be provided on the command-line
FAILURE-MISSING-PRECOMPOBJ-NEXT: failed to load reference '{{.*}}precomp.obj': No matching precompiled header could be located.

CHECK: Types (TPI Stream)
CHECK-NOT: LF_PRECOMP
Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/DebugInfo/PDB/GenericError.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ enum class pdb_error_code {
dia_sdk_not_present,
dia_failed_loading,
signature_out_of_date,
external_cmdline_ref,
no_matching_pch,
unspecified,
};
} // namespace pdb
Expand Down
4 changes: 2 additions & 2 deletions llvm/lib/DebugInfo/PDB/GenericError.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ class PDBErrorCategory : public std::error_category {
return "The PDB file path is an invalid UTF8 sequence.";
case pdb_error_code::signature_out_of_date:
return "The signature does not match; the file(s) might be out of date.";
case pdb_error_code::external_cmdline_ref:
return "The path to this file must be provided on the command-line.";
case pdb_error_code::no_matching_pch:
return "No matching precompiled header could be located.";
}
llvm_unreachable("Unrecognized generic_error_code");
}
Expand Down

0 comments on commit 02c5386

Please sign in to comment.