Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -42,6 +42,7 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Object/Decompressor.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" @@ -796,6 +797,16 @@ return V; } +static void ensureWritable(StringRef Path) { + if (Path.empty()) + return; + + auto Err = FileOutputBuffer::create(Path, 1); + if (!Err) + error("cannot open output file " + Path + ": " + + Err.getError().message()); +} + // Do actual linking. Note that when this function is called, // all linker scripts have already been parsed. template void LinkerDriver::link(opt::InputArgList &Args) { @@ -819,6 +830,13 @@ if (Config->Entry.empty() && !Config->Relocatable) Config->Entry = (Config->EMachine == EM_MIPS) ? "__start" : "_start"; + // Fail early if the output file is not writable. If a user has a long link, + // e.g. due to a large LTO link, they do not wish to run it and find that it + // failed because there was a mistake in their command-line. + ensureWritable(Config->OutputFile); + if (ErrorCount) + return; + // Handle --trace-symbol. for (auto *Arg : Args.filtered(OPT_trace_symbol)) Symtab.trace(Arg->getValue()); Index: test/ELF/driver.test =================================================================== --- test/ELF/driver.test +++ test/ELF/driver.test @@ -11,7 +11,7 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t # RUN: not ld.lld %t -o /no/such/file 2>&1 | FileCheck -check-prefix=MISSING %s -# MISSING: failed to open /no/such/file +# MISSING: cannot open output file /no/such/file # RUN: ld.lld --help 2>&1 | FileCheck -check-prefix=HELP %s # HELP: USAGE: @@ -48,11 +48,11 @@ ## "--output=foo" is equivalent to "-o foo". # RUN: not ld.lld %t --output=/no/such/file 2>&1 | FileCheck -check-prefix=ERR7 %s -# ERR7: failed to open /no/such/file +# ERR7: cannot open output file /no/such/file ## "-output=foo" is equivalent to "-o utput=foo". # RUN: not ld.lld %t -output=/no/such/file 2>&1 | FileCheck -check-prefix=ERR8 %s -# ERR8: failed to open utput=/no/such/file +# ERR8: cannot open output file utput=/no/such/file .globl _start _start: Index: test/ELF/early-exit-for-bad-paths.s =================================================================== --- test/ELF/early-exit-for-bad-paths.s +++ test/ELF/early-exit-for-bad-paths.s @@ -0,0 +1,25 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o + +# RUN: not ld.lld %t.o -o does_not_exist/output 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=NO-DIR,CHECK +# RUN: not ld.lld %t.o -o %s/dir_is_a_file 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=DIR-IS-FILE,CHECK + +# RUN: echo "OUTPUT(\"does_not_exist/output\")" > %t.script +# RUN: not ld.lld %t.o %t.script 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=NO-DIR,CHECK +# RUN: echo "OUTPUT(\"%s/dir_is_a_file\")" > %t.script +# RUN: not ld.lld %t.o %t.script 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=DIR-IS-FILE,CHECK + +# NO-DIR: error: cannot open output file does_not_exist/output: +# DIR-IS-FILE: error: cannot open output file {{.*}}/dir_is_a_file: + +# We should exit before doing the actual link. If an undefined symbol error is +# discovered we haven't bailed out early as expected. +# CHECK-NOT: undefined_symbol + + .globl _start +_start: + call undefined_symbol