See http://distcc.samba.org/faq.html#gdb
This is ftp://ftp.gtk.org/pub/users/timj/patches/distcc-line3.diff
rediffed against distcc-2.18.3, with one tiny bugfix;
here's the bugfix (as a unified diff against postproc.c):

+#if 0
         strcpy (dirname + dl, pp_source_name);
+#else
+        /* Original patch appended pp_source_name, but
+         * that was wrong if it contained a directory part;
+         * if dir was /foo and source_name was bar/bletch,
+         * it caused embedded paths of form /foo/bar/bar/blech.c
+         */
+      dirname[dl] = 0;
+#endif


diff -Naur distcc-2.18.3/Makefile.in distcc-2.18.3-postproc/Makefile.in
--- distcc-2.18.3/Makefile.in	2004-10-23 22:05:48.000000000 -0700
+++ distcc-2.18.3-postproc/Makefile.in	2005-12-10 13:09:08.000000000 -0800
@@ -165,6 +165,7 @@
 	src/rpc.o src/tempfile.o src/bulk.o src/help.o src/filename.o	\
 	src/lock.o							\
 	src/netutil.o							\
+	src/postproc.o							\
 	src/pump.o							\
 	src/sendfile.o							\
 	src/safeguard.o src/snprintf.o src/timeval.o			\
@@ -232,6 +233,7 @@
 	src/mon.c src/mon-notify.c src/mon-text.c			\
 	src/mon-gnome.c							\
 	src/ncpus.c src/netutil.c					\
+	src/postproc.c 							\
 	src/prefork.c src/pump.c					\
 	src/remote.c src/renderer.c src/rpc.c				\
 	src/safeguard.c src/sendfile.c src/setuid.c src/serve.c		\
@@ -243,6 +245,7 @@
 
 
 HEADERS = src/access.h							\
+	src/postproc.h							\
 	src/bulk.h							\
 	src/clinet.h src/compile.h					\
 	src/daemon.h							\
diff -Naur distcc-2.18.3/src/compile.c distcc-2.18.3-postproc/src/compile.c
--- distcc-2.18.3/src/compile.c	2004-10-01 17:47:07.000000000 -0700
+++ distcc-2.18.3-postproc/src/compile.c	2005-12-10 13:09:08.000000000 -0800
@@ -44,6 +44,7 @@
 #include "lock.h"
 #include "timeval.h"
 #include "compile.h"
+#include "postproc.h"
 
 
 /**
@@ -154,6 +155,8 @@
 
     /* FIXME: argv_stripped is leaked. */
 
+    if ((ret = dcc_post_process_set_source(input_fname)) != 0)
+        goto fallback;
     if ((ret = dcc_compile_remote(argv_stripped,
                                   input_fname,
                                   cpp_fname,
diff -Naur distcc-2.18.3/src/postproc.c distcc-2.18.3-postproc/src/postproc.c
--- distcc-2.18.3/src/postproc.c	1969-12-31 16:00:00.000000000 -0800
+++ distcc-2.18.3-postproc/src/postproc.c	2005-12-10 13:09:39.000000000 -0800
@@ -0,0 +1,375 @@
+/* -*- c-file-style: "java"; indent-tabs-mode: nil; fill-column: 78 -*-
+ * 
+ * distcc -- A simple distributed compiler system
+ * $Header: $
+ *
+ * Copyright (C) 2002 by Tim Janik <timj@gtk.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "distcc.h"
+#include "trace.h"
+#include "assert.h"
+#include "postproc.h"
+
+
+typedef unsigned char uchar;
+
+/* buffered input */
+#define BUFFER_SIZE    4000
+typedef struct {
+    int    fd;
+    uchar *bound;
+    uchar  buffer[BUFFER_SIZE + 1];
+    uchar *text;
+} PPInput;
+
+static inline int peek_char (PPInput *inp)
+{
+    if (inp->text < inp->bound)
+        return *inp->text;
+    else if (inp->fd >= 0) {
+        int count;
+        uchar *buffer = inp->buffer;
+        do
+            count = read (inp->fd, buffer, BUFFER_SIZE);
+        while (count == -1 && (errno == EINTR || errno == EAGAIN));
+        if (count < 1) {
+            inp->fd = -1;
+            return EOF;
+        } else {
+            inp->text = buffer;
+            inp->bound = buffer + count;
+            return *inp->text;
+        }
+    } else
+        return EOF;
+}
+
+static inline int get_char (PPInput *inp)
+{
+    int c = peek_char (inp);
+    if (c != EOF)
+        inp->text++;
+    return c;
+}
+
+static void unget_char (PPInput *inp,
+                        uchar    c)
+{
+    /* we only allow unget backs after a non-EOF get_char() */
+    assert (inp->text > inp->buffer);
+    inp->text--;
+    inp->text[0] = c;
+}
+
+
+/* buffered output */
+typedef struct {
+    int     fd;
+    uchar  *p;
+    uchar   buffer[BUFFER_SIZE + 1];
+} PPOutput;
+
+static void flush_output (PPOutput *out)
+{
+    int count, n = out->p - out->buffer;
+    errno = 0;
+    if (n) {
+        do
+            count = write (out->fd, out->buffer, n);
+        while (count == -1 && (errno == EINTR || errno == EAGAIN));
+        if (count != n)
+            rs_log_error ("failed to write %u bytes: %s", n, strerror (errno));
+    }
+    out->p = out->buffer;
+}
+
+static inline void write_char (PPOutput *out,
+                               int       ch)
+{
+    *(out->p++) = ch;
+    if (out->p - out->buffer >= BUFFER_SIZE)
+        flush_output (out);
+}
+
+
+/* source file path name */
+static char *pp_source_name = NULL;
+
+/* rarely triggered. post processed output usually doesn't
+ * contain comments (an exception would be gcc -E -C)
+ */
+static void post_process_comment_rest (PPInput  *inp,
+                                       PPOutput *out,
+                                       int       term /* must be '\n' or '/' */)
+{
+    int seenast = FALSE;
+    /* read up to \n or * followed by / */
+    while (1) {
+        int c = get_char (inp);
+        switch (c) {
+        case EOF:
+            return;       /* broken input */
+        case '*':
+            write_char (out, c);
+            seenast = TRUE;
+            break;
+        case '/':
+            write_char (out, c);
+            if (seenast && term == c)
+                return;
+            break;
+        case '\n':
+            write_char (out, c);
+            if (term == c)
+                return;
+            seenast = FALSE;
+            break;
+        default:
+            write_char (out, c);
+            seenast = FALSE;
+            break;
+        }
+    }
+}
+
+/* process the rest of a string, after the initial '\'' or '\"' has been read */
+static inline void post_process_string_rest (PPInput  *inp,
+                                             PPOutput *out,
+                                             int       term /* must be '\"' or '\'' */)
+{
+    int escape = FALSE;
+    while (1) {
+        int c = get_char (inp);
+        switch (c) {
+        case EOF:
+            return;	/* broken input */
+        case '\\':
+            write_char (out, c);
+            escape = !escape;
+            break;
+        case '\"':
+        case '\'':
+            write_char (out, c);
+            if (c == term && !escape)
+                return;
+            escape = FALSE;
+            break;
+        default:
+            write_char (out, c);
+            escape = FALSE;
+            break;
+        }
+    }
+}
+
+/* handle the case where a line starts with '#' which indicates
+ * preprocessor instructions. the ones to care about here are
+ * '# 3 "files/foo.c"', i.e. source file references. those are
+ * patched up to only contain absolute pathnames.
+ */
+static int post_process_hashcross (PPInput    *inp,
+                                   PPOutput   *out,
+                                   const char *pathprefix)
+{
+    int c;
+    /* check whether here comes a #-line directive, hash already parsed.
+     * we need to return the last char processed for our caller to update state.
+     */
+    
+    /* read up spaces */
+    c = get_char (inp);
+    while (c == ' ' || c == '\t') {
+        write_char (out, c);
+        c = get_char (inp);
+    }
+    /* read up digits */
+    while (c >= '0' && c <= '9') {
+        write_char (out, c);
+        c = get_char (inp);
+    }
+    /* read up spaces */
+    while (c == ' ' || c == '\t') {
+        write_char (out, c);
+        c = get_char (inp);
+    }
+    /* read up double quote */
+    if (c != '\"')
+        goto abort;
+    write_char (out, c);
+    c = get_char (inp);
+    /* here it comes: if c is a slash, we got an absolute
+     * pathname. otherwise we insert our prefix and handle
+     * the rest as an ordinary string.
+     */
+    if (c != '/') {
+        const char *p = pathprefix;
+        while (*p)
+            write_char (out, *p++);
+    }
+    unget_char (inp, c);
+    post_process_string_rest (inp, out, '\"');
+    return '\"';
+    
+    abort:
+    if (c != EOF)
+        write_char (out, c);
+    return c;
+}
+
+static void post_process (PPInput    *inp,
+                          PPOutput   *out,
+                          const char *pathprefix)
+{
+    int seennewline = TRUE;
+    int lastc, c = '\n';
+    while (1) {
+        lastc = c;
+        c = get_char (inp);
+        switch (c) {
+        case EOF:
+            return;
+	case '\n':
+            write_char (out, c);
+            seennewline = TRUE;
+            break;
+	case '#':
+            write_char (out, c);
+            if (seennewline) {
+                c = post_process_hashcross (inp, out, pathprefix);
+                if (c == EOF)
+                    return;
+	    }
+            seennewline = c == '\n';
+            break;
+	case ' ':
+	case '\t':
+            write_char (out, c);
+            /* preserve seennewline */
+            break;
+	case '*':
+            write_char (out, c);
+            if (lastc == '/') {
+                post_process_comment_rest (inp, out, '/');
+                c = '/';
+            }
+            seennewline = FALSE;
+            break;
+	case '\"': case '\'':
+            write_char (out, c);
+            post_process_string_rest (inp, out, c);
+            seennewline = FALSE;
+            break;
+	default:
+            write_char (out, c);
+            seennewline = FALSE;
+            break;
+	}
+    }
+}
+
+int dcc_post_process (const char *in_fname,
+                      char      **out_fname)
+{
+    char *p, *dirname;
+    PPOutput out = { -1, 0, { 0, } };
+    PPInput inp = { -1, 0, { 0, }, 0 };
+    int ret;
+
+    rs_trace("dcc_post_process(%s,)", in_fname);
+    if ((ret = dcc_make_tmpnam ("ppline", ".i", out_fname)))
+	return ret;
+    
+    inp.fd = open (in_fname, O_RDONLY);
+    if (inp.fd < 0) {
+        rs_log_error ("opening input \"%s\" failed: %s", in_fname, strerror (errno));
+        return -1;
+    }
+
+    out.p = out.buffer;
+    out.fd = open (*out_fname, O_CREAT | O_TRUNC | O_WRONLY, 0644);
+    if (out.fd < 0) {
+        rs_log_error ("opening output \"%s\" failed: %s", *out_fname, strerror (errno));
+        close (inp.fd);
+        return -1;
+    }
+
+    if (!pp_source_name)
+        pp_source_name = strdup ("");
+    
+    /* figure slash terminated directory name */
+    if (pp_source_name[0] == '/') {
+        dirname = strdup (pp_source_name);
+    } else {
+        int dl, sl = strlen (pp_source_name);
+        char cdirbuf[8192];
+        char *cdir = getcwd (cdirbuf, sizeof (cdirbuf));
+        if (!cdir)
+            return -1;
+        dl = strlen (cdir);
+        dirname = malloc (dl + 1 + sl + 1);
+        strcpy (dirname, cdir);
+        dirname[dl++] = '/';
+#if 0
+        strcpy (dirname + dl, pp_source_name);
+#else
+        /* Original patch appended pp_source_name, but
+         * that was wrong if it contained a directory part;
+	 * if dir was /foo and source_name was bar/bletch,
+	 * it caused embedded paths of form /foo/bar/bar/blech.c
+         */
+	dirname[dl] = 0;
+#endif
+    }
+    p = strrchr (dirname, '/');
+    if (p)
+        p[1] = 0;
+    rs_trace("dcc_post_process: pp_source_name %s, setting dirname to %s", pp_source_name, dirname);
+
+    post_process (&inp, &out, dirname);
+    flush_output (&out);
+    free (dirname);
+
+    close (inp.fd);
+    if (close (out.fd)) {
+        rs_log_error ("flushing output %s failed: %s\n", *out_fname, strerror (errno));
+        return -1;
+    }
+    return 0;
+}
+
+int dcc_post_process_set_source (const char *source_name)
+{
+    rs_trace("dcc_post_process_set_source(%s)", source_name ? source_name : "");
+    if (pp_source_name)
+        free (pp_source_name);
+    pp_source_name = source_name ? strdup (source_name) : NULL;
+    return 0;
+}
diff -Naur distcc-2.18.3/src/postproc.h distcc-2.18.3-postproc/src/postproc.h
--- distcc-2.18.3/src/postproc.h	1969-12-31 16:00:00.000000000 -0800
+++ distcc-2.18.3-postproc/src/postproc.h	2005-12-10 13:09:08.000000000 -0800
@@ -0,0 +1,28 @@
+/* -*- c-file-style: "java"; indent-tabs-mode: nil; fill-column: 78 -*-
+ * 
+ * distcc -- A simple distributed compiler system
+ * $Header: $
+ *
+ * Copyright (C) 2002 by Tim Janik <timj@gtk.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/* postproc.c */
+int dcc_post_process_set_source (const char *source_name);
+int dcc_post_process            (const char *in_fname,
+                                 char      **out_fname);
+
diff -Naur distcc-2.18.3/src/remote.c distcc-2.18.3-postproc/src/remote.c
--- distcc-2.18.3/src/remote.c	2004-10-23 22:05:49.000000000 -0700
+++ distcc-2.18.3-postproc/src/remote.c	2005-12-10 13:09:08.000000000 -0800
@@ -48,6 +48,7 @@
 #include "lock.h"
 #include "compile.h"
 #include "bulk.h"
+#include "postproc.h"
 
 
 /*
@@ -183,6 +184,7 @@
     int ssh_status;
     off_t doti_size;
     struct timeval before, after;
+    char *pp_fname;
 
     if (gettimeofday(&before, NULL))
         rs_log_warning("gettimeofday failed");
@@ -208,7 +210,9 @@
     if ((ret = dcc_wait_for_cpp(cpp_pid, status, input_fname)))
         goto out;
     
-    if ((ret = dcc_x_file(to_net_fd, cpp_fname, "DOTI", host->compr, &doti_size)))
+    if ((ret = dcc_post_process(cpp_fname, &pp_fname)))
+        goto out;
+    if ((ret = dcc_x_file(to_net_fd, pp_fname, "DOTI", host->compr, &doti_size)))
         goto out;
 
     rs_trace("client finished sending request to server");
