Index: lld/trunk/COFF/Driver.cpp =================================================================== --- lld/trunk/COFF/Driver.cpp +++ lld/trunk/COFF/Driver.cpp @@ -830,6 +830,57 @@ } } +// link.exe replaces each %foo% in AltPath with the contents of environment +// variable foo, and adds the two magic env vars _PDB (expands to the basename +// of pdb's output path) and _EXT (expands to the extension of the output +// binary). +// lld only supports %_PDB% and %_EXT% and warns on references to all other env +// vars. +static void parsePDBAltPath(StringRef AltPath) { + SmallString<128> Buf; + StringRef PDBBasename = + sys::path::filename(Config->PDBPath, sys::path::Style::windows); + StringRef BinaryExtension = + sys::path::extension(Config->OutputFile, sys::path::Style::windows); + if (!BinaryExtension.empty()) + BinaryExtension = BinaryExtension.substr(1); // %_EXT% does not include '.'. + + // Invariant: + // +--------- Cursor ('a...' might be the empty string). + // | +----- FirstMark + // | | +- SecondMark + // v v v + // a...%...%... + size_t Cursor = 0; + while (Cursor < AltPath.size()) { + size_t FirstMark, SecondMark; + if ((FirstMark = AltPath.find('%', Cursor)) == StringRef::npos || + (SecondMark = AltPath.find('%', FirstMark + 1)) == StringRef::npos) { + // Didn't find another full fragment, treat rest of string as literal. + Buf.append(AltPath.substr(Cursor)); + break; + } + + // Found a full fragment. Append text in front of first %, and interpret + // text between first and second % as variable name. + Buf.append(AltPath.substr(Cursor, FirstMark - Cursor)); + StringRef Var = AltPath.substr(FirstMark, SecondMark - FirstMark + 1); + if (Var.equals_lower("%_pdb%")) + Buf.append(PDBBasename); + else if (Var.equals_lower("%_ext%")) + Buf.append(BinaryExtension); + else { + warn("only %_PDB% and %_EXT% supported in /pdbaltpath:, keeping " + + Var + " as literal"); + Buf.append(Var); + } + + Cursor = SecondMark + 1; + } + + Config->PDBAltPath = Buf; +} + void LinkerDriver::link(ArrayRef ArgsArr) { // If the first command line argument is "/lib", link.exe acts like lib.exe. // We call our own implementation of lib.exe that understands bitcode files. @@ -1411,6 +1462,9 @@ // tools won't work correctly if these assumptions are not held. sys::fs::make_absolute(Config->PDBAltPath); sys::path::remove_dots(Config->PDBAltPath); + } else { + // Don't do this earlier, so that Config->OutputFile is ready. + parsePDBAltPath(Config->PDBAltPath); } } Index: lld/trunk/test/COFF/Inputs/empty.yaml =================================================================== --- lld/trunk/test/COFF/Inputs/empty.yaml +++ lld/trunk/test/COFF/Inputs/empty.yaml @@ -0,0 +1,67 @@ +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 31C0C3 + - 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: '' +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 3 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 3963538403 + Number: 1 + - 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: 2 + - 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: 3 + - Name: '@feat.00' + Value: 1 + SectionNumber: -1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: lld/trunk/test/COFF/pdbaltpath.test =================================================================== --- lld/trunk/test/COFF/pdbaltpath.test +++ lld/trunk/test/COFF/pdbaltpath.test @@ -0,0 +1,39 @@ +# RUN: yaml2obj %p/Inputs/empty.yaml > %t.obj + +# RUN: lld-link /entry:main %t.obj /out:%t.exe /debug /pdbaltpath:hello.pdb +# RUN: llvm-readobj -coff-debug-directory %t.exe | FileCheck --check-prefix HELLO %s +# HELLO: PDBFileName: hello.pdb + +# RUN: lld-link /entry:main %t.obj /out:%t.exe /debug /pdbaltpath:%_Pdb% +# RUN: llvm-readobj -coff-debug-directory %t.exe | FileCheck --check-prefix PDBVAR %s +# PDBVAR: PDBFileName: pdbaltpath.test.tmp.pdb + +# RUN: lld-link /entry:main %t.obj /out:%t.exe /debug /pdbaltpath:foo%_ExT%.pdb +# RUN: llvm-readobj -coff-debug-directory %t.exe | FileCheck --check-prefix EXTVAR %s +# EXTVAR: PDBFileName: fooexe.pdb + +# RUN: lld-link /entry:main %t.obj /out:%t.exe /debug /pdbaltpath:%_PDB +# RUN: llvm-readobj -coff-debug-directory %t.exe | FileCheck --check-prefix NOCLOSE %s +# NOCLOSE: PDBFileName: %_PDB + +# RUN: lld-link /entry:main %t.obj /out:%t.exe /debug /pdbaltpath:foo%_PDB +# RUN: llvm-readobj -coff-debug-directory %t.exe | FileCheck --check-prefix NOCLOSE2 %s +# NOCLOSE2: PDBFileName: foo%_PDB + +# RUN: lld-link /entry:main %t.obj /out:%t.exe /debug /pdbaltpath:foo%_PDB%bar%_EXT +# RUN: llvm-readobj -coff-debug-directory %t.exe | FileCheck --check-prefix CLOSEONE %s +# CLOSEONE: PDBFileName: foopdbaltpath.test.tmp.pdbbar%_EXT + +# RUN: lld-link /entry:main %t.obj /out:%t.exe /debug /pdbaltpath:foo%_PDB%bar%_EXT% +# RUN: llvm-readobj -coff-debug-directory %t.exe | FileCheck --check-prefix CLOSETWO %s +# CLOSETWO: PDBFileName: foopdbaltpath.test.tmp.pdbbarexe + +# RUN: lld-link /entry:main %t.obj /out:%t.exe /debug /pdbaltpath:foo%_PDB%bar%_EXT%a +# RUN: llvm-readobj -coff-debug-directory %t.exe | FileCheck --check-prefix CLOSETWO2 %s +# CLOSETWO2: PDBFileName: foopdbaltpath.test.tmp.pdbbarexea + +# RUN: lld-link /entry:main %t.obj /out:%t.exe /debug /pdbaltpath:foo%FoO%bar%r%a 2>&1 | FileCheck --check-prefix UNKNOWN-WARN %s +# RUN: llvm-readobj -coff-debug-directory %t.exe | FileCheck --check-prefix ENVVARS %s +# UNKNOWN-WARN: only %_PDB% and %_EXT% supported in /pdbaltpath:, keeping %FoO% as literal +# UNKNOWN-WARN: only %_PDB% and %_EXT% supported in /pdbaltpath:, keeping %r% as literal +# ENVVARS: PDBFileName: foo%FoO%bar%r%a