diff mbox series

store debug information in $HOST_DIR/lib/debug

Message ID 20190123131539.24699-1-Gerhard@Heift.Name
State Rejected
Headers show
Series store debug information in $HOST_DIR/lib/debug | expand

Commit Message

Gerhard Heift Jan. 23, 2019, 1:15 p.m. UTC
Before striping the binaries in $TARGET_DIR, the debug information is saved to
$HOST_DIR/lib/debug using the following command as described in [1]:

  $ objcopy --only-keep-debug --compress-debug-sections $file

This allows remote debugging as described in section 8.12.2 with striped
binaries in the final image. Therefore the build ID is included during the build
process, if debug information is enabled via BR2_ENABLE_DEBUG.

[1] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html

Signed-off-by: Gerhard Heift <Gerhard@Heift.Name>
---
 Makefile                   |  6 ++---
 package/Makefile.in        | 12 ++++++---
 support/scripts/copy-debug | 52 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 63 insertions(+), 7 deletions(-)
 create mode 100755 support/scripts/copy-debug

Comments

Thomas De Schampheleire Jan. 30, 2019, 8:28 p.m. UTC | #1
El mié., 23 ene. 2019 a las 14:22, Gerhard Heift
(<gerhard@heift.name>) escribió:
>
> Before striping the binaries in $TARGET_DIR, the debug information is saved to
> $HOST_DIR/lib/debug using the following command as described in [1]:
>
>   $ objcopy --only-keep-debug --compress-debug-sections $file
>
> This allows remote debugging as described in section 8.12.2 with striped
> binaries in the final image. Therefore the build ID is included during the build
> process, if debug information is enabled via BR2_ENABLE_DEBUG.
>
> [1] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
>
> Signed-off-by: Gerhard Heift <Gerhard@Heift.Name>
> ---
>  Makefile                   |  6 ++---
>  package/Makefile.in        | 12 ++++++---
>  support/scripts/copy-debug | 52 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 63 insertions(+), 7 deletions(-)
>  create mode 100755 support/scripts/copy-debug
>

If I understand this correctly then the sysroot you'd specify in gdb
would be this new directory output/host/lib/debug, right?
Previously this gdb sysroot was set as output/staging, see
toolchain/helpers.mk. I believe that path should be adapted in your
patch.

It's unclear to me why you selected 'output/host/lib/debug' as path:
this is not about host stuff but target stuff. Moreover, it's also not
only about 'lib' but also binaries.

Do you have some numbers regarding size impact on the buildroot
directory as a whole for a typical build?

Best regards,
Thomas
Gerhard Heift Jan. 31, 2019, 12:59 p.m. UTC | #2
Am Mi., 30. Jan. 2019 um 21:29 Uhr schrieb Thomas De Schampheleire
<patrickdepinguin@gmail.com>:
>
> El mié., 23 ene. 2019 a las 14:22, Gerhard Heift
> (<gerhard@heift.name>) escribió:
> >
> > Before striping the binaries in $TARGET_DIR, the debug information is saved to
> > $HOST_DIR/lib/debug using the following command as described in [1]:
> >
> >   $ objcopy --only-keep-debug --compress-debug-sections $file
> >
> > This allows remote debugging as described in section 8.12.2 with striped
> > binaries in the final image. Therefore the build ID is included during the build
> > process, if debug information is enabled via BR2_ENABLE_DEBUG.
> >
> > [1] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
> >
> > Signed-off-by: Gerhard Heift <Gerhard@Heift.Name>
> > ---
> >  Makefile                   |  6 ++---
> >  package/Makefile.in        | 12 ++++++---
> >  support/scripts/copy-debug | 52 ++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 63 insertions(+), 7 deletions(-)
> >  create mode 100755 support/scripts/copy-debug
> >
>
> If I understand this correctly then the sysroot you'd specify in gdb
> would be this new directory output/host/lib/debug, right?
> Previously this gdb sysroot was set as output/staging, see
> toolchain/helpers.mk. I believe that path should be adapted in your
> patch.
>
> It's unclear to me why you selected 'output/host/lib/debug' as path:
> this is not about host stuff but target stuff. Moreover, it's also not
> only about 'lib' but also binaries.

I just started the cross debugger as mentioned in [1] with
  $ host/bin/<tuple>-gdb -x staging/usr/share/buildroot/gdbinit
target/usr/bin/ls
and let gdb show me, where it searchs for the debug symbols with (see [2]):
  (gdb) show debug-file-directory
which outputs the following:

  > The directory where separate debug symbols are searched for is
"$HOST_DIR/lib/debug".

This is the reason, why I selected this path. I don't need to change
anything else beside adding the build-id and copying the symbols to
this path with the provided wrapper.

If you want to store the debug information in another location, either
the gdbinit generation has to be adjusted in toolchain/helpers.mk to
contain something like
  set debug-file-directory $DEBUGDIR
or the directory has to be provided to the configuration script of gdb
in packages/gdb/gdb.mk like this
  HOST_GDB_CONF_OPTS += --with-separate-debug-dir=$DEBUGDIR

The default directory is derived from the prefix and libdir [3], where
in this case prefix=$HOST_DIR and libdir=$prefix/lib and therefore
DEBUGDIR=$HOST_DIR/lib/debug
which is the directory I used with this patch.

If another location is disered, I am happy to provied an extended
patch, either by extending the gdbinit generation or by adding the
configuration option for gdb.

If I understand the directory hierarchy correct, then the debug
symbols should be located under
DEBUGDIR=$STAGING_DIR/usr/lib/debug
Is this correct?

> Do you have some numbers regarding size impact on the buildroot
> directory as a whole for a typical build?

I just did a test with my personal build and it looks like the
build-id, which is added to identify the debugging symbols, increses
each binary or library by 56 to 60 bytes.

> Best regards,
> Thomas

Best regards,
  Gerhard

[1] https://buildroot.org/downloads/manual/manual.html#_using_literal_gdb_literal_in_buildroot
[2] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
[3] https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=gdb/configure.ac;h=d4133ea71e2207c459cab0cadf51a810fe200141;hb=f99d9b9f436dce02aa06839395c67d400b2a0de0#l120
Thomas Petazzoni Feb. 4, 2019, 4:58 p.m. UTC | #3
Hello Gerhard,

Thanks for this contribution. I'm adding in Cc a few other people to
hopefully get their attention.

On Wed, 23 Jan 2019 14:15:39 +0100
Gerhard Heift <gerhard@heift.name> wrote:

> Before striping the binaries in $TARGET_DIR, the debug information is saved to
> $HOST_DIR/lib/debug using the following command as described in [1]:
> 
>   $ objcopy --only-keep-debug --compress-debug-sections $file
> 
> This allows remote debugging as described in section 8.12.2 with striped
> binaries in the final image. Therefore the build ID is included during the build
> process, if debug information is enabled via BR2_ENABLE_DEBUG.
> 
> [1] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
> 
> Signed-off-by: Gerhard Heift <Gerhard@Heift.Name>

When you build with BR2_ENABLE_DEBUG=y and BR2_STRIP_strip=y, all the
libraries in $(STAGING_DIR) are installed with debugging symbols, and
they are not stripped.

As explained in our manual section 8.12.2, we provide a gdbinit that
tells gdb to look in $(STAGING_DIR) for debugging symbols.

Your patch has the effect of duplicating the debugging symbols: they
are already in $(STAGING_DIR), and they will now be duplicated in
$(HOST_DIR)/lib/debug. The only benefit that I can see is that the
gdbinit file is no longer needed because gdb will automatically look in
$(HOST_DIR)/lib/debug. But that can probably be resolved by making
$(HOST_DIR)/lib/debug a symlink to $(STAGING_DIR). Could you try this
instead ?

Thanks a lot,

Thomas
Thomas De Schampheleire Feb. 4, 2019, 6:05 p.m. UTC | #4
El lun., 4 feb. 2019 a las 17:58, Thomas Petazzoni
(<thomas.petazzoni@bootlin.com>) escribió:
>
> Hello Gerhard,
>
> Thanks for this contribution. I'm adding in Cc a few other people to
> hopefully get their attention.
>
> On Wed, 23 Jan 2019 14:15:39 +0100
> Gerhard Heift <gerhard@heift.name> wrote:
>
> > Before striping the binaries in $TARGET_DIR, the debug information is saved to
> > $HOST_DIR/lib/debug using the following command as described in [1]:
> >
> >   $ objcopy --only-keep-debug --compress-debug-sections $file
> >
> > This allows remote debugging as described in section 8.12.2 with striped
> > binaries in the final image. Therefore the build ID is included during the build
> > process, if debug information is enabled via BR2_ENABLE_DEBUG.
> >
> > [1] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
> >
> > Signed-off-by: Gerhard Heift <Gerhard@Heift.Name>
>
> When you build with BR2_ENABLE_DEBUG=y and BR2_STRIP_strip=y, all the
> libraries in $(STAGING_DIR) are installed with debugging symbols, and
> they are not stripped.
>
> As explained in our manual section 8.12.2, we provide a gdbinit that
> tells gdb to look in $(STAGING_DIR) for debugging symbols.
>
> Your patch has the effect of duplicating the debugging symbols: they
> are already in $(STAGING_DIR), and they will now be duplicated in
> $(HOST_DIR)/lib/debug. The only benefit that I can see is that the
> gdbinit file is no longer needed because gdb will automatically look in
> $(HOST_DIR)/lib/debug. But that can probably be resolved by making
> $(HOST_DIR)/lib/debug a symlink to $(STAGING_DIR). Could you try this
> instead ?

The difference that this patch brings is that now also binaries can be
debugged based from the target directory, i.e. you don't need to find
the binary in output/build/foo-version/somewhere/.

Today, the staging directory only contains packages that explicitly
set FOO_INSTALL_STAGING = YES, which is typically only done for
libraries.

Best regards,
Thomas
Thomas Petazzoni Feb. 4, 2019, 6:19 p.m. UTC | #5
On Mon, 4 Feb 2019 19:05:26 +0100
Thomas De Schampheleire <patrickdepinguin+buildroot@gmail.com> wrote:

> The difference that this patch brings is that now also binaries can be
> debugged based from the target directory, i.e. you don't need to find
> the binary in output/build/foo-version/somewhere/.
> 
> Today, the staging directory only contains packages that explicitly
> set FOO_INSTALL_STAGING = YES, which is typically only done for
> libraries.

Yes, but:

1) Typically the binary being debugged has to be passed explicitly as
   gdb argument, it's not automatically "inferred" by gdb. So having it
   in output/build/foo-<version>/somewhere/ or elsewhere doesn't make a
   huge difference.

2) If for convenience reasons we want to make all binaries with
   debugging symbols easily available, then the long term solution we
   have been discussing for a while is to install all packages to
   STAGING_DIR. So basically I'm more for extending STAGING_DIR to
   contain binaries rather than to create a partial copy of STAGING_DIR
   in HOST_DIR/lib/debug.

Thomas
Yann E. MORIN Feb. 4, 2019, 6:27 p.m. UTC | #6
All,

On 2019-02-04 19:19 +0100, Thomas Petazzoni spake thusly:
> On Mon, 4 Feb 2019 19:05:26 +0100
> Thomas De Schampheleire <patrickdepinguin+buildroot@gmail.com> wrote:
> 
> > The difference that this patch brings is that now also binaries can be
> > debugged based from the target directory, i.e. you don't need to find
> > the binary in output/build/foo-version/somewhere/.
> > 
> > Today, the staging directory only contains packages that explicitly
> > set FOO_INSTALL_STAGING = YES, which is typically only done for
> > libraries.
> 
> Yes, but:
> 
> 1) Typically the binary being debugged has to be passed explicitly as
>    gdb argument, it's not automatically "inferred" by gdb. So having it
>    in output/build/foo-<version>/somewhere/ or elsewhere doesn't make a
>    huge difference.
> 
> 2) If for convenience reasons we want to make all binaries with
>    debugging symbols easily available, then the long term solution we
>    have been discussing for a while is to install all packages to
>    STAGING_DIR. So basically I'm more for extending STAGING_DIR to
>    contain binaries rather than to create a partial copy of STAGING_DIR
>    in HOST_DIR/lib/debug.

I side with Thomas P. on that one.

We've had this discussion a few times already, and the conslusion was to
try and install everything in staging.

Then, target can be created at the end, by copying staging/ to target/
and do the target-finalize step on that copy.

Or so that is the main idea, and it can be refined to account for subtle
details, such as the on-going per-package directory and top-level
parallel build...

Regards,
Yann E. MORIN.
diff mbox series

Patch

diff --git a/Makefile b/Makefile
index a382a5defb..d0b5e665b0 100644
--- a/Makefile
+++ b/Makefile
@@ -743,20 +743,20 @@  endif
 	rm -rf $(TARGET_DIR)/usr/doc $(TARGET_DIR)/usr/share/doc
 	rm -rf $(TARGET_DIR)/usr/share/gtk-doc
 	rmdir $(TARGET_DIR)/usr/share 2>/dev/null || true
-	$(STRIP_FIND_CMD) | xargs -0 $(STRIPCMD) 2>/dev/null || true
+	$(STRIP_FIND_CMD) | xargs -0 $(TOPDIR)/support/scripts/copy-debug $(HOST_DIR)/lib/debug $(TARGET_OBJCOPY) $(STRIPCMD) 2>/dev/null || true
 
 # See http://sourceware.org/gdb/wiki/FAQ, "GDB does not see any threads
 # besides the one in which crash occurred; or SIGTRAP kills my program when
 # I set a breakpoint"
 ifeq ($(BR2_TOOLCHAIN_HAS_THREADS),y)
 	find $(TARGET_DIR)/lib/ -type f -name 'libpthread*.so*' | \
-		xargs -r $(STRIPCMD) $(STRIP_STRIP_DEBUG)
+		xargs -r $(TOPDIR)/support/scripts/copy-debug $(HOST_DIR)/lib/debug $(TARGET_OBJCOPY) $(STRIPCMD) $(STRIP_STRIP_DEBUG)
 endif
 
 # Valgrind needs ld.so with enough information, so only strip
 # debugging symbols.
 	find $(TARGET_DIR)/lib/ -type f -name 'ld-*.so*' | \
-		xargs -r $(STRIPCMD) $(STRIP_STRIP_DEBUG)
+		xargs -r $(TOPDIR)/support/scripts/copy-debug $(HOST_DIR)/lib/debug $(TARGET_OBJCOPY) $(STRIPCMD) $(STRIP_STRIP_DEBUG)
 	test -f $(TARGET_DIR)/etc/ld.so.conf && \
 		{ echo "ERROR: we shouldn't have a /etc/ld.so.conf file"; exit 1; } || true
 	test -d $(TARGET_DIR)/etc/ld.so.conf.d && \
diff --git a/package/Makefile.in b/package/Makefile.in
index dc818a2c18..a4b75ac2b9 100644
--- a/package/Makefile.in
+++ b/package/Makefile.in
@@ -110,6 +110,8 @@  endif
 STAGING_SUBDIR = $(GNU_TARGET_NAME)/sysroot
 STAGING_DIR    = $(HOST_DIR)/$(STAGING_SUBDIR)
 
+TARGET_LDFLAGS = $(call qstrip,$(BR2_TARGET_LDFLAGS))
+
 ifeq ($(BR2_OPTIMIZE_0),y)
 TARGET_OPTIMIZATION = -O0
 endif
@@ -132,16 +134,18 @@  ifeq ($(BR2_OPTIMIZE_FAST),y)
 TARGET_OPTIMIZATION = -Ofast
 endif
 ifeq ($(BR2_DEBUG_1),y)
-TARGET_DEBUGGING = -g1
+TARGET_DEBUGGING = -g1 -Wl,--build-id
+TARGET_LDFLAGS += -Wl,--build-id
 endif
 ifeq ($(BR2_DEBUG_2),y)
-TARGET_DEBUGGING = -g2
+TARGET_DEBUGGING = -g2 -Wl,--build-id
+TARGET_LDFLAGS += -Wl,--build-id
 endif
 ifeq ($(BR2_DEBUG_3),y)
-TARGET_DEBUGGING = -g3
+TARGET_DEBUGGING = -g3 -Wl,--build-id
+TARGET_LDFLAGS += -Wl,--build-id
 endif
 
-TARGET_LDFLAGS = $(call qstrip,$(BR2_TARGET_LDFLAGS))
 
 # By design, _FORTIFY_SOURCE requires gcc optimization to be enabled.
 # Therefore, we need to pass _FORTIFY_SOURCE and the optimization level
diff --git a/support/scripts/copy-debug b/support/scripts/copy-debug
new file mode 100755
index 0000000000..001ff157bf
--- /dev/null
+++ b/support/scripts/copy-debug
@@ -0,0 +1,52 @@ 
+#!/usr/bin/env sh
+DEBUG_DIR="$1"
+OBJCOPY_EXEC="$2"
+STRIP_EXEC="$3"
+shift 3
+
+for arg in "$@"; do
+    if [ "${arg#-}" != "${arg}" ]; then
+        continue
+    fi
+    INFO="$(file "$arg")"
+
+    # echo $arg: $INFO
+    if [ "x${INFO%not stripped*}" = "x$INFO" ]; then
+        # echo $arg already stripped
+        continue
+    fi
+    if [ "x${INFO#*BuildID[[]sha1]=}" = "x$INFO" ]; then
+        # echo $arg has no build ID
+        continue
+    fi
+
+    BUILD_ID="${INFO#*BuildID[[]sha1]=}"
+    BUILD_ID="${BUILD_ID%%,*}"
+
+    if [ "x${BUILD_ID}y" = xy ]; then
+        # echo $arg has empty build ID?
+        continue
+    fi
+
+    # 40 hex
+    if [ "x${BUILD_ID#[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]}y" != xy ]; then
+        # echo $arg has invalid build ID: $BUILD_ID?
+        continue
+    fi
+
+    POST="${BUILD_ID#[0-9a-z][0-9a-z]}"
+    PRE="${BUILD_ID%$POST}"
+
+    # echo $arg build ID: $BUILD_ID
+    # echo $DEBUG_DIR/.build-id/$PRE/$POST
+
+    mkdir -p $DEBUG_DIR/.build-id/$PRE
+    DEBUG_FILE=$DEBUG_DIR/.build-id/$PRE/$POST.debug
+
+    if ! [ -e $DEBUG_FILE ]; then
+        $OBJCOPY_EXEC --only-keep-debug --compress-debug-sections $arg $DEBUG_FILE
+        chmod 644 $DEBUG_FILE
+    fi
+done
+
+$STRIP_EXEC "$@"