diff --git a/Makefile.generating b/Makefile.generating
index b98dc5e96ebec884e0559f3d21522b5b5d53c8f9..5aeabb3e2bab9cf4e171db282b4f97205c5ad26e 100644
--- a/Makefile.generating
+++ b/Makefile.generating
@@ -31,13 +31,13 @@ tests/ptests_config: Makefile.generating share/Makefile.config
 	$(RM) $@
 	$(TOUCH) $@
 	$(ECHO) "DEFAULT_SUITES=$(PLUGIN_TESTS_LIST)" >> $@
-	$(ECHO) "TOPLEVEL_PATH=bin/toplevel.$(OCAMLBEST)$(EXE)" >> $@
+	$(ECHO) "TOPLEVEL_PATH=$(FRAMAC_ROOT_SRCDIR)/bin/toplevel.$(OCAMLBEST)$(EXE)" >> $@
 	$(ECHO) "OCAMLRUNPARAM=" >> $@
-	$(ECHO) "FRAMAC_SESSION=." >> $@
-	$(ECHO) "FRAMAC_SHARE=./share" >> $@
-	$(ECHO) "FRAMAC_PLUGIN=./lib/plugins" >> $@
-	$(ECHO) "FRAMAC_PLUGIN_GUI=./lib/plugins/gui" >> $@
-	$(ECHO) "FRAMAC_LIB=./lib/fc" >> $@
+	$(ECHO) "FRAMAC_SESSION=$(FRAMAC_ROOT_SRCDIR)" >> $@
+	$(ECHO) "FRAMAC_SHARE=$(FRAMAC_ROOT_SRCDIR)/share" >> $@
+	$(ECHO) "FRAMAC_PLUGIN=$(FRAMAC_ROOT_SRCDIR)/lib/plugins" >> $@
+	$(ECHO) "FRAMAC_PLUGIN_GUI=$(FRAMAC_ROOT_SRCDIR)/lib/plugins/gui" >> $@
+	$(ECHO) "FRAMAC_LIB=$(FRAMAC_ROOT_SRCDIR)/lib/fc" >> $@
 	$(CHMOD_RO) $@
 
 ALL_LIBRARY_NAMES=$(shell ocamlfind query -r -p-format $(LIBRARY_NAMES) $(LIBRARY_NAMES_GUI))
diff --git a/src/kernel_services/ast_queries/file.ml b/src/kernel_services/ast_queries/file.ml
index cdfbd925143a3e48433def70169c98517dcb2380..5d1fcf6df5ba8b1b970c07a1839698788108074b 100644
--- a/src/kernel_services/ast_queries/file.ml
+++ b/src/kernel_services/ast_queries/file.ml
@@ -523,10 +523,23 @@ let build_cpp_cmd = function
         include_args define_args
     in
     let cpp_command = replace_in_cpp_cmd cmdl supp_args (f:>string) (ppf:>string) in
+    let cpp_command_with_chdir =
+      if Kernel.JsonCompilationDatabase.is_set () then
+        let jcdb_path = (Kernel.JsonCompilationDatabase.get () :> string) in
+        let dir =
+          if Sys.is_directory jcdb_path then jcdb_path
+          else Filename.dirname jcdb_path
+        in
+        let cwd = Unix.getcwd () in
+        if cwd <> dir then
+          "cd " ^ dir ^ " && " ^ cpp_command
+        else cpp_command
+      else cpp_command
+    in
     Kernel.feedback ~dkey:Kernel.dkey_pp
       "preprocessing with \"%s\""
-      cpp_command;
-    Some (cpp_command, ppf, supp_args)
+      cpp_command_with_chdir;
+    Some (cpp_command_with_chdir, ppf, supp_args)
 
 let parse_cabs cpp_command = function
   | NoCPP f ->
@@ -1744,17 +1757,17 @@ let prepare_from_c_files () =
   let files = Files.get () in (* Allow pre-registration of prolog files *)
   let cpp_commands = List.map (fun f -> (f, build_cpp_cmd f)) files in
   if Kernel.PrintCppCommands.get () then print_and_exit cpp_commands;
-  if not (Kernel.AuditCheck.is_empty ()) then begin
+  let audit_check_path = Kernel.AuditCheck.get () in
+  if not (Filepath.Normalized.is_empty audit_check_path) then begin
     let all_sources_tbl = compute_sources_table cpp_commands in
-    let expected_hashes = source_hashes_of_json (Kernel.AuditCheck.get ()) in
+    let expected_hashes = source_hashes_of_json audit_check_path in
     check_source_hashes expected_hashes all_sources_tbl
   end;
-  if not (Kernel.AuditPrepare.is_empty ()) then begin
+  let audit_path = Kernel.AuditPrepare.get () in
+  if not (Filepath.Normalized.is_empty audit_path) then begin
     let all_sources_tbl = compute_sources_table cpp_commands in
-    let audit_path = Kernel.AuditPrepare.get () in
     print_all_sources audit_path all_sources_tbl;
-    if not (Filepath.Normalized.is_special_stdout audit_path)
-    then
+    if not (Filepath.Normalized.is_special_stdout audit_path) then
       Kernel.feedback "Audit: sources list written to: %a@."
         Filepath.Normalized.pretty audit_path;
   end;
diff --git a/src/kernel_services/cmdline_parameters/cmdline.ml b/src/kernel_services/cmdline_parameters/cmdline.ml
index 4fb1fe39b1174c208bfbe293d73e1b50c95b1dad..aee2fb64e6cba33139387c964db365fd8df2bdd5 100644
--- a/src/kernel_services/cmdline_parameters/cmdline.ml
+++ b/src/kernel_services/cmdline_parameters/cmdline.ml
@@ -268,10 +268,10 @@ exception Cannot_parse of string * string
 let raise_error name because = raise (Cannot_parse(name, because))
 
 let error name msg =
-  let bin_name = Sys.argv.(0) in
+  let bin_name = Filepath.Normalized.of_string Sys.argv.(0) in
   Kernel_log.abort
-    "option `%s' %s.@\nuse `%s -help' for more information."
-    name msg bin_name
+    "option `%s' %s.@\nuse `%a -help' for more information."
+    name msg Filepath.Normalized.pretty bin_name
 
 let warning name msg =
   Kernel_log.warning
diff --git a/tests/jcdb/compile_commands.json b/tests/jcdb/compile_commands.json
index 0592cf2b0799160bb9d83ce1b7aa103c2fc15b7c..0636253c51623f06ae390c688eb0c77254aad822 100644
--- a/tests/jcdb/compile_commands.json
+++ b/tests/jcdb/compile_commands.json
@@ -1,10 +1,10 @@
 [
     { "directory": ".",
-      "command": "g++ -DDUPLICATE_FLAGS_THAT_WILL_BE_OVERWRITTEN",
+      "command": "gcc -DDUPLICATE_FLAGS_THAT_WILL_BE_OVERWRITTEN",
       "file": "jcdb.c"
     },
     { "directory": ".",
-      "command": "g++ -DDUPLICATE_FLAGS_THAT_WILL_BE_OVERWRITTEN",
+      "command": "gcc -DDUPLICATE_FLAGS_THAT_WILL_BE_OVERWRITTEN",
       "file": "jcdb.c"
     },
     {
@@ -12,7 +12,7 @@
       "file": "jcdb.c"
     },
     { "directory": ".",
-      "command": "/usr/bin/clang++ -D'MSG=\"a \\\" \\\"b\"'  -D'SINGLE_DOUBLE(a)=\"a \\\"with spaces and	tab \"' -DSOMEDEF=\"With spaces, quotes and \\-es.\" -D\"DOUBLE_SINGLE(a)=a \\\"macro with spaces and non-escaped \\\\'\\\"\"   -DEMPTY=''  -DEMPTY2= -DTEST=42	 -D'MACRO_FOR_INCR(s)=s+1' -DTOUNDEF -UTOUNDEF",
+      "command": "/usr/bin/clang -D'MSG=\"a \\\" \\\"b\"'  -D'SINGLE_DOUBLE(a)=\"a \\\"with spaces and	tab \"' -DSOMEDEF=\"With spaces, quotes and \\-es.\" -D\"DOUBLE_SINGLE(a)=a \\\"macro with spaces and non-escaped \\\\'\\\"\"   -DEMPTY=''  -DEMPTY2= -DTEST=42	 -D'MACRO_FOR_INCR(s)=s+1' -DTOUNDEF -UTOUNDEF",
       "file": "jcdb.c"
     }
 ]
diff --git a/tests/jcdb/oracle/subdir.err b/tests/jcdb/oracle/subdir.err
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/jcdb/oracle/subdir.res b/tests/jcdb/oracle/subdir.res
new file mode 100644
index 0000000000000000000000000000000000000000..db55f314d60304947e9a79782ccf41d22e986345
--- /dev/null
+++ b/tests/jcdb/oracle/subdir.res
@@ -0,0 +1 @@
+[kernel] Parsing PWD/../subdir.c (with preprocessing)
diff --git a/tests/jcdb/subdir.c b/tests/jcdb/subdir.c
new file mode 100644
index 0000000000000000000000000000000000000000..1729358186a7ea3555027ab353b3194e7a8692e5
--- /dev/null
+++ b/tests/jcdb/subdir.c
@@ -0,0 +1,13 @@
+/* run.config
+   NOFRAMAC:
+   EXECNOW: LOG subdir.res LOG subdir.err (cd @PTEST_DIR@/subdir1 && @frama-c@ -add-symbolic-path $PWD/..:PWD/.. -json-compilation-database ../subdir.json ../subdir.c) > @PTEST_RESULT@/subdir.res 2> @PTEST_RESULT@/subdir.err
+*/
+
+// this test must be run with PWD in subdir1
+#include "subdir1/header.h"
+#include "included.h" // in subdir2, via '-Isubdir2' in subdir.json
+#include "__fc_builtin.h" // to check that Frama-C's libc is correctly included
+
+int main() {
+  return ONE + TWO;
+}
diff --git a/tests/jcdb/subdir.json b/tests/jcdb/subdir.json
new file mode 100644
index 0000000000000000000000000000000000000000..05879db42ad96969ed354f37842b5dfb617b3685
--- /dev/null
+++ b/tests/jcdb/subdir.json
@@ -0,0 +1,6 @@
+[
+    { "directory": ".",
+      "command": "gcc -Isubdir2",
+      "file": "subdir.c"
+    }
+]
diff --git a/tests/jcdb/subdir1/header.h b/tests/jcdb/subdir1/header.h
new file mode 100644
index 0000000000000000000000000000000000000000..f01809a81bd36dc763f39331fbce25d55b2d5651
--- /dev/null
+++ b/tests/jcdb/subdir1/header.h
@@ -0,0 +1 @@
+#define ONE 1
diff --git a/tests/jcdb/subdir2/included.h b/tests/jcdb/subdir2/included.h
new file mode 100644
index 0000000000000000000000000000000000000000..80ba189555ec881b79013d40798117477b1086d0
--- /dev/null
+++ b/tests/jcdb/subdir2/included.h
@@ -0,0 +1,3 @@
+// to be included by subdir.c
+
+#define TWO 2