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/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" @@ -529,6 +530,17 @@ return Ret; } +static void ensureWritable(StringRef Path, StringRef FileDecsription) { + if (Path.empty()) + return; + + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::F_Append); + if (EC) + error("cannot write to " + FileDecsription + " " + Path + ": " + + EC.message()); +} + // Initializes Config members by the command line options. void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition); @@ -617,9 +629,17 @@ Config->Emulation = S; } + // Fail early if output files are 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->OptRemarksFilename, "optimization remarks file"); + ensureWritable(Config->OutputFile, "output file"); + if (Args.hasArg(OPT_print_map)) Config->MapFile = "-"; + ensureWritable(Config->MapFile, "map file"); + // --omagic is an option to create old-fashioned executables in which // .text segments are writable. Today, the option is still in use to // create special-purpose programs such as boot loaders. It doesn't 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 write to 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 write to 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 write to 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,56 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o + +# If the file was created by a previous test-run, we need to mark it as writable +# or we will not be able to recreate its contents. However, chmod fails if the +# file does not already exist, so we need to handle either case. +# RUN: chmod 222 %t.readonly || true +# RUN: echo This file is read only > %t.readonly +# RUN: chmod 000 %t.readonly + +# RUN: not ld.lld %t.o -o does_not_exist/output 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=OUTPUT,NO-DIR,CHECK +# RUN: not ld.lld %t.o -o %s/dir_is_a_file 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=OUTPUT,DIR-IS-FILE,CHECK +# RUN: not ld.lld %t.o -o %p 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=OUTPUT,PATH-IS-DIR,CHECK +# RUN: not ld.lld %t.o -o %t.readonly 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=OUTPUT,READ-ONLY,CHECK + +# RUN: not ld.lld %t.o -o %t.elf --Map=does_not_exist/output 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=MAP,NO-DIR,CHECK +# RUN: not ld.lld %t.o -o %t.elf --Map=%s/dir_is_a_file 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=MAP,DIR-IS-FILE,CHECK +# RUN: not ld.lld %t.o -o %t.elf --Map=%p 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=MAP,PATH-IS-DIR,CHECK +# RUN: not ld.lld %t.o -o %t.elf --Map=%t.readonly 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=MAP,READ-ONLY,CHECK + +# RUN: not ld.lld %t.o -o %t.elf -opt-remarks-filename does_not_exist/output 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=OPTREMARKS,NO-DIR,CHECK +# RUN: not ld.lld %t.o -o %t.elf -opt-remarks-filename %s/dir_is_a_file 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=OPTREMARKS,DIR-IS-FILE,CHECK +# RUN: not ld.lld %t.o -o %t.elf -opt-remarks-filename %p 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=OPTREMARKS,PATH-IS-DIR,CHECK +# RUN: not ld.lld %t.o -o %t.elf -opt-remarks-filename %t.readonly 2>&1 | \ +# RUN: FileCheck %s -check-prefixes=OPTREMARKS,READ-ONLY,CHECK + +# OUTPUT: error: cannot write to output file +# MAP: error: cannot write to map file +# OPTREMARKS: error: cannot write to optimization remarks file + +# NO-DIR-SAME: does_not_exist/output: +# DIR-IS-FILE-SAME: {{.*}}/dir_is_a_file: +# PATH-IS-DIR-SAME: {{.*[\\/]}}ELF: +# READ-ONLY-SAME: {{.*}}.readonly: + +# 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 + +# RUN: FileCheck %s -check-prefix=UNCHANGED -input-file=%t.readonly +# UNCHANGED: This file is read only + + .globl _start +_start: + call undefined_symbol Index: test/ELF/map-file.s =================================================================== --- test/ELF/map-file.s +++ test/ELF/map-file.s @@ -8,6 +8,10 @@ // RUN: llvm-ar rc %t4.a %t4.o // RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -M | FileCheck %s // RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -print-map | FileCheck %s +// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -Map=- | FileCheck %s +// -Map=- is equivalent to -print-map. Verify that we do not create a +// link map file "-" due to, for example, checking that "-" is writable. +// RUN: not test -f - // RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -Map=%t.map // RUN: FileCheck %s < %t.map @@ -59,4 +63,4 @@ // RUN: not ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -Map=/ 2>&1 \ // RUN: | FileCheck -check-prefix=FAIL %s -// FAIL: cannot open / +// FAIL: cannot write to map file /