Skip to content

Commit

Permalink
TLS access support in multiarch Linux GNU GDB
Browse files Browse the repository at this point in the history
TLS variable was not propagating from core dump file to
GDB and this patch implements that behavior for multiarch GDB.
This  approach includes linux-thread-db.c to be compiled
for multiarch GDB, where appropriate functions for
getting TLS addresses are used. If the program being debugged
is built for host architecture, we keep the old way
of calculating TLS. For target architectures modified
libthread_db functions (td_ta_new(), td_thr_tls_get_addr(),
td_thr_tlsbase()) are used, which can work with any version of glibc.
Also, additional functions are copied from glibc
in order to satisfy missing dependencies.

Similar to native debugging,  host libthread_db.so
should be loaded into multiarch GDB. It should be
the same version of the library which is used
for getting core dump file.  This is needed in order
to initialize necessary thread data in libthread_db.so.

        * Makefile.in(INTERNAL_CCFLAGS): Add CFLAGS for glibc-dep/.
        (CCOMPILE): Force glibc-dep/ to be compiled with gcc.
        * gdb/config/glibc.mh(GLIBCFILES): Define object files that
        will be generated in order to read TLS with GDB Multiarch.
        [DCROSS_GDB]: Define if it's GDB Multiarch.
        * gdb/configure.ac(cross_makefile_frag): Add GLIBCFILES in
        order to generate Makefile for cross platforms to read TLS.
        * gdb/configure: Regenerate.
        * gdb/gdb_proc_service.h: Add necessary code in order to
        compile glibc-dep/ with GCC.
        * gdb/gdbarch.c: Likewise.
        * gdb/glibc-dep/README.txt: Brief explanation of glibc-dep/.
        * gdb/glibc-dep/gdb_td_ta_new.c(init_target_dep_constants): New
        function.
        (gdb_td_ta_new): Get current version of GLIBC from coredump file.
        * gdb/glibc-dep/native-check.c(get_host_mach): New function.
        (native_check): Check if it is native architecture when using
        GDB Multiarch.
        * gdb/glibc-dep/db-symbols.h: Take necessary code from GLIBC
        in order to annul differences between GLIBC versions for reading
        TLS.
        * gdb/glibc-dep/nptl_db/fetch-value.c: Likewise.
        * gdb/glibc-dep/nptl_db/structs.def: Likewise.
        * gdb/glibc-dep/nptl_db/td_symbol_list.c: Likewise.
        * gdb/glibc-dep/nptl_db/td_ta_map_lwp2thr.c: Likewise.
        * gdb/glibc-dep/nptl_db/td_thr_tls_get_addr.c: Likewise.
        * gdb/glibc-dep/nptl_db/td_thr_tlsbase.c: Likewise. Modify in
        order to work with different versions of GLIBC.
        * gdb/glibc-dep/nptl_db/thread_dbP.h: Likewise.
        * gdb/glibc-dep/nptl_db/tls.h: Likewise.
        * gdb/gregset.h: Remove deprecated gregset_t but use
        elf_gregset_t.
        * gdb/linux-thread-db.c: (try_thread_db_load_1): Set up TLS
        functions from glibc-dep/.
        * gdb/nat/gdb_thread_db.h: (gdb_td_ta_new, gdb_td_thr_tlsbase)
        (gdb_td_thr_tls_get_addr, native_check): Declare TLS functions.
  • Loading branch information
djolertrk authored and djtodoro committed Mar 9, 2019
1 parent 56646e1 commit c4e3d7a
Show file tree
Hide file tree
Showing 20 changed files with 1,361 additions and 6 deletions.
40 changes: 39 additions & 1 deletion gdb/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,10 @@ ALL_TARGET_OBS = \
@host_makefile_frag@
# End of host-dependent makefile fragment

# Cross fragment
@cross_makefile_frag@
# End of cross fragment

FLAGS_TO_PASS = \
"prefix=$(prefix)" \
"exec_prefix=$(exec_prefix)" \
Expand Down Expand Up @@ -1008,7 +1012,7 @@ INFOFILES = gdb.info*
# Makefile.in
DEPFILES = $(TARGET_OBS) $(SER_HARDWIRE) $(NATDEPFILES) \
$(REMOTE_OBS) $(SIM_OBS)
$(REMOTE_OBS) $(SIM_OBS) $(GLIBCFILES)
SOURCES = $(SFILES) $(ALLDEPFILES) $(YYFILES) $(CONFIG_SRCS)
# Don't include YYFILES (*.c) because we already include *.y in SFILES,
Expand Down Expand Up @@ -2321,6 +2325,40 @@ arm-get-next-pcs.o: ${srcdir}/arch/arm-get-next-pcs.c
$(COMPILE) $(srcdir)/arch/arm-get-next-pcs.c
$(POSTCOMPILE)
# gdb/glibc-dep/ dependencies
#
# Need to explicitly specify the compile rule as make will do nothing
# or try to compile the object file into the sub-directory.
td_ta_map_lwp2thr.o: $(srcdir)/glibc-dep/nptl_db/td_ta_map_lwp2thr.c
$(COMPILE) $(srcdir)/glibc-dep/nptl_db/td_ta_map_lwp2thr.c
$(POSTCOMPILE)
td_symbol_list.o: $(srcdir)/glibc-dep/nptl_db/td_symbol_list.c
$(COMPILE) $(srcdir)/glibc-dep/nptl_db/td_symbol_list.c
$(POSTCOMPILE)
fetch-value.o: $(srcdir)/glibc-dep/nptl_db/fetch-value.c
$(COMPILE) $(srcdir)/glibc-dep/nptl_db/fetch-value.c
$(POSTCOMPILE)
gdb_td_ta_new.o: $(srcdir)/glibc-dep/gdb_td_ta_new.c
$(COMPILE) $(srcdir)/glibc-dep/gdb_td_ta_new.c
$(POSTCOMPILE)
native_check.o: $(srcdir)/glibc-dep/native_check.c
$(COMPILE) $(srcdir)/glibc-dep/native_check.c
$(POSTCOMPILE)
td_thr_tlsbase.o: $(srcdir)/glibc-dep/nptl_db/td_thr_tlsbase.c
$(COMPILE) $(srcdir)/glibc-dep/nptl_db/td_thr_tlsbase.c
$(POSTCOMPILE)
td_thr_tls_get_addr.o: $(srcdir)/glibc-dep/nptl_db/td_thr_tls_get_addr.c
$(COMPILE) $(srcdir)/glibc-dep/nptl_db/td_thr_tls_get_addr.c
$(POSTCOMPILE)
# gdb/nat/ dependencies
#
# Need to explicitly specify the compile rule as make will do nothing
Expand Down
6 changes: 6 additions & 0 deletions gdb/config/glibc.mh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# GLIBC fragment comes in here
GLIBCFILES = td_symbol_list.o \
fetch-value.o gdb_td_ta_new.o \
td_thr_tlsbase.o td_thr_tls_get_addr.o \
td_ta_map_lwp2thr.o native_check.o
INTERNAL_CFLAGS += -DCROSS_GDB
37 changes: 36 additions & 1 deletion gdb/configure
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,7 @@ PACKAGE_NAME
PATH_SEPARATOR
SHELL'
ac_subst_files='host_makefile_frag'
ac_subst_files2='cross_makefile_frag'
ac_user_opts='
enable_option_checking
enable_maintainer_mode
Expand Down Expand Up @@ -2574,6 +2575,20 @@ _ASBOX

if test -n "$ac_subst_files"; then
cat <<\_ASBOX
_ASBOX
echo
for ac_var in $ac_subst_vars
do
eval ac_val=\$$ac_var
case $ac_val in
*\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
esac
$as_echo "$ac_var='\''$ac_val'\''"
done | sort
echo

if test -n "$ac_subst_files2"; then
cat <<\_ASBOX
## ------------------- ##
## File substitutions. ##
## ------------------- ##
Expand All @@ -2590,6 +2605,21 @@ _ASBOX
echo
fi

if test -s confdefs.h; then
cat <<\_ASBOX
_ASBOX
echo
for ac_var in $ac_subst_files2
do
eval ac_val=\$$ac_var
case $ac_val in
*\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
esac
$as_echo "$ac_var='\''$ac_val'\''"
done | sort
echo
fi

if test -s confdefs.h; then
cat <<\_ASBOX
## ----------- ##
Expand Down Expand Up @@ -15901,7 +15931,6 @@ fi




if test "${gdb_native}" = "yes"; then
# We pick this up from the host configuration file (.mh) because we
# do not have a native configuration Makefile fragment.
Expand All @@ -15910,6 +15939,11 @@ s/NAT_FILE[ ]*=[ ]*\([^ ]*\)/\1/p
' ${host_makefile_frag}`
fi

if test "${gdb_native}" = "no" || test "${enable_targets}" != ""; then
cross_makefile_frag=${srcdir}/config/glibc.mh
else
cross_makefile_frag=/dev/null
fi

if test x"${gdb_osabi}" != x ; then

Expand Down Expand Up @@ -17403,6 +17437,7 @@ _ACEOF
echo "cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1" &&
echo 'cat >>"\$tmp/subs1.awk" <<\\_ACAWK &&' &&
echo "$ac_subst_files" | sed 's/.*/F["&"]="$&"/' &&
echo "$ac_subst_files2" | sed 's/.*/F["&"]="$&"/' &&
echo "_ACAWK" &&
echo "_ACEOF"
} >conf$$files.sh &&
Expand Down
4 changes: 2 additions & 2 deletions gdb/gdb_proc_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ EXTERN_C_POP
#include <sys/procfs.h>
#endif

EXTERN_C_PUSH
//EXTERN_C_PUSH

/* Functions in this interface return one of these status codes. */
typedef enum
Expand Down Expand Up @@ -159,7 +159,7 @@ extern ps_err_e ps_lsetxregs (struct ps_prochandle *ph, lwpid_t lwpid,
/* Log a message (sends to gdb_stderr). */
extern void ps_plog (const char *fmt, ...);

EXTERN_C_POP
//EXTERN_C_POP

#endif /* HAVE_PROC_SERVICE_H */

Expand Down
42 changes: 42 additions & 0 deletions gdb/glibc-dep/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
Files and functions taken from GLIBC source code (${glibc_src}/nptl_db/) necessary for getting
TLS variables (from coredump file) when cross GDB using GLIBC version 2.22 and higher are stored into
${gdb_src}/glibc-dep/nptl_db directory.

gdb_td_ta_new() is stored into ${gdb_src}/glibc-dep/ because functionality of function (td_ta_new())
from GLIBC source has changed in order to get current version of GLIBC from coredump file.

gdb_td_thr_tlsbase is function taken from nptl_db/td_thr_tlsbase.c,
which has modified in GLIBC 2.22 and higher, so all dependent files had to be included into GDB source.

------------------------

${gdb_src}/glibc-dep/nptl_db

* thread_dbP.h
Taken needed definitions and macros.

* db-symbols.h
Unmodified.

* fetch-value.c
Taken needed functions.

* structs.def
Unmodified.

* td_symbol_list.c
Taken necessary definitions and modified td_lookup function.

* td_ta_map_lwp2thr.c
Taken only __td_ta_lookup_th_unique function unmodified.

* td_thr_tlsbase.c
gdb_td_thr_tlsbase(), modified in order to work with all GLIBC versions.
${gdb_src}/gdb/config/${arch}/tls.h depending on architecture got different
values of TLS_TCB_AT_TP and TLS_DTV_AT_TP macros taken
from ${glibc_src}/sysdeps/${arch}/nptl/tls.h.

* td_thr_tls_get_addr.c
Unmodified.

-------------------------
72 changes: 72 additions & 0 deletions gdb/glibc-dep/gdb_td_ta_new.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <version.h>

#include "nptl_db/thread_dbP.h"
#include "../nat/gdb_thread_db.h"
#include "../../../bfd/bfd.h"
#include "nptl_db/tls.h"

static td_err_e
init_target_dep_constants()
{
struct bfd_arch_info *bfdarch = gdbarch_bfd_arch_info (target_gdbarch ());
unsigned arch = bfdarch->arch;
switch(arch){
case bfd_arch_mips:
tls_tcb_at_tp = 0;
tls_dtv_at_tp = 1;
forced_dynamic_tls_offset = -2;
no_tls_offset = -1;
tcb_alignment = 16;
break;
case bfd_arch_arm:
tls_tcb_at_tp = 0;
tls_dtv_at_tp = 1;
forced_dynamic_tls_offset = -2;
no_tls_offset = -1;
tcb_alignment = 0; //set to zero because not in use for TLS_PRE_TCB_SIZE
break;
case bfd_arch_i386:
tls_tcb_at_tp = 1;
tls_dtv_at_tp = 0;
forced_dynamic_tls_offset = -1;
no_tls_offset = 0;
tcb_alignment = 0; //set to zero because not in use for TLS_PRE_TCB_SIZE
break;
case bfd_arch_powerpc:
tls_tcb_at_tp = 0;
tls_dtv_at_tp = 1;
forced_dynamic_tls_offset = -2;
no_tls_offset = -1;
tcb_alignment = 48;
break;
case bfd_arch_aarch64:
tls_tcb_at_tp = 0;
tls_dtv_at_tp = 1;
forced_dynamic_tls_offset = -1;
no_tls_offset = 0;
tcb_alignment = 0; //set to zero because not in use for TLS_PRE_TCB_SIZE
break;
default:
return TD_ERR;
}
return TD_OK;
}


td_err_e
gdb_td_ta_new (struct ps_prochandle *ps, td_thragent_t **ta)
{
psaddr_t versaddr;

/* Check whether the versions match. */
if (td_lookup (ps, SYM_nptl_version, &versaddr) != PS_OK)
return TD_NOLIBTHREAD;
if (ps_pdread (ps, versaddr, versbuf, sizeof (versbuf)) != PS_OK)
return TD_ERR;
if(init_target_dep_constants() != TD_OK)
return TD_ERR;
return TD_OK;
}
40 changes: 40 additions & 0 deletions gdb/glibc-dep/native_check.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include "native_check.h"
#include <string.h>
#include <stdio.h>
#include "../config.h"
#include "../../../bfd/bfd.h"
#include <sys/utsname.h>

unsigned get_host_mach(char *name_of_arch){
unsigned result;

if(strstr(name_of_arch, "x86_64") != NULL)
result = bfd_arch_i386;
else if(strstr(name_of_arch, "i386") != NULL)
result = bfd_arch_i386;
else if(strstr(name_of_arch, "mips") != NULL)
result = bfd_arch_mips;
else if(strstr(name_of_arch, "arm") != NULL)
result = bfd_arch_arm;
else if(strstr(name_of_arch, "powerpc") != NULL)
result = bfd_arch_powerpc;
else if(strstr(name_of_arch, "aarch64") != NULL)
result = bfd_arch_aarch64;
else
result = -1;

return result;
}

int native_check(unsigned bfd_arch){
struct utsname buf;
if(uname(&buf) == 0){
unsigned host_mach = get_host_mach(buf.machine);

if(host_mach == bfd_arch)
return 0; //zero if it's host
else
return 1;
}
return -1;
}
2 changes: 2 additions & 0 deletions gdb/glibc-dep/native_check.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
unsigned get_host_mach(char *name_of_arch);
int native_check(unsigned bfd_arch);
51 changes: 51 additions & 0 deletions gdb/glibc-dep/nptl_db/db-symbols.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* List of symbols in libpthread examined by libthread_db.
Copyright (C) 2009-2016 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */

#define DOT(x) x /* No prefix. */

#define STRINGIFY(name) STRINGIFY_1(name)
#define STRINGIFY_1(name) #name

#define DB_STRUCT(type) \
DB_LOOKUP_NAME (SYM_SIZEOF_##type, _thread_db_sizeof_##type)
#define DB_STRUCT_FIELD(type, field) \
DB_LOOKUP_NAME (SYM_##type##_FIELD_##field, _thread_db_##type##_##field)
#define DB_SYMBOL(name) \
DB_LOOKUP_NAME (SYM_##name, name)
#define DB_FUNCTION(name) \
DB_LOOKUP_NAME (SYM_##name, DOT (name))
#define DB_VARIABLE(name) \
DB_LOOKUP_NAME (SYM_##name, name) \
DB_LOOKUP_NAME (SYM_DESC_##name, _thread_db_##name)

# include "structs.def"

# undef DB_STRUCT
# undef DB_FUNCTION
# undef DB_SYMBOL
# undef DB_VARIABLE
# undef DOT

DB_LOOKUP_NAME_TH_UNIQUE (SYM_TH_UNIQUE_REGISTER64, _thread_db_register64)
DB_LOOKUP_NAME_TH_UNIQUE (SYM_TH_UNIQUE_REGISTER32, _thread_db_register32)
DB_LOOKUP_NAME_TH_UNIQUE (SYM_TH_UNIQUE_CONST_THREAD_AREA,
_thread_db_const_thread_area)
DB_LOOKUP_NAME_TH_UNIQUE (SYM_TH_UNIQUE_REGISTER32_THREAD_AREA,
_thread_db_register32_thread_area)
DB_LOOKUP_NAME_TH_UNIQUE (SYM_TH_UNIQUE_REGISTER64_THREAD_AREA,
_thread_db_register64_thread_area)
Loading

0 comments on commit c4e3d7a

Please sign in to comment.