mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 12:34:43 +00:00
Compare commits
8 Commits
binutils-2
...
users/arne
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95368b12b2 | ||
|
|
654305d228 | ||
|
|
368915f5f1 | ||
|
|
44098f3fb6 | ||
|
|
fc83f37b41 | ||
|
|
757186093b | ||
|
|
75d79af413 | ||
|
|
c48c4ca231 |
@@ -805,6 +805,7 @@ ALL_TARGET_OBS = \
|
||||
iq2000-tdep.o \
|
||||
linux-record.o \
|
||||
linux-tdep.o \
|
||||
lk-low.o \
|
||||
lm32-tdep.o \
|
||||
m32c-tdep.o \
|
||||
m32r-linux-tdep.o \
|
||||
@@ -847,6 +848,8 @@ ALL_TARGET_OBS = \
|
||||
rs6000-tdep.o \
|
||||
rx-tdep.o \
|
||||
s390-linux-tdep.o \
|
||||
s390-lk-tdep.o \
|
||||
s390-tdep.o \
|
||||
score-tdep.o \
|
||||
sh-linux-tdep.o \
|
||||
sh-nbsd-tdep.o \
|
||||
@@ -1092,6 +1095,10 @@ SFILES = \
|
||||
jit.c \
|
||||
language.c \
|
||||
linespec.c \
|
||||
lk-cmds.c \
|
||||
lk-lists.c \
|
||||
lk-low.c \
|
||||
lk-modules.c \
|
||||
location.c \
|
||||
m2-exp.y \
|
||||
m2-lang.c \
|
||||
@@ -1339,6 +1346,10 @@ HFILES_NO_SRCDIR = \
|
||||
linux-nat.h \
|
||||
linux-record.h \
|
||||
linux-tdep.h \
|
||||
lk-cmds.h \
|
||||
lk-lists.h \
|
||||
lk-low.h \
|
||||
lk-modules.h \
|
||||
location.h \
|
||||
m2-lang.h \
|
||||
m32r-tdep.h \
|
||||
@@ -1396,7 +1407,7 @@ HFILES_NO_SRCDIR = \
|
||||
remote-notif.h \
|
||||
rs6000-aix-tdep.h \
|
||||
rs6000-tdep.h \
|
||||
s390-linux-tdep.h \
|
||||
s390-tdep.h \
|
||||
score-tdep.h \
|
||||
selftest-arch.h \
|
||||
sentinel-frame.h \
|
||||
@@ -1699,6 +1710,9 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
|
||||
jit.o \
|
||||
language.o \
|
||||
linespec.o \
|
||||
lk-cmds.o \
|
||||
lk-lists.o \
|
||||
lk-modules.o \
|
||||
location.o \
|
||||
m2-lang.o \
|
||||
m2-typeprint.o \
|
||||
@@ -2531,6 +2545,10 @@ ALLDEPFILES = \
|
||||
linux-fork.c \
|
||||
linux-record.c \
|
||||
linux-tdep.c \
|
||||
lk-cmds.c \
|
||||
lk-lists.c \
|
||||
lk-low.c \
|
||||
lk-modules.c \
|
||||
lm32-tdep.c \
|
||||
m32r-linux-nat.c \
|
||||
m32r-linux-tdep.c \
|
||||
@@ -2586,6 +2604,8 @@ ALLDEPFILES = \
|
||||
rx-tdep.c \
|
||||
s390-linux-nat.c \
|
||||
s390-linux-tdep.c \
|
||||
s390-lk-tdep.c \
|
||||
s390-tdep.c \
|
||||
score-tdep.c \
|
||||
ser-go32.c \
|
||||
ser-mingw.c \
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "filestuff.h"
|
||||
#include "extension.h"
|
||||
#include "gdb/section-scripts.h"
|
||||
#include <string>
|
||||
|
||||
/* The section to look in for auto-loaded scripts (in file formats that
|
||||
support sections).
|
||||
@@ -175,21 +176,20 @@ static VEC (char_ptr) *auto_load_safe_path_vec;
|
||||
this vector must be freed by free_char_ptr_vec by the caller. */
|
||||
|
||||
static VEC (char_ptr) *
|
||||
auto_load_expand_dir_vars (const char *string)
|
||||
auto_load_expand_dir_vars (std::string orig)
|
||||
{
|
||||
VEC (char_ptr) *dir_vec;
|
||||
char *s;
|
||||
std::string str = orig;
|
||||
|
||||
s = xstrdup (string);
|
||||
substitute_path_component (&s, "$datadir", gdb_datadir);
|
||||
substitute_path_component (&s, "$debugdir", debug_file_directory);
|
||||
substitute_path_component (str, "$datadir", gdb_datadir);
|
||||
substitute_path_component (str, "$debugdir", debug_file_directory);
|
||||
|
||||
if (debug_auto_load && strcmp (s, string) != 0)
|
||||
if (debug_auto_load && str.compare (orig) != 0)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
_("auto-load: Expanded $-variables to \"%s\".\n"), s);
|
||||
_("auto-load: Expanded $-variables to \"%s\".\n"),
|
||||
str.c_str ());
|
||||
|
||||
dir_vec = dirnames_to_char_ptr_vec (s);
|
||||
xfree(s);
|
||||
dir_vec = dirnames_to_char_ptr_vec (str.c_str ());
|
||||
|
||||
return dir_vec;
|
||||
}
|
||||
|
||||
@@ -83,6 +83,17 @@ startswith (const char *string, const char *pattern)
|
||||
return strncmp (string, pattern, strlen (pattern)) == 0;
|
||||
}
|
||||
|
||||
/* Return non-zero if the end of STRING matches PATTERN, zero
|
||||
otherwise. */
|
||||
|
||||
static inline int
|
||||
endswith (const char *string, const char *pattern)
|
||||
{
|
||||
return (strlen (string) > strlen (pattern)
|
||||
&& strncmp (string + strlen (string) - strlen (pattern), pattern,
|
||||
strlen (pattern)) == 0);
|
||||
}
|
||||
|
||||
ULONGEST strtoulst (const char *num, const char **trailer, int base);
|
||||
|
||||
/* Skip leading whitespace characters in INP, returning an updated
|
||||
|
||||
@@ -76,6 +76,7 @@ WHICH = aarch64 \
|
||||
s390-linux32v2 s390-linux64v2 s390x-linux64v2 \
|
||||
s390-te-linux64 s390x-te-linux64 s390-vx-linux64 s390x-vx-linux64 \
|
||||
s390-tevx-linux64 s390x-tevx-linux64 \
|
||||
s390x-cr-linux64 s390x-vxcr-linux64 \
|
||||
tic6x-c64xp tic6x-c64x tic6x-c62x \
|
||||
tic6x-c64xp-linux tic6x-c64x-linux tic6x-c62x-linux
|
||||
|
||||
@@ -109,6 +110,8 @@ s390x-linux64v2-expedite = r14,r15,pswa
|
||||
s390x-te-linux64-expedite = r14,r15,pswa
|
||||
s390x-vx-linux64-expedite = r14,r15,pswa
|
||||
s390x-tevx-linux64-expedite = r14,r15,pswa
|
||||
s390x-cr-linux64-expedite = r14,r15,pswa
|
||||
s390x-vxcr-linux64-expedite = r14,r15,pswa
|
||||
tic6x-c64xp-expedite = A15,PC
|
||||
tic6x-c64x-expedite = A15,PC
|
||||
tic6x-c62x-expedite = A15,PC
|
||||
@@ -214,14 +217,16 @@ XMLTOC = \
|
||||
s390-linux64v1.xml \
|
||||
s390-linux64v2.xml \
|
||||
s390-te-linux64.xml \
|
||||
s390-vx-linux64.xml \
|
||||
s390-tevx-linux64.xml \
|
||||
s390x-linux64.xml \
|
||||
s390x-linux64v1.xml \
|
||||
s390x-linux64v2.xml \
|
||||
s390x-te-linux64.xml \
|
||||
s390-tevx-linux64.xml \
|
||||
s390-vx-linux64.xml \
|
||||
s390x-tevx-linux64.xml \
|
||||
s390x-vx-linux64.xml \
|
||||
s390x-cr-linux64.xml \
|
||||
s390x-tevx-linux64.xml \
|
||||
s390x-vxcr-linux64.xml \
|
||||
tic6x-c62x-linux.xml \
|
||||
tic6x-c62x.xml \
|
||||
tic6x-c64x-linux.xml \
|
||||
|
||||
26
gdb/features/s390-cr.xml
Normal file
26
gdb/features/s390-cr.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved. -->
|
||||
|
||||
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
||||
<feature name="org.gnu.gdb.s390.cr">
|
||||
<reg name="cr0" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr1" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr2" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr3" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr4" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr5" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr6" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr7" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr8" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr9" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr10" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr11" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr12" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr13" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr14" bitsize="64" type="uint64" group="control"/>
|
||||
<reg name="cr15" bitsize="64" type="uint64" group="control"/>
|
||||
</feature>
|
||||
99
gdb/features/s390x-cr-linux64.c
Normal file
99
gdb/features/s390x-cr-linux64.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro:
|
||||
Original: s390x-cr-linux64.xml */
|
||||
|
||||
#include "defs.h"
|
||||
#include "osabi.h"
|
||||
#include "target-descriptions.h"
|
||||
|
||||
struct target_desc *tdesc_s390x_cr_linux64;
|
||||
static void
|
||||
initialize_tdesc_s390x_cr_linux64 (void)
|
||||
{
|
||||
struct target_desc *result = allocate_target_description ();
|
||||
struct tdesc_feature *feature;
|
||||
|
||||
set_tdesc_architecture (result, bfd_scan_arch ("s390:64-bit"));
|
||||
|
||||
feature = tdesc_create_feature (result, "org.gnu.gdb.s390.core");
|
||||
tdesc_create_reg (feature, "pswm", 0, 1, "psw", 64, "uint64");
|
||||
tdesc_create_reg (feature, "pswa", 1, 1, "psw", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r0", 2, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r1", 3, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r2", 4, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r3", 5, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r4", 6, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r5", 7, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r6", 8, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r7", 9, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r8", 10, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r9", 11, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r10", 12, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r11", 13, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r12", 14, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r13", 15, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r14", 16, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r15", 17, 1, "general", 64, "uint64");
|
||||
|
||||
feature = tdesc_create_feature (result, "org.gnu.gdb.s390.acr");
|
||||
tdesc_create_reg (feature, "acr0", 18, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr1", 19, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr2", 20, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr3", 21, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr4", 22, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr5", 23, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr6", 24, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr7", 25, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr8", 26, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr9", 27, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr10", 28, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr11", 29, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr12", 30, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr13", 31, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr14", 32, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr15", 33, 1, "access", 32, "uint32");
|
||||
|
||||
feature = tdesc_create_feature (result, "org.gnu.gdb.s390.fpr");
|
||||
tdesc_create_reg (feature, "fpc", 34, 1, "float", 32, "uint32");
|
||||
tdesc_create_reg (feature, "f0", 35, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f1", 36, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f2", 37, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f3", 38, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f4", 39, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f5", 40, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f6", 41, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f7", 42, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f8", 43, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f9", 44, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f10", 45, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f11", 46, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f12", 47, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f13", 48, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f14", 49, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f15", 50, 1, "float", 64, "ieee_double");
|
||||
|
||||
feature = tdesc_create_feature (result, "org.gnu.gdb.s390.cr");
|
||||
tdesc_create_reg (feature, "cr0", 51, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr1", 52, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr2", 53, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr3", 54, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr4", 55, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr5", 56, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr6", 57, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr7", 58, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr8", 59, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr9", 60, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr10", 61, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr11", 62, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr12", 63, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr13", 64, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr14", 65, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr15", 66, 1, "control", 64, "uint64");
|
||||
|
||||
feature = tdesc_create_feature (result, "org.gnu.gdb.s390.privileged");
|
||||
tdesc_create_reg (feature, "timer", 67, 1, "system", 64, "uint64");
|
||||
tdesc_create_reg (feature, "todcmp", 68, 1, "system", 64, "uint64");
|
||||
tdesc_create_reg (feature, "todpreg", 69, 1, "system", 32, "uint32");
|
||||
tdesc_create_reg (feature, "prefix", 70, 1, "system", 32, "uint32");
|
||||
|
||||
tdesc_s390x_cr_linux64 = result;
|
||||
}
|
||||
24
gdb/features/s390x-cr-linux64.xml
Normal file
24
gdb/features/s390x-cr-linux64.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved. -->
|
||||
|
||||
<!-- S/390 64-bit kernel-level code. -->
|
||||
|
||||
<!DOCTYPE target SYSTEM "gdb-target.dtd">
|
||||
<target>
|
||||
<architecture>s390:64-bit</architecture>
|
||||
<xi:include href="s390x-core64.xml"/>
|
||||
<xi:include href="s390-acr.xml"/>
|
||||
<xi:include href="s390-fpr.xml"/>
|
||||
<xi:include href="s390-cr.xml"/>
|
||||
|
||||
<feature name="org.gnu.gdb.s390.privileged">
|
||||
<reg name="timer" bitsize="64" type="uint64" group="system"/>
|
||||
<reg name="todcmp" bitsize="64" type="uint64" group="system"/>
|
||||
<reg name="todpreg" bitsize="32" type="uint32" group="system"/>
|
||||
<reg name="prefix" bitsize="32" type="uint32" group="system"/>
|
||||
</feature>
|
||||
</target>
|
||||
169
gdb/features/s390x-vxcr-linux64.c
Normal file
169
gdb/features/s390x-vxcr-linux64.c
Normal file
@@ -0,0 +1,169 @@
|
||||
/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro:
|
||||
Original: s390x-vxcr-linux64.xml */
|
||||
|
||||
#include "defs.h"
|
||||
#include "osabi.h"
|
||||
#include "target-descriptions.h"
|
||||
|
||||
struct target_desc *tdesc_s390x_vxcr_linux64;
|
||||
static void
|
||||
initialize_tdesc_s390x_vxcr_linux64 (void)
|
||||
{
|
||||
struct target_desc *result = allocate_target_description ();
|
||||
struct tdesc_feature *feature;
|
||||
struct tdesc_type *field_type;
|
||||
struct tdesc_type *type;
|
||||
|
||||
set_tdesc_architecture (result, bfd_scan_arch ("s390:64-bit"));
|
||||
|
||||
feature = tdesc_create_feature (result, "org.gnu.gdb.s390.core");
|
||||
tdesc_create_reg (feature, "pswm", 0, 1, "psw", 64, "uint64");
|
||||
tdesc_create_reg (feature, "pswa", 1, 1, "psw", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r0", 2, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r1", 3, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r2", 4, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r3", 5, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r4", 6, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r5", 7, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r6", 8, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r7", 9, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r8", 10, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r9", 11, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r10", 12, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r11", 13, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r12", 14, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r13", 15, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r14", 16, 1, "general", 64, "uint64");
|
||||
tdesc_create_reg (feature, "r15", 17, 1, "general", 64, "uint64");
|
||||
|
||||
feature = tdesc_create_feature (result, "org.gnu.gdb.s390.acr");
|
||||
tdesc_create_reg (feature, "acr0", 18, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr1", 19, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr2", 20, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr3", 21, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr4", 22, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr5", 23, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr6", 24, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr7", 25, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr8", 26, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr9", 27, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr10", 28, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr11", 29, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr12", 30, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr13", 31, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr14", 32, 1, "access", 32, "uint32");
|
||||
tdesc_create_reg (feature, "acr15", 33, 1, "access", 32, "uint32");
|
||||
|
||||
feature = tdesc_create_feature (result, "org.gnu.gdb.s390.fpr");
|
||||
tdesc_create_reg (feature, "fpc", 34, 1, "float", 32, "uint32");
|
||||
tdesc_create_reg (feature, "f0", 35, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f1", 36, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f2", 37, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f3", 38, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f4", 39, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f5", 40, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f6", 41, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f7", 42, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f8", 43, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f9", 44, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f10", 45, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f11", 46, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f12", 47, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f13", 48, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f14", 49, 1, "float", 64, "ieee_double");
|
||||
tdesc_create_reg (feature, "f15", 50, 1, "float", 64, "ieee_double");
|
||||
|
||||
feature = tdesc_create_feature (result, "org.gnu.gdb.s390.vx");
|
||||
field_type = tdesc_named_type (feature, "ieee_single");
|
||||
tdesc_create_vector (feature, "v4f", field_type, 4);
|
||||
|
||||
field_type = tdesc_named_type (feature, "ieee_double");
|
||||
tdesc_create_vector (feature, "v2d", field_type, 2);
|
||||
|
||||
field_type = tdesc_named_type (feature, "int8");
|
||||
tdesc_create_vector (feature, "v16i8", field_type, 16);
|
||||
|
||||
field_type = tdesc_named_type (feature, "int16");
|
||||
tdesc_create_vector (feature, "v8i16", field_type, 8);
|
||||
|
||||
field_type = tdesc_named_type (feature, "int32");
|
||||
tdesc_create_vector (feature, "v4i32", field_type, 4);
|
||||
|
||||
field_type = tdesc_named_type (feature, "int64");
|
||||
tdesc_create_vector (feature, "v2i64", field_type, 2);
|
||||
|
||||
type = tdesc_create_union (feature, "vec128");
|
||||
field_type = tdesc_named_type (feature, "v4f");
|
||||
tdesc_add_field (type, "v4_float", field_type);
|
||||
field_type = tdesc_named_type (feature, "v2d");
|
||||
tdesc_add_field (type, "v2_double", field_type);
|
||||
field_type = tdesc_named_type (feature, "v16i8");
|
||||
tdesc_add_field (type, "v16_int8", field_type);
|
||||
field_type = tdesc_named_type (feature, "v8i16");
|
||||
tdesc_add_field (type, "v8_int16", field_type);
|
||||
field_type = tdesc_named_type (feature, "v4i32");
|
||||
tdesc_add_field (type, "v4_int32", field_type);
|
||||
field_type = tdesc_named_type (feature, "v2i64");
|
||||
tdesc_add_field (type, "v2_int64", field_type);
|
||||
field_type = tdesc_named_type (feature, "uint128");
|
||||
tdesc_add_field (type, "uint128", field_type);
|
||||
|
||||
tdesc_create_reg (feature, "v0l", 51, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v1l", 52, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v2l", 53, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v3l", 54, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v4l", 55, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v5l", 56, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v6l", 57, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v7l", 58, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v8l", 59, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v9l", 60, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v10l", 61, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v11l", 62, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v12l", 63, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v13l", 64, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v14l", 65, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v15l", 66, 1, NULL, 64, "uint64");
|
||||
tdesc_create_reg (feature, "v16", 67, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v17", 68, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v18", 69, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v19", 70, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v20", 71, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v21", 72, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v22", 73, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v23", 74, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v24", 75, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v25", 76, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v26", 77, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v27", 78, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v28", 79, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v29", 80, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v30", 81, 1, NULL, 128, "vec128");
|
||||
tdesc_create_reg (feature, "v31", 82, 1, NULL, 128, "vec128");
|
||||
|
||||
feature = tdesc_create_feature (result, "org.gnu.gdb.s390.cr");
|
||||
tdesc_create_reg (feature, "cr0", 83, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr1", 84, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr2", 85, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr3", 86, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr4", 87, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr5", 88, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr6", 89, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr7", 90, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr8", 91, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr9", 92, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr10", 93, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr11", 94, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr12", 95, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr13", 96, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr14", 97, 1, "control", 64, "uint64");
|
||||
tdesc_create_reg (feature, "cr15", 98, 1, "control", 64, "uint64");
|
||||
|
||||
feature = tdesc_create_feature (result, "org.gnu.gdb.s390.privileged");
|
||||
tdesc_create_reg (feature, "timer", 99, 1, "system", 64, "uint64");
|
||||
tdesc_create_reg (feature, "todcmp", 100, 1, "system", 64, "uint64");
|
||||
tdesc_create_reg (feature, "todpreg", 101, 1, "system", 32, "uint32");
|
||||
tdesc_create_reg (feature, "prefix", 102, 1, "system", 32, "uint32");
|
||||
|
||||
tdesc_s390x_vxcr_linux64 = result;
|
||||
}
|
||||
25
gdb/features/s390x-vxcr-linux64.xml
Normal file
25
gdb/features/s390x-vxcr-linux64.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved. -->
|
||||
|
||||
<!-- S/390 64-bit kernel-level code. -->
|
||||
|
||||
<!DOCTYPE target SYSTEM "gdb-target.dtd">
|
||||
<target>
|
||||
<architecture>s390:64-bit</architecture>
|
||||
<xi:include href="s390x-core64.xml"/>
|
||||
<xi:include href="s390-acr.xml"/>
|
||||
<xi:include href="s390-fpr.xml"/>
|
||||
<xi:include href="s390-vx.xml"/>
|
||||
<xi:include href="s390-cr.xml"/>
|
||||
|
||||
<feature name="org.gnu.gdb.s390.privileged">
|
||||
<reg name="timer" bitsize="64" type="uint64" group="system"/>
|
||||
<reg name="todcmp" bitsize="64" type="uint64" group="system"/>
|
||||
<reg name="todpreg" bitsize="32" type="uint32" group="system"/>
|
||||
<reg name="prefix" bitsize="32" type="uint32" group="system"/>
|
||||
</feature>
|
||||
</target>
|
||||
@@ -339,6 +339,7 @@ struct gdbarch
|
||||
gdbarch_gcc_target_options_ftype *gcc_target_options;
|
||||
gdbarch_gnu_triplet_regexp_ftype *gnu_triplet_regexp;
|
||||
gdbarch_addressable_memory_unit_size_ftype *addressable_memory_unit_size;
|
||||
gdbarch_lk_init_private_ftype *lk_init_private;
|
||||
};
|
||||
|
||||
/* Create a new ``struct gdbarch'' based on information provided by
|
||||
@@ -1123,6 +1124,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: iterate_over_regset_sections = <%s>\n",
|
||||
host_address_to_string (gdbarch->iterate_over_regset_sections));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: gdbarch_lk_init_private_p() = %d\n",
|
||||
gdbarch_lk_init_private_p (gdbarch));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: lk_init_private = <%s>\n",
|
||||
host_address_to_string (gdbarch->lk_init_private));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: long_bit = %s\n",
|
||||
plongest (gdbarch->long_bit));
|
||||
@@ -4956,6 +4963,30 @@ set_gdbarch_addressable_memory_unit_size (struct gdbarch *gdbarch,
|
||||
gdbarch->addressable_memory_unit_size = addressable_memory_unit_size;
|
||||
}
|
||||
|
||||
int
|
||||
gdbarch_lk_init_private_p (struct gdbarch *gdbarch)
|
||||
{
|
||||
gdb_assert (gdbarch != NULL);
|
||||
return gdbarch->lk_init_private != NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gdbarch_lk_init_private (struct gdbarch *gdbarch)
|
||||
{
|
||||
gdb_assert (gdbarch != NULL);
|
||||
gdb_assert (gdbarch->lk_init_private != NULL);
|
||||
if (gdbarch_debug >= 2)
|
||||
fprintf_unfiltered (gdb_stdlog, "gdbarch_lk_init_private called\n");
|
||||
gdbarch->lk_init_private (gdbarch);
|
||||
}
|
||||
|
||||
void
|
||||
set_gdbarch_lk_init_private (struct gdbarch *gdbarch,
|
||||
gdbarch_lk_init_private_ftype lk_init_private)
|
||||
{
|
||||
gdbarch->lk_init_private = lk_init_private;
|
||||
}
|
||||
|
||||
|
||||
/* Keep a registry of per-architecture data-pointers required by GDB
|
||||
modules. */
|
||||
|
||||
@@ -1545,6 +1545,14 @@ typedef int (gdbarch_addressable_memory_unit_size_ftype) (struct gdbarch *gdbarc
|
||||
extern int gdbarch_addressable_memory_unit_size (struct gdbarch *gdbarch);
|
||||
extern void set_gdbarch_addressable_memory_unit_size (struct gdbarch *gdbarch, gdbarch_addressable_memory_unit_size_ftype *addressable_memory_unit_size);
|
||||
|
||||
/* Initiate architecture dependent private data for the linux-kernel target. */
|
||||
|
||||
extern int gdbarch_lk_init_private_p (struct gdbarch *gdbarch);
|
||||
|
||||
typedef void (gdbarch_lk_init_private_ftype) (struct gdbarch *gdbarch);
|
||||
extern void gdbarch_lk_init_private (struct gdbarch *gdbarch);
|
||||
extern void set_gdbarch_lk_init_private (struct gdbarch *gdbarch, gdbarch_lk_init_private_ftype *lk_init_private);
|
||||
|
||||
/* Definition for an unknown syscall, used basically in error-cases. */
|
||||
#define UNKNOWN_SYSCALL (-1)
|
||||
|
||||
|
||||
@@ -1163,6 +1163,10 @@ m:const char *:gnu_triplet_regexp:void:::default_gnu_triplet_regexp::0
|
||||
# each address in memory.
|
||||
m:int:addressable_memory_unit_size:void:::default_addressable_memory_unit_size::0
|
||||
|
||||
# Initialize architecture dependent private data for the linux-kernel
|
||||
# target.
|
||||
M:void:lk_init_private:void:
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
253
gdb/lk-cmds.c
Normal file
253
gdb/lk-cmds.c
Normal file
@@ -0,0 +1,253 @@
|
||||
/* Commands for Linux kernel target.
|
||||
|
||||
Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
#include "cli/cli-decode.h"
|
||||
#include "gdbcore.h"
|
||||
#include "inferior.h"
|
||||
#include "lk-lists.h"
|
||||
#include "lk-low.h"
|
||||
#include "lk-modules.h"
|
||||
#include "typeprint.h"
|
||||
#include "valprint.h"
|
||||
|
||||
|
||||
/* Print line for module MOD to UIOUT for lsmod command. */
|
||||
|
||||
static bool
|
||||
lk_lsmod_print_single_module (struct ui_out *uiout, CORE_ADDR mod)
|
||||
{
|
||||
char *src_list, name[LK_MODULE_NAME_LEN + 2];
|
||||
CORE_ADDR next, src_list_addr;
|
||||
size_t src_num, src_size, list_len;
|
||||
bool loaded;
|
||||
struct cleanup *ui_chain;
|
||||
|
||||
|
||||
/* Get name. */
|
||||
read_memory_string (mod + LK_OFFSET (module, name), name + 1,
|
||||
LK_MODULE_NAME_LEN);
|
||||
loaded = lk_modules_debug_info_loaded (name + 1);
|
||||
name[0] = loaded ? ' ' : '*' ;
|
||||
name[LK_MODULE_NAME_LEN + 1] = '\0';
|
||||
|
||||
/* Get size. */
|
||||
if (LK_FIELD (module, module_core))
|
||||
{
|
||||
src_size = lk_read_uint (mod + LK_OFFSET (module, init_size));
|
||||
src_size += lk_read_uint (mod + LK_OFFSET (module, core_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
src_size = lk_read_uint (mod + LK_OFFSET (module, init_layout)
|
||||
+ LK_OFFSET (module_layout, size));
|
||||
src_size += lk_read_uint (mod + LK_OFFSET (module, core_layout)
|
||||
+ LK_OFFSET (module_layout, size));
|
||||
}
|
||||
|
||||
/* Get number of sources and list of their names. */
|
||||
src_num = 0;
|
||||
src_list_addr = mod + LK_OFFSET (module, source_list);
|
||||
src_list = xstrdup ("");
|
||||
list_len = 0;
|
||||
|
||||
lk_list_for_each (next, src_list_addr, module, source_list)
|
||||
{
|
||||
char src_name[LK_MODULE_NAME_LEN + 1];
|
||||
CORE_ADDR src_mod, src_addr;
|
||||
|
||||
src_addr = (LK_CONTAINER_OF (next, module_use, source_list)
|
||||
+ LK_OFFSET (module_use, source));
|
||||
src_mod = lk_read_addr (src_addr);
|
||||
read_memory_string (src_mod + LK_OFFSET (module, name), src_name,
|
||||
LK_MODULE_NAME_LEN);
|
||||
|
||||
/* 2 = strlen (", "). */
|
||||
list_len += strlen (src_name) + 2;
|
||||
src_list = reconcat (src_list, src_list, src_name, ", ", NULL);
|
||||
|
||||
src_num++;
|
||||
}
|
||||
|
||||
make_cleanup (xfree, src_list);
|
||||
/* Remove trailing comma. */
|
||||
if (strlen (src_list) >= 2)
|
||||
src_list [list_len - 2] = '\0';
|
||||
|
||||
ui_chain = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
|
||||
|
||||
uiout->field_fmt ("addr", "0x%s",
|
||||
phex(mod, lk_builtin_type_size (unsigned_long)));
|
||||
uiout->field_string ("module", name);
|
||||
uiout->field_int ("size", src_size);
|
||||
uiout->field_int ("src_num", src_num);
|
||||
uiout->field_string ("src_list", src_list);
|
||||
uiout->text ("\n");
|
||||
|
||||
do_cleanups (ui_chain);
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/* Print information about loaded kernel modules. Output equivalent to
|
||||
lsmod, but also prints the address of the corrensponfing struct module.
|
||||
Marks modules with missing debug info with an asterix '*'. */
|
||||
|
||||
void
|
||||
lk_lsmod (char *args, int from_tty)
|
||||
{
|
||||
struct ui_out *uiout;
|
||||
struct cleanup *ui_chain;
|
||||
CORE_ADDR modules, next;
|
||||
bool all_loaded = true;
|
||||
|
||||
uiout = current_uiout;
|
||||
ui_chain = make_cleanup_ui_out_table_begin_end (uiout, 5, -1,
|
||||
"ModuleTable");
|
||||
uiout->table_header (14, ui_left, "addr", "ADDR");
|
||||
uiout->table_header (20, ui_left, "module", "Module");
|
||||
uiout->table_header (7, ui_right, "size", "Size");
|
||||
uiout->table_header (4, ui_right, "src_num", "");
|
||||
uiout->table_header (40, ui_left, "src_list", "Used by");
|
||||
|
||||
uiout->table_body ();
|
||||
|
||||
modules = LK_ADDR (modules);
|
||||
lk_list_for_each (next, modules, module, list)
|
||||
{
|
||||
CORE_ADDR mod;
|
||||
mod = LK_CONTAINER_OF (next, module, list);
|
||||
all_loaded &= lk_lsmod_print_single_module (uiout, mod);
|
||||
}
|
||||
if (!all_loaded)
|
||||
uiout->text ("(*) Missing debug info for module.\n");
|
||||
|
||||
do_cleanups (ui_chain);
|
||||
}
|
||||
|
||||
static void
|
||||
lk_print_struct (char *args_, int from_tty)
|
||||
{
|
||||
struct format_data fmt;
|
||||
size_t pos;
|
||||
print_command_parse_format ((const char **) &args_, "print", &fmt);
|
||||
|
||||
if (!args_)
|
||||
return;
|
||||
|
||||
std::string args (args_);
|
||||
/* No address given default to behave like ptype. */
|
||||
if ((pos = args.find (" ")) == std::string::npos)
|
||||
{
|
||||
args = "struct " + args;
|
||||
char *tmp = xstrdup (args.c_str ());
|
||||
whatis_exp (tmp, 1);
|
||||
xfree (tmp);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
std::string type = args.substr (0, pos);
|
||||
std::string addr = args.substr (args.find_first_not_of (" ", pos));
|
||||
|
||||
if ((pos = type.find ("."))!= std::string::npos)
|
||||
{
|
||||
std::string field = type.substr (pos + 1);
|
||||
type = type.substr (0, pos);
|
||||
args = "((struct " + type + " *) " + addr + ")->" + field;
|
||||
}
|
||||
else if ((pos = type.find ("->"))!= std::string::npos)
|
||||
{
|
||||
std::string field = type.substr (pos + 2);
|
||||
type = type.substr (0, pos);
|
||||
args = "((struct " + type + " *) " + addr + ")->" + field;
|
||||
}
|
||||
else
|
||||
args = "*(struct " + type + " *) " + addr;
|
||||
|
||||
expression_up expr = parse_expression (args.c_str ());
|
||||
struct value *val = evaluate_expression (expr.get ());
|
||||
|
||||
print_value (val, &fmt);
|
||||
}
|
||||
|
||||
#include "gdbtypes.h"
|
||||
void
|
||||
lk_print_offset (char *args_, int from_tty)
|
||||
{
|
||||
std::string args (args_);
|
||||
std::string type, field;
|
||||
size_t pos;
|
||||
|
||||
if ((pos = args.find ('.')) != std::string::npos)
|
||||
{
|
||||
type = "struct " + args.substr (0, pos);
|
||||
field = args.substr (pos + 1);
|
||||
}
|
||||
else if ((pos = args.find ("->")) != std::string::npos)
|
||||
{
|
||||
type = "struct " + args.substr (0, pos);
|
||||
field = args.substr (pos + 2);
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
expression_up expr = parse_expression (type.c_str ());
|
||||
struct type *tp = value_type (evaluate_type (expr.get ()));
|
||||
|
||||
struct field *first = TYPE_FIELDS (tp);
|
||||
struct field *last = first + TYPE_NFIELDS (tp);
|
||||
|
||||
for (; first != last; first++)
|
||||
if (field.compare (first->name) == 0)
|
||||
break;
|
||||
|
||||
if (first == last)
|
||||
return;
|
||||
|
||||
size_t offset = FIELD_BITPOS (*first);
|
||||
|
||||
if (offset % TARGET_CHAR_BIT)
|
||||
printf_unfiltered ("offset = %lu + %lu\n", offset / 8, offset % TARGET_CHAR_BIT);
|
||||
else
|
||||
printf_unfiltered ("offset = %lu\n", offset / 8);
|
||||
}
|
||||
|
||||
void
|
||||
lk_init_cmds ()
|
||||
{
|
||||
add_com ("lsmod", class_vars, lk_lsmod, "\n\
|
||||
lsmod\n\n\
|
||||
List kernel modules as known by the kernel. The address belongs to the \
|
||||
corresponding struct module. \
|
||||
");
|
||||
|
||||
add_com ("struct", class_vars, lk_print_struct, "\n\
|
||||
struct <struct>.<field> <address>\n\n\
|
||||
Print content of field <field> in structure <struct> for structure located\n\
|
||||
at address <address>. If no field is given prints entire content of\n\
|
||||
<struct>. If neither <field> nor <address> is given prints type definition\n\
|
||||
of <struct> (equivalent to ptype).\
|
||||
");
|
||||
|
||||
add_com ("offset", class_vars, lk_print_offset, "\n\
|
||||
offset <struct>.<field>\n\n\
|
||||
Print offset of field <field> in structure <struct> in byte (+ bit for bit fields).\n\
|
||||
");
|
||||
}
|
||||
25
gdb/lk-cmds.h
Normal file
25
gdb/lk-cmds.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/* Commands for Linux kernel target.
|
||||
|
||||
Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef __LK_CMDS_H__
|
||||
#define __LK_CMDS_H__
|
||||
|
||||
extern void lk_init_cmds ();
|
||||
|
||||
#endif /* __LK_CMDS_H__ */
|
||||
47
gdb/lk-lists.c
Normal file
47
gdb/lk-lists.c
Normal file
@@ -0,0 +1,47 @@
|
||||
/* Iterators for internal data structures of the Linux kernel.
|
||||
|
||||
Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
#include "inferior.h"
|
||||
#include "lk-lists.h"
|
||||
#include "lk-low.h"
|
||||
|
||||
/* Returns next entry from struct list_head CURR while iterating field
|
||||
SNAME->FNAME. */
|
||||
|
||||
CORE_ADDR
|
||||
lk_list_head_next (CORE_ADDR curr, const char *sname, const char *fname)
|
||||
{
|
||||
CORE_ADDR next, next_prev;
|
||||
|
||||
/* We must always assume that the data we handle is corrupted. Thus use
|
||||
curr->next->prev == curr as sanity check. */
|
||||
next = lk_read_addr (curr + LK_OFFSET (list_head, next));
|
||||
next_prev = lk_read_addr (next + LK_OFFSET (list_head, prev));
|
||||
|
||||
if (!curr || curr != next_prev)
|
||||
{
|
||||
error (_("Memory corruption detected while iterating list_head at "\
|
||||
"0x%s belonging to list %s->%s."),
|
||||
phex (curr, lk_builtin_type_size (unsigned_long)) , sname, fname);
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
56
gdb/lk-lists.h
Normal file
56
gdb/lk-lists.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* Iterators for internal data structures of the Linux kernel.
|
||||
|
||||
Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef __LK_LISTS_H__
|
||||
#define __LK_LISTS_H__
|
||||
|
||||
extern CORE_ADDR lk_list_head_next (CORE_ADDR curr, const char *sname,
|
||||
const char *fname);
|
||||
|
||||
/* Iterator over field SNAME->FNAME of type struct list_head starting at
|
||||
address START of type struct list_head. This iterator is intended to be
|
||||
used for lists initiated with macro LIST_HEAD (include/linux/list.h) in
|
||||
the kernel, i.e. lists that START is a global variable of type struct
|
||||
list_head and _not_ of type struct SNAME as the rest of the list. Thus
|
||||
START will not be iterated over but only be used to start/terminate the
|
||||
iteration. */
|
||||
|
||||
#define lk_list_for_each(next, start, sname, fname) \
|
||||
for ((next) = lk_list_head_next ((start), #sname, #fname); \
|
||||
(next) != (start); \
|
||||
(next) = lk_list_head_next ((next), #sname, #fname))
|
||||
|
||||
/* Iterator over struct SNAME linked together via field SNAME->FNAME of type
|
||||
struct list_head starting at address START of type struct SNAME. In
|
||||
contrast to the iterator above, START is a "full" member of the list and
|
||||
thus will be iterated over. */
|
||||
|
||||
#define lk_list_for_each_container(cont, start, sname, fname) \
|
||||
CORE_ADDR _next; \
|
||||
bool _first_loop = true; \
|
||||
for ((cont) = (start), \
|
||||
_next = (start) + LK_OFFSET (sname, fname); \
|
||||
\
|
||||
(cont) != (start) || _first_loop; \
|
||||
\
|
||||
_next = lk_list_head_next (_next, #sname, #fname), \
|
||||
(cont) = LK_CONTAINER_OF (_next, sname, fname), \
|
||||
_first_loop = false)
|
||||
|
||||
#endif /* __LK_LISTS_H__ */
|
||||
964
gdb/lk-low.c
Normal file
964
gdb/lk-low.c
Normal file
@@ -0,0 +1,964 @@
|
||||
/* Basic Linux kernel support, architecture independent.
|
||||
|
||||
Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
#include "block.h"
|
||||
#include "exceptions.h"
|
||||
#include "frame.h"
|
||||
#include "gdbarch.h"
|
||||
#include "gdbcore.h"
|
||||
#include "gdbthread.h"
|
||||
#include "gdbtypes.h"
|
||||
#include "inferior.h"
|
||||
#include "lk-cmds.h"
|
||||
#include "lk-lists.h"
|
||||
#include "lk-low.h"
|
||||
#include "lk-modules.h"
|
||||
#include "objfiles.h"
|
||||
#include "observer.h"
|
||||
#include "solib.h"
|
||||
#include "target.h"
|
||||
#include "value.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
struct target_ops *linux_kernel_ops = NULL;
|
||||
|
||||
/* Initialize a private data entry for an address, where NAME is the name
|
||||
of the symbol, i.e. variable name in Linux, ALIAS the name used to
|
||||
retrieve the entry from hashtab, and SILENT a flag to determine if
|
||||
errors should be ignored.
|
||||
|
||||
Returns a pointer to the new entry. In case of an error, either returns
|
||||
NULL (SILENT = TRUE) or throws an error (SILENT = FALSE). If SILENT = TRUE
|
||||
the caller is responsible to check for errors.
|
||||
|
||||
Do not use directly, use LK_DECLARE_* macros defined in lk-low.h instead. */
|
||||
|
||||
struct lk_private_data *
|
||||
lk_init_addr (const char *name, const char *alias, int silent)
|
||||
{
|
||||
/* Initialize to NULL to silence gcc. */
|
||||
struct value *val = NULL;
|
||||
struct lk_private_data *data;
|
||||
void **new_slot;
|
||||
void *old_slot;
|
||||
|
||||
if ((old_slot = lk_find (alias)) != NULL)
|
||||
return (struct lk_private_data *) old_slot;
|
||||
|
||||
TRY
|
||||
{
|
||||
/* Choose global block for search, in case the variable was redefined
|
||||
in the current context. */
|
||||
const struct block *global = block_global_block (get_selected_block (0));
|
||||
const char *tmp = name;
|
||||
expression_up expr = parse_exp_1 (&tmp, 0, global, 0);
|
||||
|
||||
gdb_assert (*tmp == '\0');
|
||||
val = evaluate_expression (expr.get ());
|
||||
}
|
||||
CATCH (except, RETURN_MASK_ALL)
|
||||
{
|
||||
if (!silent)
|
||||
error (_("Could not find address %s. Aborting."), alias);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
END_CATCH
|
||||
|
||||
data = XCNEW (struct lk_private_data);
|
||||
data->alias = alias;
|
||||
data->data.addr = value_address (val);
|
||||
|
||||
new_slot = lk_find_slot (alias);
|
||||
*new_slot = data;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Same as lk_init_addr but for structs. */
|
||||
|
||||
struct lk_private_data *
|
||||
lk_init_struct (const char *name, const char *alias, int silent)
|
||||
{
|
||||
/* Initialize to NULL to silence GCC. */
|
||||
struct value *val = NULL;
|
||||
struct lk_private_data *data;
|
||||
void **new_slot;
|
||||
void *old_slot;
|
||||
|
||||
if ((old_slot = lk_find (alias)) != NULL)
|
||||
return (struct lk_private_data *) old_slot;
|
||||
|
||||
/* There are two ways to define structs
|
||||
o struct name { ... };
|
||||
o typedef struct { ... } name;
|
||||
Both are used in the linux kernel. Thus we have to check for both ways.
|
||||
We do this by first searching for "struct name" (the "struct " is added
|
||||
by macro LK_STRUCT_NAME in lk-low.h) and if not found seach for "name".
|
||||
|
||||
Note: The alias will always keep its "struct "-prefix, even when
|
||||
given explicitely. Besides some weird error messages this has no effect.
|
||||
*/
|
||||
retry:
|
||||
TRY
|
||||
{
|
||||
/* Choose global block for search, in case the struct was redefined
|
||||
in the current context. */
|
||||
const struct block *global = block_global_block(get_selected_block (0));
|
||||
const char *tmp = name;
|
||||
expression_up expr = parse_exp_1 (&tmp, 0, global, 0);
|
||||
|
||||
gdb_assert (*tmp == '\0');
|
||||
/* parsing just for 'name' can cause name clashes. Thus also check for
|
||||
OP_TYPE. */
|
||||
if (expr->elts[0].opcode != OP_TYPE)
|
||||
error ("We just need to get to the catch block");
|
||||
|
||||
val = evaluate_type (expr.get ());
|
||||
}
|
||||
CATCH (except, RETURN_MASK_ALL)
|
||||
{
|
||||
/* 7 = strlen ("struct "). */
|
||||
if (strncmp (name, "struct ", 7) == 0)
|
||||
{
|
||||
name += 7;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (!silent)
|
||||
error (_("Could not find %s. Aborting."), alias);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
END_CATCH
|
||||
|
||||
data = XCNEW (struct lk_private_data);
|
||||
data->alias = alias;
|
||||
data->data.type = value_type (val);
|
||||
|
||||
new_slot = lk_find_slot (alias);
|
||||
*new_slot = data;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Nearly the same as lk_init_addr, with the difference that two names are
|
||||
needed, i.e. the struct name S_NAME containing the field with name
|
||||
F_NAME. */
|
||||
|
||||
struct lk_private_data *
|
||||
lk_init_field (const char *s_name, const char *f_name, const char *alias,
|
||||
int silent)
|
||||
{
|
||||
struct lk_private_data *data;
|
||||
struct lk_private_data *parent;
|
||||
struct field *first, *last, *field;
|
||||
void **new_slot;
|
||||
void *old_slot;
|
||||
|
||||
if ((old_slot = lk_find (alias)) != NULL)
|
||||
return (struct lk_private_data *) old_slot;
|
||||
|
||||
parent = lk_find (s_name);
|
||||
if (parent == NULL)
|
||||
{
|
||||
parent = lk_init_struct (s_name, s_name, silent);
|
||||
|
||||
/* Only SILENT == true needed, as otherwise lk_init_struct would throw
|
||||
an error. */
|
||||
if (parent == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
first = TYPE_FIELDS (parent->data.type);
|
||||
last = first + TYPE_NFIELDS (parent->data.type);
|
||||
for (field = first; field < last; field ++)
|
||||
{
|
||||
if (streq (field->name, f_name))
|
||||
break;
|
||||
}
|
||||
|
||||
if (field == last)
|
||||
{
|
||||
if (!silent)
|
||||
error (_("Could not find field %s->%s. Aborting."), s_name, f_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = XCNEW (struct lk_private_data);
|
||||
data->alias = alias;
|
||||
data->data.field = field;
|
||||
|
||||
new_slot = lk_find_slot (alias);
|
||||
*new_slot = data;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Map cpu number CPU to the original PTID from target beneath. */
|
||||
|
||||
static ptid_t
|
||||
lk_cpu_to_old_ptid (const int cpu)
|
||||
{
|
||||
struct lk_ptid_map *ptid_map;
|
||||
|
||||
for (ptid_map = LK_PRIVATE->old_ptid; ptid_map;
|
||||
ptid_map = ptid_map->next)
|
||||
{
|
||||
if (ptid_map->cpu == cpu)
|
||||
return ptid_map->old_ptid;
|
||||
}
|
||||
|
||||
error (_("Could not map CPU %d to original PTID. Aborting."), cpu);
|
||||
}
|
||||
|
||||
/* Helper functions to read and return basic types at a given ADDRess. */
|
||||
|
||||
/* Read and return the integer value at address ADDR. */
|
||||
|
||||
int
|
||||
lk_read_int (CORE_ADDR addr)
|
||||
{
|
||||
size_t int_size = lk_builtin_type_size (int);
|
||||
enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
|
||||
return read_memory_integer (addr, int_size, endian);
|
||||
}
|
||||
|
||||
/* Read and return the unsigned integer value at address ADDR. */
|
||||
|
||||
unsigned int
|
||||
lk_read_uint (CORE_ADDR addr)
|
||||
{
|
||||
size_t uint_size = lk_builtin_type_size (unsigned_int);
|
||||
enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
|
||||
return read_memory_integer (addr, uint_size, endian);
|
||||
}
|
||||
|
||||
/* Read and return the long integer value at address ADDR. */
|
||||
|
||||
LONGEST
|
||||
lk_read_long (CORE_ADDR addr)
|
||||
{
|
||||
size_t long_size = lk_builtin_type_size (long);
|
||||
enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
|
||||
return read_memory_integer (addr, long_size, endian);
|
||||
}
|
||||
|
||||
/* Read and return the unsigned long integer value at address ADDR. */
|
||||
|
||||
ULONGEST
|
||||
lk_read_ulong (CORE_ADDR addr)
|
||||
{
|
||||
size_t ulong_size = lk_builtin_type_size (unsigned_long);
|
||||
enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
|
||||
return read_memory_unsigned_integer (addr, ulong_size, endian);
|
||||
}
|
||||
|
||||
/* Read and return the address value at address ADDR. */
|
||||
|
||||
CORE_ADDR
|
||||
lk_read_addr (CORE_ADDR addr)
|
||||
{
|
||||
return (CORE_ADDR) lk_read_ulong (addr);
|
||||
}
|
||||
|
||||
/* Reads a bitmap at a given ADDRess of size SIZE (in bits). Allocates and
|
||||
returns an array of ulongs. The caller is responsible to free the array
|
||||
after it is no longer needed. */
|
||||
|
||||
ULONGEST *
|
||||
lk_read_bitmap (CORE_ADDR addr, size_t size)
|
||||
{
|
||||
ULONGEST *bitmap;
|
||||
size_t ulong_size, len;
|
||||
|
||||
ulong_size = lk_builtin_type_size (unsigned_long);
|
||||
len = LK_DIV_ROUND_UP (size, ulong_size * LK_BITS_PER_BYTE);
|
||||
bitmap = XNEWVEC (ULONGEST, len);
|
||||
|
||||
for (size_t i = 0; i < len; i++)
|
||||
bitmap[i] = lk_read_ulong (addr + i * ulong_size);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/* Return the next set bit in bitmap BITMAP of size SIZE (in bits)
|
||||
starting from bit (index) BIT. Return SIZE when the end of the bitmap
|
||||
was reached. To iterate over all set bits use macro
|
||||
LK_BITMAP_FOR_EACH_SET_BIT defined in lk-low.h. */
|
||||
|
||||
size_t
|
||||
lk_bitmap_find_next_bit (ULONGEST *bitmap, size_t size, size_t bit)
|
||||
{
|
||||
size_t ulong_size, bits_per_ulong, elt;
|
||||
|
||||
ulong_size = lk_builtin_type_size (unsigned_long);
|
||||
bits_per_ulong = ulong_size * LK_BITS_PER_BYTE;
|
||||
elt = bit / bits_per_ulong;
|
||||
|
||||
while (bit < size)
|
||||
{
|
||||
/* FIXME: Explain why using lsb0 bit order. */
|
||||
if (bitmap[elt] & (1UL << (bit % bits_per_ulong)))
|
||||
return bit;
|
||||
|
||||
bit++;
|
||||
if (bit % bits_per_ulong == 0)
|
||||
elt++;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/* Returns the Hamming weight, i.e. number of set bits, of bitmap BITMAP
|
||||
with size SIZE (in bits). */
|
||||
|
||||
size_t
|
||||
lk_bitmap_hweight (ULONGEST *bitmap, size_t size)
|
||||
{
|
||||
size_t ulong_size, bit, bits_per_ulong, elt, retval;
|
||||
|
||||
ulong_size = lk_builtin_type_size (unsigned_long);
|
||||
bits_per_ulong = ulong_size * LK_BITS_PER_BYTE;
|
||||
elt = bit = 0;
|
||||
retval = 0;
|
||||
|
||||
while (bit < size)
|
||||
{
|
||||
if (bitmap[elt] & (1 << bit % bits_per_ulong))
|
||||
retval++;
|
||||
|
||||
bit++;
|
||||
if (bit % bits_per_ulong == 0)
|
||||
elt++;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Provide the per_cpu_offset of cpu CPU. See comment in lk-low.h for
|
||||
details. */
|
||||
|
||||
CORE_ADDR
|
||||
lk_get_percpu_offset (unsigned int cpu)
|
||||
{
|
||||
size_t ulong_size = lk_builtin_type_size (unsigned_long);
|
||||
CORE_ADDR percpu_elt;
|
||||
|
||||
/* Give the architecture a chance to overwrite default behaviour. */
|
||||
if (LK_HOOK->get_percpu_offset)
|
||||
return LK_HOOK->get_percpu_offset (cpu);
|
||||
|
||||
percpu_elt = LK_ADDR (__per_cpu_offset) + (ulong_size * cpu);
|
||||
return lk_read_addr (percpu_elt);
|
||||
}
|
||||
|
||||
|
||||
/* Test if a given task TASK is running. See comment in lk-low.h for
|
||||
details. */
|
||||
|
||||
unsigned int
|
||||
lk_task_running (CORE_ADDR task)
|
||||
{
|
||||
ULONGEST *cpu_online_mask;
|
||||
size_t size;
|
||||
unsigned int cpu;
|
||||
struct cleanup *old_chain;
|
||||
|
||||
size = LK_BITMAP_SIZE (cpumask);
|
||||
cpu_online_mask = lk_read_bitmap (LK_ADDR (cpu_online_mask), size);
|
||||
old_chain = make_cleanup (xfree, cpu_online_mask);
|
||||
|
||||
LK_BITMAP_FOR_EACH_SET_BIT (cpu_online_mask, size, cpu)
|
||||
{
|
||||
CORE_ADDR rq;
|
||||
CORE_ADDR curr;
|
||||
|
||||
rq = LK_ADDR (runqueues) + lk_get_percpu_offset (cpu);
|
||||
curr = lk_read_addr (rq + LK_OFFSET (rq, curr));
|
||||
|
||||
if (curr == task)
|
||||
break;
|
||||
}
|
||||
|
||||
if (cpu == size)
|
||||
cpu = LK_CPU_INVAL;
|
||||
|
||||
do_cleanups (old_chain);
|
||||
return cpu;
|
||||
}
|
||||
|
||||
/* Update running tasks with information from struct rq->curr. */
|
||||
|
||||
static void
|
||||
lk_update_running_tasks ()
|
||||
{
|
||||
ULONGEST *cpu_online_mask;
|
||||
size_t size;
|
||||
unsigned int cpu;
|
||||
struct cleanup *old_chain;
|
||||
|
||||
size = LK_BITMAP_SIZE (cpumask);
|
||||
cpu_online_mask = lk_read_bitmap (LK_ADDR (cpu_online_mask), size);
|
||||
old_chain = make_cleanup (xfree, cpu_online_mask);
|
||||
|
||||
LK_BITMAP_FOR_EACH_SET_BIT (cpu_online_mask, size, cpu)
|
||||
{
|
||||
struct thread_info *tp;
|
||||
CORE_ADDR rq, curr;
|
||||
LONGEST pid, inf_pid;
|
||||
ptid_t new_ptid, old_ptid;
|
||||
|
||||
rq = LK_ADDR (runqueues) + lk_get_percpu_offset (cpu);
|
||||
curr = lk_read_addr (rq + LK_OFFSET (rq, curr));
|
||||
pid = lk_read_int (curr + LK_OFFSET (task_struct, pid));
|
||||
inf_pid = current_inferior ()->pid;
|
||||
|
||||
new_ptid = ptid_build (inf_pid, pid, curr);
|
||||
old_ptid = lk_cpu_to_old_ptid (cpu); /* FIXME not suitable for
|
||||
running targets? */
|
||||
|
||||
tp = find_thread_ptid (old_ptid);
|
||||
if (tp && tp->state != THREAD_EXITED)
|
||||
thread_change_ptid (old_ptid, new_ptid);
|
||||
}
|
||||
do_cleanups (old_chain);
|
||||
}
|
||||
|
||||
/* Update sleeping tasks by walking the task_structs starting from
|
||||
init_task. */
|
||||
|
||||
static void
|
||||
lk_update_sleeping_tasks ()
|
||||
{
|
||||
CORE_ADDR init_task, task, thread;
|
||||
int inf_pid;
|
||||
|
||||
inf_pid = current_inferior ()->pid;
|
||||
init_task = LK_ADDR (init_task);
|
||||
|
||||
lk_list_for_each_container (task, init_task, task_struct, tasks)
|
||||
{
|
||||
lk_list_for_each_container (thread, task, task_struct, thread_group)
|
||||
{
|
||||
int pid;
|
||||
ptid_t ptid;
|
||||
struct thread_info *tp;
|
||||
|
||||
pid = lk_read_int (thread + LK_OFFSET (task_struct, pid));
|
||||
ptid = ptid_build (inf_pid, pid, thread);
|
||||
|
||||
tp = find_thread_ptid (ptid);
|
||||
if (tp == NULL || tp->state == THREAD_EXITED)
|
||||
add_thread (ptid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Function for targets to_update_thread_list hook. */
|
||||
|
||||
static void
|
||||
lk_update_thread_list (struct target_ops *target)
|
||||
{
|
||||
prune_threads ();
|
||||
lk_update_running_tasks ();
|
||||
lk_update_sleeping_tasks ();
|
||||
}
|
||||
|
||||
/* Function for targets to_fetch_registers hook. */
|
||||
|
||||
static void
|
||||
lk_fetch_registers (struct target_ops *target,
|
||||
struct regcache *regcache, int regnum)
|
||||
{
|
||||
CORE_ADDR task;
|
||||
unsigned int cpu;
|
||||
|
||||
task = (CORE_ADDR) ptid_get_tid (inferior_ptid);
|
||||
cpu = lk_task_running (task);
|
||||
|
||||
/* Let the target beneath fetch registers of running tasks. */
|
||||
if (cpu != LK_CPU_INVAL)
|
||||
{
|
||||
struct cleanup *old_inferior_ptid;
|
||||
|
||||
old_inferior_ptid = save_inferior_ptid ();
|
||||
inferior_ptid = lk_cpu_to_old_ptid (cpu);
|
||||
linux_kernel_ops->beneath->to_fetch_registers (target, regcache, regnum);
|
||||
do_cleanups (old_inferior_ptid);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct gdbarch *gdbarch;
|
||||
unsigned int i;
|
||||
|
||||
LK_HOOK->get_registers (task, target, regcache, regnum);
|
||||
|
||||
/* Mark all registers not found as unavailable. */
|
||||
gdbarch = get_regcache_arch (regcache);
|
||||
for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
|
||||
{
|
||||
if (regcache_register_status (regcache, i) == REG_UNKNOWN)
|
||||
regcache_raw_supply (regcache, i, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Function for targets to_pid_to_str hook. Marks running tasks with an
|
||||
asterisk "*". */
|
||||
|
||||
static char *
|
||||
lk_pid_to_str (struct target_ops *target, ptid_t ptid)
|
||||
{
|
||||
static char buf[64];
|
||||
long pid;
|
||||
CORE_ADDR task;
|
||||
|
||||
pid = ptid_get_lwp (ptid);
|
||||
task = (CORE_ADDR) ptid_get_tid (ptid);
|
||||
|
||||
xsnprintf (buf, sizeof (buf), "PID: %5li%s, 0x%s",
|
||||
pid, ((lk_task_running (task) != LK_CPU_INVAL) ? "*" : ""),
|
||||
phex (task, lk_builtin_type_size (unsigned_long)));
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Function for targets to_thread_name hook. */
|
||||
|
||||
static const char *
|
||||
lk_thread_name (struct target_ops *target, struct thread_info *ti)
|
||||
{
|
||||
static char buf[LK_TASK_COMM_LEN + 1];
|
||||
char tmp[LK_TASK_COMM_LEN + 1];
|
||||
CORE_ADDR task, comm;
|
||||
size_t size;
|
||||
|
||||
size = std::min ((unsigned int) LK_TASK_COMM_LEN,
|
||||
LK_ARRAY_LEN(LK_FIELD (task_struct, comm)));
|
||||
|
||||
task = (CORE_ADDR) ptid_get_tid (ti->ptid);
|
||||
comm = task + LK_OFFSET (task_struct, comm);
|
||||
read_memory (comm, (gdb_byte *) tmp, size);
|
||||
|
||||
xsnprintf (buf, sizeof (buf), "%-16s", tmp);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Translate a kernel virtual address ADDR to a physical address. */
|
||||
|
||||
CORE_ADDR
|
||||
lk_kvtop (CORE_ADDR addr)
|
||||
{
|
||||
CORE_ADDR pgd = lk_read_addr (LK_ADDR (init_mm)
|
||||
+ LK_OFFSET (mm_struct, pgd));
|
||||
return LK_HOOK->vtop (pgd, addr);
|
||||
}
|
||||
|
||||
/* Restore current_target to TARGET. */
|
||||
static void
|
||||
restore_current_target (void *target)
|
||||
{
|
||||
current_target.beneath = (struct target_ops *) target;
|
||||
}
|
||||
|
||||
/* Function for targets to_xfer_partial hook. */
|
||||
|
||||
enum target_xfer_status
|
||||
lk_xfer_partial (struct target_ops *ops, enum target_object object,
|
||||
const char *annex, gdb_byte *readbuf,
|
||||
const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
|
||||
ULONGEST *xfered_len)
|
||||
{
|
||||
enum target_xfer_status ret_val;
|
||||
struct cleanup *old_chain = make_cleanup (restore_current_target, ops);
|
||||
|
||||
current_target.beneath = ops->beneath;
|
||||
|
||||
if (LK_HOOK->is_kvaddr (offset))
|
||||
offset = lk_kvtop (offset);
|
||||
|
||||
ret_val = ops->beneath->to_xfer_partial (ops->beneath, object, annex,
|
||||
readbuf, writebuf, offset, len,
|
||||
xfered_len);
|
||||
do_cleanups (old_chain);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/* Functions to initialize and free target_ops and its private data. As well
|
||||
as functions for targets to_open/close/detach hooks. */
|
||||
|
||||
/* Check if OBFFILE is a Linux kernel. */
|
||||
|
||||
static int
|
||||
lk_is_linux_kernel (struct objfile *objfile)
|
||||
{
|
||||
int ok = 0;
|
||||
|
||||
if (objfile == NULL || !(objfile->flags & OBJF_MAINLINE))
|
||||
return 0;
|
||||
|
||||
ok += lookup_minimal_symbol ("linux_banner", NULL, objfile).minsym != NULL;
|
||||
ok += lookup_minimal_symbol ("_stext", NULL, objfile).minsym != NULL;
|
||||
ok += lookup_minimal_symbol ("_etext", NULL, objfile).minsym != NULL;
|
||||
|
||||
return (ok > 2);
|
||||
}
|
||||
|
||||
/* Initialize struct lk_private. */
|
||||
|
||||
static void
|
||||
lk_init_private ()
|
||||
{
|
||||
linux_kernel_ops->to_data = XCNEW (struct lk_private);
|
||||
LK_PRIVATE->hooks = XCNEW (struct lk_private_hooks);
|
||||
LK_PRIVATE->data = htab_create_alloc (31, (htab_hash) lk_hash_private_data,
|
||||
(htab_eq) lk_private_data_eq, NULL,
|
||||
xcalloc, xfree);
|
||||
}
|
||||
|
||||
/* Initialize architecture independent private data. Must be called
|
||||
_after_ symbol tables were initialized. */
|
||||
|
||||
/* FIXME: throw error more fine-grained. */
|
||||
/* FIXME: make independent of compile options. */
|
||||
|
||||
static void
|
||||
lk_init_private_data ()
|
||||
{
|
||||
if (LK_PRIVATE->data != NULL)
|
||||
htab_empty (LK_PRIVATE->data);
|
||||
|
||||
LK_DECLARE_FIELD (task_struct, tasks);
|
||||
LK_DECLARE_FIELD (task_struct, pid);
|
||||
LK_DECLARE_FIELD (task_struct, tgid);
|
||||
LK_DECLARE_FIELD (task_struct, thread_group);
|
||||
LK_DECLARE_FIELD (task_struct, comm);
|
||||
LK_DECLARE_FIELD (task_struct, thread);
|
||||
|
||||
LK_DECLARE_FIELD (list_head, next);
|
||||
LK_DECLARE_FIELD (list_head, prev);
|
||||
|
||||
LK_DECLARE_FIELD (rq, curr);
|
||||
|
||||
LK_DECLARE_FIELD (cpumask, bits);
|
||||
|
||||
LK_DECLARE_FIELD (mm_struct, pgd);
|
||||
|
||||
LK_DECLARE_FIELD (pgd_t, pgd);
|
||||
|
||||
LK_DECLARE_FIELD (module, list);
|
||||
LK_DECLARE_FIELD (module, name);
|
||||
LK_DECLARE_FIELD (module, source_list);
|
||||
LK_DECLARE_FIELD (module, arch);
|
||||
LK_DECLARE_FIELD (module, init);
|
||||
LK_DECLARE_FIELD (module, percpu);
|
||||
LK_DECLARE_FIELD (module, percpu_size);
|
||||
|
||||
/* Module offset moved to new struct module_layout with linux 4.5.
|
||||
It must be checked in code which of this fields exist. */
|
||||
if (LK_DECLARE_FIELD_SILENT (module_layout, base)) /* linux 4.5+ */
|
||||
{
|
||||
LK_DECLARE_FIELD (module, init_layout);
|
||||
LK_DECLARE_FIELD (module, core_layout);
|
||||
|
||||
LK_DECLARE_FIELD (module_layout, size);
|
||||
LK_DECLARE_FIELD (module_layout, text_size);
|
||||
LK_DECLARE_FIELD (module_layout, ro_size);
|
||||
}
|
||||
else if (LK_DECLARE_FIELD_SILENT (module, module_core)) /* linux -4.4 */
|
||||
{
|
||||
LK_DECLARE_FIELD (module, init_size);
|
||||
LK_DECLARE_FIELD (module, core_size);
|
||||
|
||||
LK_DECLARE_FIELD (module, core_text_size);
|
||||
LK_DECLARE_FIELD (module, core_ro_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
error (_("Could not find module base. Aborting."));
|
||||
}
|
||||
|
||||
LK_DECLARE_FIELD (module_use, source_list);
|
||||
LK_DECLARE_FIELD (module_use, source);
|
||||
|
||||
LK_DECLARE_FIELD (uts_namespace, name);
|
||||
|
||||
LK_DECLARE_STRUCT_ALIAS (new_utsname, utsname);
|
||||
LK_DECLARE_STRUCT_ALIAS (old_utsname, utsname);
|
||||
LK_DECLARE_STRUCT_ALIAS (oldold_utsname, utsname);
|
||||
if (LK_STRUCT (utsname) == NULL)
|
||||
error (_("Could not find struct utsname. Aborting."));
|
||||
LK_DECLARE_FIELD (utsname, version);
|
||||
LK_DECLARE_FIELD (utsname, release);
|
||||
|
||||
LK_DECLARE_ADDR (init_task);
|
||||
LK_DECLARE_ADDR (runqueues);
|
||||
LK_DECLARE_ADDR (__per_cpu_offset);
|
||||
LK_DECLARE_ADDR (init_mm);
|
||||
LK_DECLARE_ADDR (modules);
|
||||
LK_DECLARE_ADDR (init_uts_ns);
|
||||
|
||||
LK_DECLARE_ADDR_ALIAS (__cpu_online_mask, cpu_online_mask); /* linux 4.5+ */
|
||||
LK_DECLARE_ADDR_ALIAS (cpu_online_bits, cpu_online_mask); /* linux -4.4 */
|
||||
if (LK_ADDR (cpu_online_mask) == -1)
|
||||
error (_("Could not find address cpu_online_mask. Aborting."));
|
||||
}
|
||||
|
||||
/* Frees the cpu to old ptid map. */
|
||||
|
||||
static void
|
||||
lk_free_ptid_map ()
|
||||
{
|
||||
while (LK_PRIVATE->old_ptid)
|
||||
{
|
||||
struct lk_ptid_map *tmp;
|
||||
|
||||
tmp = LK_PRIVATE->old_ptid;
|
||||
LK_PRIVATE->old_ptid = tmp->next;
|
||||
XDELETE (tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the cpu to old ptid map. Prefer the arch dependent
|
||||
map_running_task_to_cpu hook if provided, else assume that the PID used
|
||||
by target beneath is the same as in task_struct PID task_struct. See
|
||||
comment on lk_ptid_map in lk-low.h for details. */
|
||||
|
||||
static void
|
||||
lk_init_ptid_map ()
|
||||
{
|
||||
struct thread_info *ti;
|
||||
ULONGEST *cpu_online_mask;
|
||||
size_t size;
|
||||
unsigned int cpu;
|
||||
struct cleanup *old_chain;
|
||||
|
||||
if (LK_PRIVATE->old_ptid != NULL)
|
||||
lk_free_ptid_map ();
|
||||
|
||||
size = LK_BITMAP_SIZE (cpumask);
|
||||
cpu_online_mask = lk_read_bitmap (LK_ADDR (cpu_online_mask), size);
|
||||
old_chain = make_cleanup (xfree, cpu_online_mask);
|
||||
|
||||
ALL_THREADS (ti)
|
||||
{
|
||||
struct lk_ptid_map *ptid_map = XCNEW (struct lk_ptid_map);
|
||||
CORE_ADDR rq, curr;
|
||||
int pid;
|
||||
|
||||
/* Give the architecture a chance to overwrite default behaviour. */
|
||||
if (LK_HOOK->map_running_task_to_cpu)
|
||||
{
|
||||
ptid_map->cpu = LK_HOOK->map_running_task_to_cpu (ti);
|
||||
}
|
||||
else
|
||||
{
|
||||
LK_BITMAP_FOR_EACH_SET_BIT (cpu_online_mask, size, cpu)
|
||||
{
|
||||
rq = LK_ADDR (runqueues) + lk_get_percpu_offset (cpu);
|
||||
curr = lk_read_addr (rq + LK_OFFSET (rq, curr));
|
||||
pid = lk_read_int (curr + LK_OFFSET (task_struct, pid));
|
||||
|
||||
if (pid == ptid_get_lwp (ti->ptid))
|
||||
{
|
||||
ptid_map->cpu = cpu;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cpu == size)
|
||||
error (_("Could not map thread with pid %d, lwp %lu to a cpu."),
|
||||
ti->ptid.pid, ti->ptid.lwp);
|
||||
}
|
||||
ptid_map->old_ptid = ti->ptid;
|
||||
ptid_map->next = LK_PRIVATE->old_ptid;
|
||||
LK_PRIVATE->old_ptid = ptid_map;
|
||||
}
|
||||
|
||||
do_cleanups (old_chain);
|
||||
}
|
||||
|
||||
/* Initializes all private data and pushes the linux kernel target, if not
|
||||
already done. */
|
||||
|
||||
static void
|
||||
lk_try_push_target ()
|
||||
{
|
||||
struct gdbarch *gdbarch;
|
||||
|
||||
gdbarch = current_inferior ()->gdbarch;
|
||||
if (!(gdbarch && gdbarch_lk_init_private_p (gdbarch)))
|
||||
error (_("Linux kernel debugging not supported on %s."),
|
||||
gdbarch_bfd_arch_info (gdbarch)->printable_name);
|
||||
|
||||
lk_init_private ();
|
||||
lk_init_private_data ();
|
||||
gdbarch_lk_init_private (gdbarch);
|
||||
/* Check for required arch hooks. */
|
||||
gdb_assert (LK_HOOK->get_registers);
|
||||
gdb_assert (LK_HOOK->is_kvaddr);
|
||||
gdb_assert (LK_HOOK->vtop);
|
||||
gdb_assert (LK_HOOK->get_module_text_offset);
|
||||
|
||||
lk_init_ptid_map ();
|
||||
lk_update_thread_list (linux_kernel_ops);
|
||||
|
||||
if (!target_is_pushed (linux_kernel_ops))
|
||||
push_target (linux_kernel_ops);
|
||||
|
||||
lk_init_cmds ();
|
||||
|
||||
set_solib_ops (gdbarch, lk_modules_so_ops);
|
||||
}
|
||||
|
||||
/* Function for targets to_open hook. */
|
||||
|
||||
static void
|
||||
lk_open (const char *args, int from_tty)
|
||||
{
|
||||
struct objfile *objfile;
|
||||
|
||||
if (target_is_pushed (linux_kernel_ops))
|
||||
{
|
||||
printf_unfiltered (_("Linux kernel target already pushed. Aborting\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
for (objfile = current_program_space->objfiles; objfile;
|
||||
objfile = objfile->next)
|
||||
{
|
||||
if (lk_is_linux_kernel (objfile)
|
||||
&& ptid_get_pid (inferior_ptid) != 0)
|
||||
{
|
||||
lk_try_push_target ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
printf_unfiltered (_("Could not find a valid Linux kernel object file. "
|
||||
"Aborting.\n"));
|
||||
}
|
||||
|
||||
/* Function for targets to_close hook. Deletes all private data. */
|
||||
|
||||
static void
|
||||
lk_close (struct target_ops *ops)
|
||||
{
|
||||
htab_delete (LK_PRIVATE->data);
|
||||
lk_free_ptid_map ();
|
||||
XDELETE (LK_PRIVATE->hooks);
|
||||
|
||||
XDELETE (LK_PRIVATE);
|
||||
linux_kernel_ops->to_data = NULL;
|
||||
}
|
||||
|
||||
/* Function for targets to_detach hook. */
|
||||
|
||||
static void
|
||||
lk_detach (struct target_ops *t, const char *args, int from_tty)
|
||||
{
|
||||
struct target_ops *beneath = linux_kernel_ops->beneath;
|
||||
|
||||
unpush_target (linux_kernel_ops);
|
||||
reinit_frame_cache ();
|
||||
if (from_tty)
|
||||
printf_filtered (_("Linux kernel target detached.\n"));
|
||||
|
||||
beneath->to_detach (beneath, args, from_tty);
|
||||
}
|
||||
|
||||
/* Function for new objfile observer. */
|
||||
|
||||
static void
|
||||
lk_observer_new_objfile (struct objfile *objfile)
|
||||
{
|
||||
if (lk_is_linux_kernel (objfile)
|
||||
&& ptid_get_pid (inferior_ptid) != 0)
|
||||
lk_try_push_target ();
|
||||
}
|
||||
|
||||
/* Function for inferior created observer. */
|
||||
|
||||
static void
|
||||
lk_observer_inferior_created (struct target_ops *ops, int from_tty)
|
||||
{
|
||||
struct objfile *objfile;
|
||||
|
||||
if (ptid_get_pid (inferior_ptid) == 0)
|
||||
return;
|
||||
|
||||
for (objfile = current_inferior ()->pspace->objfiles; objfile;
|
||||
objfile = objfile->next)
|
||||
{
|
||||
if (lk_is_linux_kernel (objfile))
|
||||
{
|
||||
lk_try_push_target ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize linux kernel target. */
|
||||
|
||||
static void
|
||||
init_linux_kernel_ops (void)
|
||||
{
|
||||
struct target_ops *t;
|
||||
|
||||
if (linux_kernel_ops != NULL)
|
||||
return;
|
||||
|
||||
t = XCNEW (struct target_ops);
|
||||
t->to_shortname = "linux-kernel";
|
||||
t->to_longname = "linux kernel support";
|
||||
t->to_doc = "Adds support to debug the Linux kernel";
|
||||
|
||||
/* set t->to_data = struct lk_private in lk_init_private. */
|
||||
|
||||
t->to_open = lk_open;
|
||||
t->to_close = lk_close;
|
||||
t->to_detach = lk_detach;
|
||||
t->to_fetch_registers = lk_fetch_registers;
|
||||
t->to_update_thread_list = lk_update_thread_list;
|
||||
t->to_pid_to_str = lk_pid_to_str;
|
||||
t->to_thread_name = lk_thread_name;
|
||||
t->to_xfer_partial = lk_xfer_partial;
|
||||
|
||||
t->to_stratum = thread_stratum;
|
||||
t->to_magic = OPS_MAGIC;
|
||||
|
||||
linux_kernel_ops = t;
|
||||
|
||||
add_target (t);
|
||||
}
|
||||
|
||||
/* Provide a prototype to silence -Wmissing-prototypes. */
|
||||
extern initialize_file_ftype _initialize_linux_kernel;
|
||||
|
||||
void
|
||||
_initialize_linux_kernel (void)
|
||||
{
|
||||
init_linux_kernel_ops ();
|
||||
|
||||
observer_attach_new_objfile (lk_observer_new_objfile);
|
||||
observer_attach_inferior_created (lk_observer_inferior_created);
|
||||
}
|
||||
333
gdb/lk-low.h
Normal file
333
gdb/lk-low.h
Normal file
@@ -0,0 +1,333 @@
|
||||
/* Basic Linux kernel support, architecture independent.
|
||||
|
||||
Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef __LK_LOW_H__
|
||||
#define __LK_LOW_H__
|
||||
|
||||
#include "target.h"
|
||||
|
||||
extern struct target_ops *linux_kernel_ops;
|
||||
|
||||
/* Copy constants defined in Linux kernel. */
|
||||
#define LK_TASK_COMM_LEN 16
|
||||
#define LK_BITS_PER_BYTE 8
|
||||
#define LK_MODULE_NAME_LEN 56
|
||||
#define LK_UTS_NAME_LEN 64
|
||||
|
||||
/* Definitions used in linux kernel target. */
|
||||
#define LK_CPU_INVAL -1U
|
||||
|
||||
/* Private data structs for this target. */
|
||||
/* Forward declarations. */
|
||||
struct lk_private_hooks;
|
||||
struct lk_ptid_map;
|
||||
|
||||
/* Short hand access to private data. */
|
||||
#define LK_PRIVATE ((struct lk_private *) linux_kernel_ops->to_data)
|
||||
#define LK_HOOK (LK_PRIVATE->hooks)
|
||||
|
||||
struct lk_private
|
||||
{
|
||||
/* Hashtab for needed addresses, structs and fields. */
|
||||
htab_t data;
|
||||
|
||||
/* Linked list to map between cpu number and original ptid from target
|
||||
beneath. */
|
||||
struct lk_ptid_map *old_ptid;
|
||||
|
||||
/* Hooks for architecture dependent functions. */
|
||||
struct lk_private_hooks *hooks;
|
||||
};
|
||||
|
||||
/* We use the following convention for PTIDs:
|
||||
|
||||
ptid->pid = inferiors PID
|
||||
ptid->lwp = PID from task_stuct
|
||||
ptid->tid = address of task_struct
|
||||
|
||||
The task_structs address as TID has two reasons. First, we need it quite
|
||||
often and there is no other reasonable way to pass it down. Second, it
|
||||
helps us to distinguish swapper tasks as they all have PID = 0.
|
||||
|
||||
Furthermore we cannot rely on the target beneath to use the same PID as the
|
||||
task_struct. Thus we need a mapping between our PTID and the PTID of the
|
||||
target beneath. Otherwise it is impossible to pass jobs, e.g. fetching
|
||||
registers of running tasks, to the target beneath. */
|
||||
|
||||
/* Private data struct to map between our and the target beneath PTID. */
|
||||
|
||||
struct lk_ptid_map
|
||||
{
|
||||
struct lk_ptid_map *next;
|
||||
unsigned int cpu;
|
||||
ptid_t old_ptid;
|
||||
};
|
||||
|
||||
/* Private data struct to be stored in hashtab. */
|
||||
|
||||
struct lk_private_data
|
||||
{
|
||||
const char *alias;
|
||||
|
||||
union
|
||||
{
|
||||
CORE_ADDR addr;
|
||||
struct type *type;
|
||||
struct field *field;
|
||||
} data;
|
||||
};
|
||||
|
||||
/* Wrapper for htab_hash_string to work with our private data. */
|
||||
|
||||
static inline hashval_t
|
||||
lk_hash_private_data (const struct lk_private_data *entry)
|
||||
{
|
||||
return htab_hash_string (entry->alias);
|
||||
}
|
||||
|
||||
/* Function for htab_eq to work with our private data. */
|
||||
|
||||
static inline int
|
||||
lk_private_data_eq (const struct lk_private_data *entry,
|
||||
const struct lk_private_data *element)
|
||||
{
|
||||
return streq (entry->alias, element->alias);
|
||||
}
|
||||
|
||||
/* Wrapper for htab_find_slot to work with our private data. Do not use
|
||||
directly, use the macros below instead. */
|
||||
|
||||
static inline void **
|
||||
lk_find_slot (const char *alias)
|
||||
{
|
||||
const struct lk_private_data dummy = { alias };
|
||||
return htab_find_slot (LK_PRIVATE->data, &dummy, INSERT);
|
||||
}
|
||||
|
||||
/* Wrapper for htab_find to work with our private data. Do not use
|
||||
directly, use the macros below instead. */
|
||||
|
||||
static inline struct lk_private_data *
|
||||
lk_find (const char *alias)
|
||||
{
|
||||
const struct lk_private_data dummy = { alias };
|
||||
return (struct lk_private_data *) htab_find (LK_PRIVATE->data, &dummy);
|
||||
}
|
||||
|
||||
/* Functions to initialize private data. Do not use directly, use the
|
||||
macros below instead. */
|
||||
|
||||
extern struct lk_private_data *lk_init_addr (const char *name,
|
||||
const char *alias, int silent);
|
||||
extern struct lk_private_data *lk_init_struct (const char *name,
|
||||
const char *alias, int silent);
|
||||
extern struct lk_private_data *lk_init_field (const char *s_name,
|
||||
const char *f_name,
|
||||
const char *alias, int silent);
|
||||
|
||||
/* The names we use to store our private data in the hashtab. */
|
||||
|
||||
#define LK_STRUCT_NAME(s_name) ("struct " #s_name)
|
||||
#define LK_FIELD_NAME(s_name, f_name) (#s_name " " #f_name)
|
||||
|
||||
/* Macros to initiate addresses and fields, where (S_/F_)NAME is the variables
|
||||
name as used in Linux. LK_DECLARE_FIELD also initializes the corresponding
|
||||
struct entry. Throws an error, if no symbol with the given name is found.
|
||||
*/
|
||||
|
||||
#define LK_DECLARE_ADDR(name) \
|
||||
lk_init_addr (#name, #name, 0)
|
||||
#define LK_DECLARE_FIELD(s_name, f_name) \
|
||||
lk_init_field (LK_STRUCT_NAME (s_name), #f_name,\
|
||||
LK_FIELD_NAME (s_name, f_name), 0)
|
||||
|
||||
/* Same as LK_DECLARE_*, but returns NULL instead of throwing an error if no
|
||||
symbol was found. The caller is responsible to check for possible errors.
|
||||
*/
|
||||
|
||||
#define LK_DECLARE_ADDR_SILENT(name) \
|
||||
lk_init_addr (#name, #name, 1)
|
||||
#define LK_DECLARE_FIELD_SILENT(s_name, f_name) \
|
||||
lk_init_field (LK_STRUCT_NAME (s_name), #f_name,\
|
||||
LK_FIELD_NAME (s_name, f_name), 1)
|
||||
|
||||
/* Same as LK_DECLARE_*_SILENT, but allows you to give an ALIAS name. If used
|
||||
for a struct, the struct has to be declared explicitly _before_ any of its
|
||||
fields. They are ment to be used, when a variable in the kernel was simply
|
||||
renamed (at least from our point of view). The caller is responsible to
|
||||
check for possible errors. */
|
||||
|
||||
#define LK_DECLARE_ADDR_ALIAS(name, alias) \
|
||||
lk_init_addr (#name, #alias, 1)
|
||||
#define LK_DECLARE_STRUCT_ALIAS(s_name, alias) \
|
||||
lk_init_struct (LK_STRUCT_NAME(s_name), LK_STRUCT_NAME (alias), 1)
|
||||
#define LK_DECLARE_FIELD_ALIAS(s_alias, f_name, f_alias) \
|
||||
lk_init_field (LK_STRUCT_NAME (s_alias), #f_name, \
|
||||
LK_FIELD_NAME (s_alias, f_alias), 1)
|
||||
|
||||
/* Macros to retrieve private data from hashtab. Returns NULL (-1) if no entry
|
||||
with the given ALIAS exists. The caller only needs to check for possible
|
||||
errors if not done so at initialization. */
|
||||
|
||||
#define LK_ADDR(alias) \
|
||||
(lk_find (#alias) ? (lk_find (#alias))->data.addr : -1)
|
||||
#define LK_STRUCT(alias) \
|
||||
(lk_find (LK_STRUCT_NAME (alias)) \
|
||||
? (lk_find (LK_STRUCT_NAME (alias)))->data.type \
|
||||
: NULL)
|
||||
#define LK_FIELD(s_alias, f_alias) \
|
||||
(lk_find (LK_FIELD_NAME (s_alias, f_alias)) \
|
||||
? (lk_find (LK_FIELD_NAME (s_alias, f_alias)))->data.field \
|
||||
: NULL)
|
||||
|
||||
|
||||
/* Definitions for architecture dependent hooks. */
|
||||
/* Hook to read registers from the target and supply their content
|
||||
to the regcache. */
|
||||
typedef void (*lk_hook_get_registers) (CORE_ADDR task,
|
||||
struct target_ops *target,
|
||||
struct regcache *regcache,
|
||||
int regnum);
|
||||
|
||||
/* Hook to check if address ADDR is a kernel virtual address.
|
||||
NOTE: This hook is called in the context of target beneath. */
|
||||
typedef int (*lk_hook_is_kvaddr) (CORE_ADDR addr);
|
||||
|
||||
/* Hook to translate virtual adress ADDR to a pysical address using page
|
||||
table located at PGD.
|
||||
NOTE: This hook is called in the context of target beneath. */
|
||||
typedef CORE_ADDR (*lk_hook_vtop) (CORE_ADDR addr, CORE_ADDR pgd);
|
||||
|
||||
/* Hook to get the offset between a modules base and the start of its
|
||||
.text section. */
|
||||
typedef CORE_ADDR (*lk_hook_get_module_text_offset) (CORE_ADDR mod);
|
||||
|
||||
/* Hook to return the per_cpu_offset of cpu CPU. Only architectures that
|
||||
do not use the __per_cpu_offset array to determin the offset have to
|
||||
supply this hook. */
|
||||
typedef CORE_ADDR (*lk_hook_get_percpu_offset) (unsigned int cpu);
|
||||
|
||||
/* Hook to map a running task to a logical CPU. Required if the target
|
||||
beneath uses a different PID as struct rq. */
|
||||
typedef unsigned int (*lk_hook_map_running_task_to_cpu) (struct thread_info *ti);
|
||||
|
||||
struct lk_private_hooks
|
||||
{
|
||||
/* required */
|
||||
lk_hook_get_registers get_registers;
|
||||
|
||||
/* required */
|
||||
lk_hook_is_kvaddr is_kvaddr;
|
||||
|
||||
/* required */
|
||||
lk_hook_vtop vtop;
|
||||
|
||||
/* reqired */
|
||||
lk_hook_get_module_text_offset get_module_text_offset;
|
||||
|
||||
/* optional, required if __per_cpu_offset array is not used to determine
|
||||
offset. */
|
||||
lk_hook_get_percpu_offset get_percpu_offset;
|
||||
|
||||
/* optional, required if the target beneath uses a different PID as struct
|
||||
rq. */
|
||||
lk_hook_map_running_task_to_cpu map_running_task_to_cpu;
|
||||
};
|
||||
|
||||
/* Helper functions to read and return a value at a given ADDRess. */
|
||||
extern int lk_read_int (CORE_ADDR addr);
|
||||
extern unsigned int lk_read_uint (CORE_ADDR addr);
|
||||
extern LONGEST lk_read_long (CORE_ADDR addr);
|
||||
extern ULONGEST lk_read_ulong (CORE_ADDR addr);
|
||||
extern CORE_ADDR lk_read_addr (CORE_ADDR addr);
|
||||
|
||||
/* Reads a bitmap at a given ADDRess of size SIZE (in bits). Allocates and
|
||||
returns an array of ulongs. The caller is responsible to free the array
|
||||
after it is no longer needed. */
|
||||
extern ULONGEST *lk_read_bitmap (CORE_ADDR addr, size_t size);
|
||||
|
||||
/* Walks the bitmap BITMAP of size SIZE from bit (index) BIT.
|
||||
Returns the index of the next set bit or SIZE, when the end of the bitmap
|
||||
was reached. To iterate over all set bits use macro
|
||||
LK_BITMAP_FOR_EACH_SET_BIT defined below. */
|
||||
extern size_t lk_bitmap_find_next_bit (ULONGEST *bitmap, size_t bit,
|
||||
size_t size);
|
||||
#define LK_BITMAP_FOR_EACH_SET_BIT(bitmap, size, bit) \
|
||||
for ((bit) = lk_bitmap_find_next_bit ((bitmap), (size), 0); \
|
||||
(bit) < (size); \
|
||||
(bit) = lk_bitmap_find_next_bit ((bitmap), (size), (bit) + 1))
|
||||
|
||||
/* Returns the size of BITMAP in bits. */
|
||||
#define LK_BITMAP_SIZE(bitmap) \
|
||||
(FIELD_SIZE (LK_FIELD (bitmap, bits)) * LK_BITS_PER_BYTE)
|
||||
|
||||
/* Returns the Hamming weight, i.e. number of set bits, of bitmap BITMAP with
|
||||
size SIZE (in bits). */
|
||||
extern size_t lk_bitmap_hweight (ULONGEST *bitmap, size_t size);
|
||||
|
||||
|
||||
/* Short hand access to current gdbarchs builtin types and their
|
||||
size (in byte). For TYPE replace spaces " " by underscore "_", e.g.
|
||||
"unsigned int" => "unsigned_int". */
|
||||
#define lk_builtin_type(type) \
|
||||
(builtin_type (current_inferior ()->gdbarch)->builtin_##type)
|
||||
#define lk_builtin_type_size(type) \
|
||||
(lk_builtin_type (type)->length)
|
||||
|
||||
/* If field FIELD is an array returns its length (in #elements). */
|
||||
#define LK_ARRAY_LEN(field) \
|
||||
(FIELD_SIZE (field) / FIELD_TARGET_SIZE (field))
|
||||
|
||||
/* Short hand access to the offset of field F_NAME in struct S_NAME. */
|
||||
#define LK_OFFSET(s_name, f_name) \
|
||||
(FIELD_OFFSET (LK_FIELD (s_name, f_name)))
|
||||
|
||||
/* Returns the container of field FNAME of struct SNAME located at address
|
||||
ADDR. */
|
||||
#define LK_CONTAINER_OF(addr, sname, fname) \
|
||||
((addr) - LK_OFFSET (sname, fname))
|
||||
|
||||
/* Divides numinator N by demoniator D and rounds up the result. */
|
||||
#define LK_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
|
||||
|
||||
|
||||
/* Additional access macros to fields in the style of gdbtypes.h */
|
||||
/* Returns the size of field FIELD (in bytes). If FIELD is an array returns
|
||||
the size of the whole array. */
|
||||
#define FIELD_SIZE(field) \
|
||||
TYPE_LENGTH (check_typedef (FIELD_TYPE (*field)))
|
||||
|
||||
/* Returns the size of the target type of field FIELD (in bytes). If FIELD is
|
||||
an array returns the size of its elements. */
|
||||
#define FIELD_TARGET_SIZE(field) \
|
||||
TYPE_LENGTH (check_typedef (TYPE_TARGET_TYPE (FIELD_TYPE (*field))))
|
||||
|
||||
/* Returns the offset of field FIELD (in bytes). */
|
||||
#define FIELD_OFFSET(field) \
|
||||
(FIELD_BITPOS (*field) / TARGET_CHAR_BIT)
|
||||
|
||||
/* Provides the per_cpu_offset of cpu CPU. If the architecture
|
||||
provides a get_percpu_offset hook, the call is passed to it. Otherwise
|
||||
returns the __per_cpu_offset[CPU] element. */
|
||||
extern CORE_ADDR lk_get_percpu_offset (unsigned int cpu);
|
||||
|
||||
/* Tests if a given task TASK is running. Returns either the cpu-id
|
||||
if running or LK_CPU_INVAL if not. */
|
||||
extern unsigned int lk_task_running (CORE_ADDR task);
|
||||
#endif /* __LK_LOW_H__ */
|
||||
412
gdb/lk-modules.c
Normal file
412
gdb/lk-modules.c
Normal file
@@ -0,0 +1,412 @@
|
||||
/* Handle Linux kernel modules as shared libraries.
|
||||
|
||||
Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
#include "common/filestuff.h"
|
||||
#include "filenames.h"
|
||||
#include "gdbcmd.h"
|
||||
#include "gdbcore.h"
|
||||
#include "gdb_regex.h"
|
||||
#include "lk-lists.h"
|
||||
#include "lk-low.h"
|
||||
#include "lk-modules.h"
|
||||
#include "objfiles.h"
|
||||
#include "observer.h"
|
||||
#include "readline/readline.h"
|
||||
#include "solib.h"
|
||||
#include "solist.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
struct target_so_ops *lk_modules_so_ops = NULL;
|
||||
|
||||
/* Info for single section type. */
|
||||
|
||||
struct lm_info_sec
|
||||
{
|
||||
CORE_ADDR start;
|
||||
CORE_ADDR offset;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
/* Link map info to include in an allocated so_list entry. */
|
||||
|
||||
struct lm_info
|
||||
{
|
||||
CORE_ADDR base;
|
||||
unsigned int size;
|
||||
|
||||
struct lm_info_sec text;
|
||||
struct lm_info_sec init_text;
|
||||
struct lm_info_sec ro_data;
|
||||
struct lm_info_sec rw_data;
|
||||
struct lm_info_sec percpu;
|
||||
};
|
||||
|
||||
/* Check if debug info for module NAME are loaded. */
|
||||
|
||||
bool
|
||||
lk_modules_debug_info_loaded (const std::string &name)
|
||||
{
|
||||
struct so_list *so;
|
||||
|
||||
for (so = master_so_list (); so; so = so->next)
|
||||
{
|
||||
if (name == so->so_original_name)
|
||||
return (so->symbols_loaded && objfile_has_symbols (so->objfile));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Replace tags, like '$release', with corresponding data in
|
||||
solib_search_path.
|
||||
|
||||
Known tags:
|
||||
$release Linux kernel release, same as 'uname -r'
|
||||
|
||||
Returns the expanded path. */
|
||||
|
||||
static std::string
|
||||
lk_modules_expand_search_path ()
|
||||
{
|
||||
char release[LK_UTS_NAME_LEN + 1];
|
||||
CORE_ADDR utsname;
|
||||
|
||||
utsname = LK_ADDR (init_uts_ns) + LK_OFFSET (uts_namespace, name);
|
||||
read_memory_string (utsname + LK_OFFSET (utsname, release),
|
||||
release, LK_UTS_NAME_LEN);
|
||||
release[LK_UTS_NAME_LEN] = '\0';
|
||||
|
||||
std::string search_path = get_solib_search_path ();
|
||||
substitute_path_component (search_path, "$release", release);
|
||||
|
||||
return search_path;
|
||||
}
|
||||
|
||||
/* With kernel modules there is the problem that the kernel only stores
|
||||
the modules name but not the path from wich it was loaded from.
|
||||
Thus we need to map the name to a path GDB can read from. We use file
|
||||
modules.order to do so. It is created by kbuild containing the order in
|
||||
which the modules appear in the Makefile and is also used by modprobe.
|
||||
The drawback of this method is that it needs the modules.order file and
|
||||
all relative paths, starting from <solib-search-path>, must be exactly the
|
||||
same as decribed in it. */
|
||||
|
||||
/* Open file <solib-search-path>/modules.order and return its file
|
||||
pointer. */
|
||||
|
||||
FILE *
|
||||
lk_modules_open_mod_order ()
|
||||
{
|
||||
FILE *mod_order;
|
||||
std::string filename = concat_path (lk_modules_expand_search_path (),
|
||||
"modules.order");
|
||||
mod_order = gdb_fopen_cloexec (filename.c_str (), "r");
|
||||
|
||||
if (!mod_order)
|
||||
{
|
||||
error (_("\
|
||||
Can not find file module.order at %s \
|
||||
to load module symbol files.\n\
|
||||
Please check if solib-search-path is set correctly."),
|
||||
filename.c_str ());
|
||||
}
|
||||
|
||||
return mod_order;
|
||||
}
|
||||
|
||||
/* Build map between module name and path to binary file by reading file
|
||||
modules.order. Returns unordered_map with module name as key and its
|
||||
path as value. */
|
||||
|
||||
std::unordered_map<std::string, std::string>
|
||||
lk_modules_build_path_map ()
|
||||
{
|
||||
std::unordered_map<std::string, std::string> umap;
|
||||
FILE *mod_order;
|
||||
struct cleanup *old_chain;
|
||||
char line[SO_NAME_MAX_PATH_SIZE + 1];
|
||||
|
||||
mod_order = lk_modules_open_mod_order ();
|
||||
old_chain = make_cleanup_fclose (mod_order);
|
||||
|
||||
line[SO_NAME_MAX_PATH_SIZE] = '\0';
|
||||
std::string search_path = lk_modules_expand_search_path ();
|
||||
while (fgets (line, SO_NAME_MAX_PATH_SIZE, mod_order))
|
||||
{
|
||||
/* Remove trailing newline. */
|
||||
line[strlen (line) - 1] = '\0';
|
||||
|
||||
std::string name = lbasename (line);
|
||||
|
||||
/* 3 = strlen (".ko"). */
|
||||
if (!endswith (name.c_str (), ".ko")
|
||||
|| name.length () >= LK_MODULE_NAME_LEN + 3)
|
||||
continue;
|
||||
|
||||
name = name.substr (0, name.length () - 3);
|
||||
|
||||
/* Kernel modules are named after the files they are stored in with
|
||||
all minus '-' replaced by underscore '_'. Do the same to enable
|
||||
mapping. */
|
||||
for (size_t p = name.find('-'); p != std::string::npos;
|
||||
p = name.find ('-', p + 1))
|
||||
name[p] = '_';
|
||||
|
||||
umap[name] = concat_path(search_path, line);
|
||||
}
|
||||
|
||||
do_cleanups (old_chain);
|
||||
return umap;
|
||||
}
|
||||
|
||||
/* Allocate and fill a copy of struct lm_info for module at address MOD. */
|
||||
|
||||
struct lm_info *
|
||||
lk_modules_read_lm_info (CORE_ADDR mod)
|
||||
{
|
||||
struct lm_info *lmi = XNEW (struct lm_info);
|
||||
struct cleanup *old_chain = make_cleanup (xfree, lmi);
|
||||
|
||||
if (LK_FIELD (module, module_core)) /* linux -4.4 */
|
||||
{
|
||||
lmi->base = lk_read_addr (mod + LK_OFFSET (module, module_core));
|
||||
lmi->size = lk_read_addr (mod + LK_OFFSET (module, core_size));
|
||||
|
||||
lmi->text.start = lmi->base;
|
||||
lmi->text.offset = LK_HOOK->get_module_text_offset (mod);
|
||||
lmi->text.size = lk_read_uint (mod + LK_OFFSET (module, core_text_size));
|
||||
|
||||
lmi->ro_data.start = lmi->base + lmi->text.size;
|
||||
lmi->ro_data.offset = 0;
|
||||
lmi->ro_data.size = lk_read_uint (mod + LK_OFFSET (module,
|
||||
core_ro_size));
|
||||
}
|
||||
else /* linux 4.5+ */
|
||||
{
|
||||
CORE_ADDR mod_core = mod + LK_OFFSET (module, core_layout);
|
||||
|
||||
lmi->base = lk_read_addr (mod_core
|
||||
+ LK_OFFSET (module_layout, base));
|
||||
lmi->size = lk_read_uint (mod_core
|
||||
+ LK_OFFSET (module_layout, size));
|
||||
|
||||
lmi->text.start = lmi->base;
|
||||
lmi->text.offset = LK_HOOK->get_module_text_offset (mod);
|
||||
lmi->text.size = lk_read_uint (mod_core
|
||||
+ LK_OFFSET (module_layout, text_size));
|
||||
|
||||
lmi->ro_data.start = lmi->base + lmi->text.size;
|
||||
lmi->ro_data.offset = 0;
|
||||
lmi->ro_data.size = lk_read_uint (mod_core
|
||||
+ LK_OFFSET (module_layout, ro_size));
|
||||
}
|
||||
|
||||
lmi->rw_data.start = lmi->base + lmi->ro_data.size;
|
||||
lmi->rw_data.offset = 0;
|
||||
lmi->rw_data.size = lmi->size - lmi->ro_data.size;
|
||||
|
||||
lmi->init_text.start = lk_read_addr (mod + LK_OFFSET (module, init));
|
||||
lmi->init_text.offset = 0;
|
||||
|
||||
lmi->percpu.start = lk_read_addr (mod + LK_OFFSET (module, percpu));
|
||||
lmi->percpu.size = lk_read_uint (mod + LK_OFFSET (module, percpu_size));
|
||||
lmi->percpu.offset = 0;
|
||||
|
||||
discard_cleanups (old_chain);
|
||||
return lmi;
|
||||
}
|
||||
|
||||
/* Function for current_sos hook. */
|
||||
|
||||
struct so_list *
|
||||
lk_modules_current_sos (void)
|
||||
{
|
||||
CORE_ADDR modules, next;
|
||||
FILE *mod_order;
|
||||
struct so_list *list = NULL;
|
||||
std::unordered_map<std::string, std::string> umap;
|
||||
|
||||
umap = lk_modules_build_path_map ();
|
||||
modules = LK_ADDR (modules);
|
||||
lk_list_for_each (next, modules, module, list)
|
||||
{
|
||||
char name[LK_MODULE_NAME_LEN];
|
||||
CORE_ADDR mod, name_addr;
|
||||
|
||||
mod = LK_CONTAINER_OF (next, module, list);
|
||||
name_addr = mod + LK_OFFSET (module, name);
|
||||
read_memory_string (name_addr, name, LK_MODULE_NAME_LEN);
|
||||
|
||||
if (umap.count (name))
|
||||
{
|
||||
struct so_list *newso = XCNEW (struct so_list);
|
||||
|
||||
newso->next = list;
|
||||
list = newso;
|
||||
newso->lm_info = lk_modules_read_lm_info (mod);
|
||||
strncpy (newso->so_original_name, name, SO_NAME_MAX_PATH_SIZE);
|
||||
strncpy (newso->so_name, umap[name].c_str (), SO_NAME_MAX_PATH_SIZE);
|
||||
newso->pspace = current_program_space;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Relocate target_section SEC to section type LMI_SEC. Helper function for
|
||||
lk_modules_relocate_section_addresses. */
|
||||
|
||||
void
|
||||
lk_modules_relocate_sec (struct target_section *sec,
|
||||
struct lm_info_sec *lmi_sec)
|
||||
{
|
||||
unsigned int alignment = 1;
|
||||
|
||||
alignment = 1 << sec->the_bfd_section->alignment_power;
|
||||
|
||||
/* Adjust offset to section alignment. */
|
||||
if (lmi_sec->offset % alignment != 0)
|
||||
lmi_sec->offset += alignment - (lmi_sec->offset % alignment);
|
||||
|
||||
sec->addr += lmi_sec->start + lmi_sec->offset;
|
||||
sec->endaddr += lmi_sec->start + lmi_sec->offset;
|
||||
lmi_sec->offset += sec->endaddr - sec->addr;
|
||||
}
|
||||
|
||||
/* Function for relocate_section_addresses hook. */
|
||||
|
||||
void
|
||||
lk_modules_relocate_section_addresses (struct so_list *so,
|
||||
struct target_section *sec)
|
||||
{
|
||||
struct lm_info *lmi = so->lm_info;
|
||||
unsigned int flags = sec->the_bfd_section->flags;
|
||||
const char *name = sec->the_bfd_section->name;
|
||||
|
||||
if (streq (name, ".modinfo") || streq (name, "__versions"))
|
||||
return;
|
||||
|
||||
/* FIXME: Make dependent on module state, i.e. only map .init sections if
|
||||
* state is MODULE_STATE_COMING. */
|
||||
if (startswith (name, ".init"))
|
||||
lk_modules_relocate_sec (sec, &lmi->init_text);
|
||||
else if (endswith (name, ".percpu"))
|
||||
lk_modules_relocate_sec (sec, &lmi->percpu);
|
||||
else if (flags & SEC_CODE)
|
||||
lk_modules_relocate_sec (sec, &lmi->text);
|
||||
else if (flags & SEC_READONLY)
|
||||
lk_modules_relocate_sec (sec, &lmi->ro_data);
|
||||
else if (flags & SEC_ALLOC)
|
||||
lk_modules_relocate_sec (sec, &lmi->rw_data);
|
||||
|
||||
/* Set address range to be displayed with info shared.
|
||||
size = text + (ro + rw) data without .init sections. */
|
||||
if (so->addr_low == so->addr_high)
|
||||
{
|
||||
so->addr_low = lmi->base;
|
||||
so->addr_high = lmi->base + lmi->size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Function for free_so hook. */
|
||||
|
||||
void
|
||||
lk_modules_free_so (struct so_list *so)
|
||||
{
|
||||
xfree (so->lm_info);
|
||||
}
|
||||
|
||||
/* Function for clear_so hook. */
|
||||
|
||||
void
|
||||
lk_modules_clear_so (struct so_list *so)
|
||||
{
|
||||
if (so->lm_info != NULL)
|
||||
memset (so->lm_info, 0, sizeof (struct lm_info));
|
||||
}
|
||||
|
||||
/* Function for clear_solib hook. */
|
||||
|
||||
void
|
||||
lk_modules_clear_solib ()
|
||||
{
|
||||
/* Nothing to do. */
|
||||
}
|
||||
|
||||
/* Function for clear_create_inferior_hook hook. */
|
||||
|
||||
void
|
||||
lk_modules_create_inferior_hook (int from_tty)
|
||||
{
|
||||
/* Nothing to do. */
|
||||
}
|
||||
|
||||
/* Function for clear_create_inferior_hook hook. */
|
||||
|
||||
int
|
||||
lk_modules_in_dynsym_resolve_code (CORE_ADDR pc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Function for same hook. */
|
||||
|
||||
int
|
||||
lk_modules_same (struct so_list *gdb, struct so_list *inf)
|
||||
{
|
||||
return streq (gdb->so_name, inf->so_name);
|
||||
}
|
||||
|
||||
/* Initialize linux modules solib target. */
|
||||
|
||||
void
|
||||
init_lk_modules_so_ops (void)
|
||||
{
|
||||
struct target_so_ops *t;
|
||||
|
||||
if (lk_modules_so_ops != NULL)
|
||||
return;
|
||||
|
||||
t = XCNEW (struct target_so_ops);
|
||||
t->relocate_section_addresses = lk_modules_relocate_section_addresses;
|
||||
t->free_so = lk_modules_free_so;
|
||||
t->clear_so = lk_modules_clear_so;
|
||||
t->clear_solib = lk_modules_clear_solib;
|
||||
t->solib_create_inferior_hook = lk_modules_create_inferior_hook;
|
||||
t->current_sos = lk_modules_current_sos;
|
||||
t->bfd_open = solib_bfd_open;
|
||||
t->in_dynsym_resolve_code = lk_modules_in_dynsym_resolve_code;
|
||||
t->same = lk_modules_same;
|
||||
|
||||
lk_modules_so_ops = t;
|
||||
}
|
||||
|
||||
/* Provide a prototype to silence -Wmissing-prototypes. */
|
||||
extern initialize_file_ftype _initialize_lk_modules;
|
||||
|
||||
void
|
||||
_initialize_lk_modules (void)
|
||||
{
|
||||
init_lk_modules_so_ops ();
|
||||
}
|
||||
29
gdb/lk-modules.h
Normal file
29
gdb/lk-modules.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/* Handle kernel modules as shared libraries.
|
||||
|
||||
Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef __LK_MODULES_H__
|
||||
#define __LK_MODULES_H__
|
||||
|
||||
extern struct target_so_ops *lk_modules_so_ops;
|
||||
|
||||
/* Check if debug info for module NAME are loaded. Needed by lsmod command. */
|
||||
|
||||
extern bool lk_modules_debug_info_loaded (const std::string &name);
|
||||
|
||||
#endif /* __LK_MODULES_H__ */
|
||||
76
gdb/regformats/s390x-cr-linux64.dat
Normal file
76
gdb/regformats/s390x-cr-linux64.dat
Normal file
@@ -0,0 +1,76 @@
|
||||
# THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi :set ro:
|
||||
# Generated from: s390x-cr-linux64.xml
|
||||
name:s390x_cr_linux64
|
||||
xmltarget:s390x-cr-linux64.xml
|
||||
expedite:r14,r15,pswa
|
||||
64:pswm
|
||||
64:pswa
|
||||
64:r0
|
||||
64:r1
|
||||
64:r2
|
||||
64:r3
|
||||
64:r4
|
||||
64:r5
|
||||
64:r6
|
||||
64:r7
|
||||
64:r8
|
||||
64:r9
|
||||
64:r10
|
||||
64:r11
|
||||
64:r12
|
||||
64:r13
|
||||
64:r14
|
||||
64:r15
|
||||
32:acr0
|
||||
32:acr1
|
||||
32:acr2
|
||||
32:acr3
|
||||
32:acr4
|
||||
32:acr5
|
||||
32:acr6
|
||||
32:acr7
|
||||
32:acr8
|
||||
32:acr9
|
||||
32:acr10
|
||||
32:acr11
|
||||
32:acr12
|
||||
32:acr13
|
||||
32:acr14
|
||||
32:acr15
|
||||
32:fpc
|
||||
64:f0
|
||||
64:f1
|
||||
64:f2
|
||||
64:f3
|
||||
64:f4
|
||||
64:f5
|
||||
64:f6
|
||||
64:f7
|
||||
64:f8
|
||||
64:f9
|
||||
64:f10
|
||||
64:f11
|
||||
64:f12
|
||||
64:f13
|
||||
64:f14
|
||||
64:f15
|
||||
64:cr0
|
||||
64:cr1
|
||||
64:cr2
|
||||
64:cr3
|
||||
64:cr4
|
||||
64:cr5
|
||||
64:cr6
|
||||
64:cr7
|
||||
64:cr8
|
||||
64:cr9
|
||||
64:cr10
|
||||
64:cr11
|
||||
64:cr12
|
||||
64:cr13
|
||||
64:cr14
|
||||
64:cr15
|
||||
64:timer
|
||||
64:todcmp
|
||||
32:todpreg
|
||||
32:prefix
|
||||
108
gdb/regformats/s390x-vxcr-linux64.dat
Normal file
108
gdb/regformats/s390x-vxcr-linux64.dat
Normal file
@@ -0,0 +1,108 @@
|
||||
# THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi :set ro:
|
||||
# Generated from: s390x-vxcr-linux64.xml
|
||||
name:s390x_vxcr_linux64
|
||||
xmltarget:s390x-vxcr-linux64.xml
|
||||
expedite:r14,r15,pswa
|
||||
64:pswm
|
||||
64:pswa
|
||||
64:r0
|
||||
64:r1
|
||||
64:r2
|
||||
64:r3
|
||||
64:r4
|
||||
64:r5
|
||||
64:r6
|
||||
64:r7
|
||||
64:r8
|
||||
64:r9
|
||||
64:r10
|
||||
64:r11
|
||||
64:r12
|
||||
64:r13
|
||||
64:r14
|
||||
64:r15
|
||||
32:acr0
|
||||
32:acr1
|
||||
32:acr2
|
||||
32:acr3
|
||||
32:acr4
|
||||
32:acr5
|
||||
32:acr6
|
||||
32:acr7
|
||||
32:acr8
|
||||
32:acr9
|
||||
32:acr10
|
||||
32:acr11
|
||||
32:acr12
|
||||
32:acr13
|
||||
32:acr14
|
||||
32:acr15
|
||||
32:fpc
|
||||
64:f0
|
||||
64:f1
|
||||
64:f2
|
||||
64:f3
|
||||
64:f4
|
||||
64:f5
|
||||
64:f6
|
||||
64:f7
|
||||
64:f8
|
||||
64:f9
|
||||
64:f10
|
||||
64:f11
|
||||
64:f12
|
||||
64:f13
|
||||
64:f14
|
||||
64:f15
|
||||
64:v0l
|
||||
64:v1l
|
||||
64:v2l
|
||||
64:v3l
|
||||
64:v4l
|
||||
64:v5l
|
||||
64:v6l
|
||||
64:v7l
|
||||
64:v8l
|
||||
64:v9l
|
||||
64:v10l
|
||||
64:v11l
|
||||
64:v12l
|
||||
64:v13l
|
||||
64:v14l
|
||||
64:v15l
|
||||
128:v16
|
||||
128:v17
|
||||
128:v18
|
||||
128:v19
|
||||
128:v20
|
||||
128:v21
|
||||
128:v22
|
||||
128:v23
|
||||
128:v24
|
||||
128:v25
|
||||
128:v26
|
||||
128:v27
|
||||
128:v28
|
||||
128:v29
|
||||
128:v30
|
||||
128:v31
|
||||
64:cr0
|
||||
64:cr1
|
||||
64:cr2
|
||||
64:cr3
|
||||
64:cr4
|
||||
64:cr5
|
||||
64:cr6
|
||||
64:cr7
|
||||
64:cr8
|
||||
64:cr9
|
||||
64:cr10
|
||||
64:cr11
|
||||
64:cr12
|
||||
64:cr13
|
||||
64:cr14
|
||||
64:cr15
|
||||
64:timer
|
||||
64:todcmp
|
||||
32:todpreg
|
||||
32:prefix
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "gdbcmd.h"
|
||||
|
||||
#include "s390-linux-tdep.h"
|
||||
#include "s390-tdep.h"
|
||||
#include "elf/common.h"
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/* Target-dependent code for GDB, the GNU debugger.
|
||||
/* Target-dependent code for GNU/Linux on S390.
|
||||
Copyright (C) 2003-2017 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
@@ -16,181 +16,19 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef S390_TDEP_H
|
||||
#define S390_TDEP_H
|
||||
#ifndef S390_LINUX_TDEP_H
|
||||
#define S390_LINUX_TDEP_H
|
||||
|
||||
/* Hardware capabilities. */
|
||||
extern void s390_gdbarch_linux_init (struct gdbarch_info info,
|
||||
struct gdbarch *gdbarch);
|
||||
|
||||
#ifndef HWCAP_S390_HIGH_GPRS
|
||||
#define HWCAP_S390_HIGH_GPRS 512
|
||||
#endif
|
||||
|
||||
#ifndef HWCAP_S390_TE
|
||||
#define HWCAP_S390_TE 1024
|
||||
#endif
|
||||
|
||||
#ifndef HWCAP_S390_VX
|
||||
#define HWCAP_S390_VX 2048
|
||||
#endif
|
||||
|
||||
/* Register information. */
|
||||
|
||||
/* Program Status Word. */
|
||||
#define S390_PSWM_REGNUM 0
|
||||
#define S390_PSWA_REGNUM 1
|
||||
/* General Purpose Registers. */
|
||||
#define S390_R0_REGNUM 2
|
||||
#define S390_R1_REGNUM 3
|
||||
#define S390_R2_REGNUM 4
|
||||
#define S390_R3_REGNUM 5
|
||||
#define S390_R4_REGNUM 6
|
||||
#define S390_R5_REGNUM 7
|
||||
#define S390_R6_REGNUM 8
|
||||
#define S390_R7_REGNUM 9
|
||||
#define S390_R8_REGNUM 10
|
||||
#define S390_R9_REGNUM 11
|
||||
#define S390_R10_REGNUM 12
|
||||
#define S390_R11_REGNUM 13
|
||||
#define S390_R12_REGNUM 14
|
||||
#define S390_R13_REGNUM 15
|
||||
#define S390_R14_REGNUM 16
|
||||
#define S390_R15_REGNUM 17
|
||||
/* Access Registers. */
|
||||
#define S390_A0_REGNUM 18
|
||||
#define S390_A1_REGNUM 19
|
||||
#define S390_A2_REGNUM 20
|
||||
#define S390_A3_REGNUM 21
|
||||
#define S390_A4_REGNUM 22
|
||||
#define S390_A5_REGNUM 23
|
||||
#define S390_A6_REGNUM 24
|
||||
#define S390_A7_REGNUM 25
|
||||
#define S390_A8_REGNUM 26
|
||||
#define S390_A9_REGNUM 27
|
||||
#define S390_A10_REGNUM 28
|
||||
#define S390_A11_REGNUM 29
|
||||
#define S390_A12_REGNUM 30
|
||||
#define S390_A13_REGNUM 31
|
||||
#define S390_A14_REGNUM 32
|
||||
#define S390_A15_REGNUM 33
|
||||
/* Floating Point Control Word. */
|
||||
#define S390_FPC_REGNUM 34
|
||||
/* Floating Point Registers. */
|
||||
#define S390_F0_REGNUM 35
|
||||
#define S390_F1_REGNUM 36
|
||||
#define S390_F2_REGNUM 37
|
||||
#define S390_F3_REGNUM 38
|
||||
#define S390_F4_REGNUM 39
|
||||
#define S390_F5_REGNUM 40
|
||||
#define S390_F6_REGNUM 41
|
||||
#define S390_F7_REGNUM 42
|
||||
#define S390_F8_REGNUM 43
|
||||
#define S390_F9_REGNUM 44
|
||||
#define S390_F10_REGNUM 45
|
||||
#define S390_F11_REGNUM 46
|
||||
#define S390_F12_REGNUM 47
|
||||
#define S390_F13_REGNUM 48
|
||||
#define S390_F14_REGNUM 49
|
||||
#define S390_F15_REGNUM 50
|
||||
/* General Purpose Register Upper Halves. */
|
||||
#define S390_R0_UPPER_REGNUM 51
|
||||
#define S390_R1_UPPER_REGNUM 52
|
||||
#define S390_R2_UPPER_REGNUM 53
|
||||
#define S390_R3_UPPER_REGNUM 54
|
||||
#define S390_R4_UPPER_REGNUM 55
|
||||
#define S390_R5_UPPER_REGNUM 56
|
||||
#define S390_R6_UPPER_REGNUM 57
|
||||
#define S390_R7_UPPER_REGNUM 58
|
||||
#define S390_R8_UPPER_REGNUM 59
|
||||
#define S390_R9_UPPER_REGNUM 60
|
||||
#define S390_R10_UPPER_REGNUM 61
|
||||
#define S390_R11_UPPER_REGNUM 62
|
||||
#define S390_R12_UPPER_REGNUM 63
|
||||
#define S390_R13_UPPER_REGNUM 64
|
||||
#define S390_R14_UPPER_REGNUM 65
|
||||
#define S390_R15_UPPER_REGNUM 66
|
||||
/* GNU/Linux-specific optional registers. */
|
||||
#define S390_ORIG_R2_REGNUM 67
|
||||
#define S390_LAST_BREAK_REGNUM 68
|
||||
#define S390_SYSTEM_CALL_REGNUM 69
|
||||
/* Transaction diagnostic block. */
|
||||
#define S390_TDB_DWORD0_REGNUM 70
|
||||
#define S390_TDB_ABORT_CODE_REGNUM 71
|
||||
#define S390_TDB_CONFLICT_TOKEN_REGNUM 72
|
||||
#define S390_TDB_ATIA_REGNUM 73
|
||||
#define S390_TDB_R0_REGNUM 74
|
||||
#define S390_TDB_R1_REGNUM 75
|
||||
#define S390_TDB_R2_REGNUM 76
|
||||
#define S390_TDB_R3_REGNUM 77
|
||||
#define S390_TDB_R4_REGNUM 78
|
||||
#define S390_TDB_R5_REGNUM 79
|
||||
#define S390_TDB_R6_REGNUM 80
|
||||
#define S390_TDB_R7_REGNUM 81
|
||||
#define S390_TDB_R8_REGNUM 82
|
||||
#define S390_TDB_R9_REGNUM 83
|
||||
#define S390_TDB_R10_REGNUM 84
|
||||
#define S390_TDB_R11_REGNUM 85
|
||||
#define S390_TDB_R12_REGNUM 86
|
||||
#define S390_TDB_R13_REGNUM 87
|
||||
#define S390_TDB_R14_REGNUM 88
|
||||
#define S390_TDB_R15_REGNUM 89
|
||||
/* Vector registers. */
|
||||
#define S390_V0_LOWER_REGNUM 90
|
||||
#define S390_V1_LOWER_REGNUM 91
|
||||
#define S390_V2_LOWER_REGNUM 92
|
||||
#define S390_V3_LOWER_REGNUM 93
|
||||
#define S390_V4_LOWER_REGNUM 94
|
||||
#define S390_V5_LOWER_REGNUM 95
|
||||
#define S390_V6_LOWER_REGNUM 96
|
||||
#define S390_V7_LOWER_REGNUM 97
|
||||
#define S390_V8_LOWER_REGNUM 98
|
||||
#define S390_V9_LOWER_REGNUM 99
|
||||
#define S390_V10_LOWER_REGNUM 100
|
||||
#define S390_V11_LOWER_REGNUM 101
|
||||
#define S390_V12_LOWER_REGNUM 102
|
||||
#define S390_V13_LOWER_REGNUM 103
|
||||
#define S390_V14_LOWER_REGNUM 104
|
||||
#define S390_V15_LOWER_REGNUM 105
|
||||
#define S390_V16_REGNUM 106
|
||||
#define S390_V17_REGNUM 107
|
||||
#define S390_V18_REGNUM 108
|
||||
#define S390_V19_REGNUM 109
|
||||
#define S390_V20_REGNUM 110
|
||||
#define S390_V21_REGNUM 111
|
||||
#define S390_V22_REGNUM 112
|
||||
#define S390_V23_REGNUM 113
|
||||
#define S390_V24_REGNUM 114
|
||||
#define S390_V25_REGNUM 115
|
||||
#define S390_V26_REGNUM 116
|
||||
#define S390_V27_REGNUM 117
|
||||
#define S390_V28_REGNUM 118
|
||||
#define S390_V29_REGNUM 119
|
||||
#define S390_V30_REGNUM 120
|
||||
#define S390_V31_REGNUM 121
|
||||
/* Total. */
|
||||
#define S390_NUM_REGS 122
|
||||
|
||||
/* Special register usage. */
|
||||
#define S390_SP_REGNUM S390_R15_REGNUM
|
||||
#define S390_RETADDR_REGNUM S390_R14_REGNUM
|
||||
#define S390_FRAME_REGNUM S390_R11_REGNUM
|
||||
|
||||
#define S390_IS_GREGSET_REGNUM(i) \
|
||||
(((i) >= S390_PSWM_REGNUM && (i) <= S390_A15_REGNUM) \
|
||||
|| ((i) >= S390_R0_UPPER_REGNUM && (i) <= S390_R15_UPPER_REGNUM) \
|
||||
|| (i) == S390_ORIG_R2_REGNUM)
|
||||
|
||||
#define S390_IS_FPREGSET_REGNUM(i) \
|
||||
((i) >= S390_FPC_REGNUM && (i) <= S390_F15_REGNUM)
|
||||
|
||||
#define S390_IS_TDBREGSET_REGNUM(i) \
|
||||
((i) >= S390_TDB_DWORD0_REGNUM && (i) <= S390_TDB_R15_REGNUM)
|
||||
|
||||
/* Core file register sets, defined in s390-tdep.c. */
|
||||
/* Core file register sets, defined in s390-linux-tdep.c. */
|
||||
#define s390_sizeof_gregset 0x90
|
||||
#define s390x_sizeof_gregset 0xd8
|
||||
extern const struct regset s390_gregset;
|
||||
#define s390_sizeof_fpregset 0x88
|
||||
extern const struct regset s390_fpregset;
|
||||
extern const struct regset s390_upper_regset;
|
||||
extern const struct regset s390_last_break_regset;
|
||||
extern const struct regset s390x_last_break_regset;
|
||||
extern const struct regset s390_system_call_regset;
|
||||
@@ -216,4 +54,4 @@ extern struct target_desc *tdesc_s390x_te_linux64;
|
||||
extern struct target_desc *tdesc_s390x_vx_linux64;
|
||||
extern struct target_desc *tdesc_s390x_tevx_linux64;
|
||||
|
||||
#endif
|
||||
#endif /* S390_LINUX_TDEP_H */
|
||||
|
||||
390
gdb/s390-lk-tdep.c
Normal file
390
gdb/s390-lk-tdep.c
Normal file
@@ -0,0 +1,390 @@
|
||||
/* Target-dependent code for linux-kernel target on S390.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
#include "gdbarch.h"
|
||||
#include "gdbcore.h"
|
||||
#include "gdbthread.h"
|
||||
#include "lk-low.h"
|
||||
#include "osabi.h"
|
||||
#include "regcache.h"
|
||||
#include "regset.h"
|
||||
#include "s390-tdep.h"
|
||||
#include "s390-lk-tdep.h"
|
||||
|
||||
#include "features/s390x-cr-linux64.c"
|
||||
#include "features/s390x-vxcr-linux64.c"
|
||||
|
||||
/* Register maps and sets. */
|
||||
|
||||
static const struct regcache_map_entry s390x_gregmap_lk[] =
|
||||
{
|
||||
{ 10, S390_R6_REGNUM }, /* r0-r5 volatile */
|
||||
{ -2, REGCACHE_MAP_SKIP, 8 },
|
||||
{ 1, S390_PSWA_REGNUM }, /* Use r14 for PSWA. */
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static const struct regcache_map_entry s390x_regmap_cr [] =
|
||||
{
|
||||
{ 16, S390_CR0_REGNUM, 8 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static const struct regcache_map_entry s390x_regmap_timer [] =
|
||||
{
|
||||
{ 1, S390_TIMER_REGNUM, 8 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static const struct regcache_map_entry s390x_regmap_todcmp [] =
|
||||
{
|
||||
{ 1, S390_TODCMP_REGNUM, 8 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static const struct regcache_map_entry s390x_regmap_todpreg [] =
|
||||
{
|
||||
{ 1, S390_TODPREG_REGNUM, 4 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static const struct regcache_map_entry s390x_regmap_prefix [] =
|
||||
{
|
||||
{ 1, S390_PREFIX_REGNUM, 4 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
const struct regset s390x_gregset_lk = {
|
||||
s390x_gregmap_lk,
|
||||
regcache_supply_regset,
|
||||
regcache_collect_regset
|
||||
};
|
||||
|
||||
const struct regset s390x_cr_regset = {
|
||||
s390x_regmap_cr,
|
||||
regcache_supply_regset,
|
||||
regcache_collect_regset
|
||||
};
|
||||
|
||||
const struct regset s390x_timer_regset = {
|
||||
s390x_regmap_timer,
|
||||
regcache_supply_regset,
|
||||
regcache_collect_regset
|
||||
};
|
||||
|
||||
const struct regset s390x_todcmp_regset = {
|
||||
s390x_regmap_todcmp,
|
||||
regcache_supply_regset,
|
||||
regcache_collect_regset
|
||||
};
|
||||
|
||||
const struct regset s390x_todpreg_regset = {
|
||||
s390x_regmap_todpreg,
|
||||
regcache_supply_regset,
|
||||
regcache_collect_regset
|
||||
};
|
||||
|
||||
const struct regset s390x_prefix_regset = {
|
||||
s390x_regmap_prefix,
|
||||
regcache_supply_regset,
|
||||
regcache_collect_regset
|
||||
};
|
||||
|
||||
/* Function for Linux kernel target get_registers hook. Supplies gprs for
|
||||
task TASK to REGCACHE. Uses r14 (back jump address) as current pswa. */
|
||||
|
||||
void
|
||||
s390_lk_get_registers (CORE_ADDR task, struct target_ops *target,
|
||||
struct regcache *regcache, int regnum)
|
||||
{
|
||||
const struct regset *regset;
|
||||
CORE_ADDR ksp, gprs, pswa;
|
||||
gdb_byte buf[80]; /* 80 = 10 (#registers saved) * 8 (64 bit width) */
|
||||
size_t size;
|
||||
|
||||
regset = &s390x_gregset_lk;
|
||||
|
||||
ksp = lk_read_addr (task + LK_OFFSET (task_struct, thread)
|
||||
+ LK_OFFSET (thread_struct, ksp));
|
||||
gprs = ksp + LK_OFFSET (stack_frame, gprs);
|
||||
size = FIELD_SIZE (LK_FIELD (stack_frame, gprs));
|
||||
gdb_assert (size <= sizeof (buf));
|
||||
|
||||
read_memory (gprs, buf, size);
|
||||
regset->supply_regset (regset, regcache, -1, buf, size);
|
||||
}
|
||||
|
||||
/* Function for Linux kernel target get_percpu_offset hook. Returns the
|
||||
percpu_offset from lowcore for cpu CPU. */
|
||||
|
||||
CORE_ADDR
|
||||
s390_lk_get_percpu_offset (unsigned int cpu)
|
||||
{
|
||||
CORE_ADDR lowcore_ptr, lowcore;
|
||||
size_t ptr_len = lk_builtin_type_size (unsigned_long);
|
||||
|
||||
lowcore_ptr = LK_ADDR (lowcore_ptr) + (ptr_len * cpu);
|
||||
lowcore = lk_read_addr (lowcore_ptr);
|
||||
|
||||
return lk_read_addr (lowcore + LK_OFFSET (lowcore, percpu_offset));
|
||||
}
|
||||
|
||||
/* Function for Linux kernel target map_running_task_to_cpu hook. */
|
||||
|
||||
unsigned int
|
||||
s390_lk_map_running_task_to_cpu (struct thread_info *ti)
|
||||
{
|
||||
struct regcache *regcache;
|
||||
enum register_status reg_status;
|
||||
CORE_ADDR lowcore;
|
||||
unsigned int cpu;
|
||||
|
||||
regcache = get_thread_regcache (ti->ptid);
|
||||
reg_status = regcache_raw_read_unsigned (regcache, S390_PREFIX_REGNUM,
|
||||
(ULONGEST *) &lowcore);
|
||||
if (reg_status != REG_VALID)
|
||||
error (_("Could not find prefix register for thread with pid %d, lwp %li."),
|
||||
ti->ptid.pid, ti->ptid.lwp);
|
||||
|
||||
cpu = lk_read_uint (lowcore + LK_OFFSET (lowcore, cpu_nr));
|
||||
|
||||
return cpu;
|
||||
}
|
||||
|
||||
/* Function for Linux kernel target is_kvaddr hook. */
|
||||
|
||||
int
|
||||
s390_lk_is_kvaddr (CORE_ADDR addr)
|
||||
{
|
||||
return addr >= LK_ADDR (high_memory);
|
||||
}
|
||||
|
||||
/* Read table entry from TABLE at offset OFFSET. Helper for s390_lk_vtop. */
|
||||
|
||||
static inline ULONGEST
|
||||
s390_lk_read_table_entry (CORE_ADDR table, ULONGEST offset)
|
||||
{
|
||||
return lk_read_ulong (table + offset * lk_builtin_type_size (unsigned_long));
|
||||
}
|
||||
|
||||
/* Function for Linux kernel target vtop hook. Assume 64 bit addresses. */
|
||||
|
||||
CORE_ADDR
|
||||
s390_lk_vtop (CORE_ADDR table, CORE_ADDR vaddr)
|
||||
{
|
||||
ULONGEST entry, offset;
|
||||
CORE_ADDR paddr;
|
||||
unsigned int table_type;
|
||||
size_t addr_size = lk_builtin_type_size (unsigned_long);
|
||||
|
||||
/* Read first entry in table to get its type. As the Table-Type bits are
|
||||
the same in every table assume Region1-Table. */
|
||||
entry = s390_lk_read_table_entry (table, 0);
|
||||
table_type = (entry & S390_LK_RFTE_TT) >> 2;
|
||||
|
||||
switch (table_type)
|
||||
{
|
||||
case S390_LK_DAT_TT_REGION1:
|
||||
{
|
||||
offset = (vaddr & S390_LK_VADDR_RFX) >> 53;
|
||||
entry = s390_lk_read_table_entry (table, offset);
|
||||
|
||||
/* Do sanity checks. */
|
||||
if (!entry)
|
||||
warning (_("Trying to translate address 0x%s with empty "\
|
||||
"region-first-table entry."),
|
||||
phex (vaddr, addr_size));
|
||||
else if ((entry & S390_LK_RFTE_TT) >> 2 != S390_LK_DAT_TT_REGION1)
|
||||
warning (_("Trying to translate address 0x%s with corrupt "\
|
||||
"table type in region-first-table entry."),
|
||||
phex (vaddr, addr_size));
|
||||
else if (entry & S390_LK_RFTE_I)
|
||||
warning (_("Translating address 0x%s with invalid bit set at "\
|
||||
"region-first-table entry."),
|
||||
phex (vaddr, addr_size));
|
||||
|
||||
table = entry & S390_LK_RFTE_O;
|
||||
}
|
||||
/* fall through */
|
||||
case S390_LK_DAT_TT_REGION2:
|
||||
{
|
||||
offset = (vaddr & S390_LK_VADDR_RSX) >> 42;
|
||||
entry = s390_lk_read_table_entry (table, offset);
|
||||
|
||||
/* Do sanity checks. */
|
||||
if (!entry)
|
||||
warning (_("Trying to translate address 0x%s with empty "\
|
||||
"region-second-table entry."),
|
||||
phex (vaddr, addr_size));
|
||||
else if ((entry & S390_LK_RSTE_TT) >> 2 != S390_LK_DAT_TT_REGION2)
|
||||
warning (_("Trying to translate address 0x%s with corrupt "\
|
||||
"table type in region-second-table entry."),
|
||||
phex (vaddr, addr_size));
|
||||
else if (entry & S390_LK_RSTE_I)
|
||||
warning (_("Translating address 0x%s with invalid bit set at "\
|
||||
"region-second-table entry."),
|
||||
phex (vaddr, addr_size));
|
||||
|
||||
table = entry & S390_LK_RSTE_O;
|
||||
}
|
||||
/* fall through */
|
||||
case S390_LK_DAT_TT_REGION3:
|
||||
{
|
||||
offset = (vaddr & S390_LK_VADDR_RTX) >> 31;
|
||||
entry = s390_lk_read_table_entry (table, offset);
|
||||
|
||||
/* Do sanity checks. */
|
||||
if (!entry)
|
||||
warning (_("Trying to translate address 0x%s with empty "\
|
||||
"region-third-table entry."),
|
||||
phex (vaddr, addr_size));
|
||||
else if ((entry & S390_LK_RTTE_TT) >> 2 != S390_LK_DAT_TT_REGION3)
|
||||
warning (_("Trying to translate address 0x%s with corrupt "\
|
||||
"table type in region-third-table entry."),
|
||||
phex (vaddr, addr_size));
|
||||
else if (entry & S390_LK_RTTE_I)
|
||||
warning (_("Translating address 0x%s with invalid bit set at "\
|
||||
"region-third-table entry."),
|
||||
phex (vaddr, addr_size));
|
||||
|
||||
/* Check for huge page. */
|
||||
if (entry & S390_LK_RTTE_FC)
|
||||
{
|
||||
paddr = ((entry & S390_LK_RTTE_RFAA)
|
||||
+ (vaddr & ~S390_LK_RTTE_RFAA));
|
||||
return paddr;
|
||||
}
|
||||
|
||||
table = entry & S390_LK_RTTE_O;
|
||||
}
|
||||
/* fall through */
|
||||
case S390_LK_DAT_TT_SEGMENT:
|
||||
{
|
||||
offset = (vaddr & S390_LK_VADDR_SX) >> 20;
|
||||
entry = s390_lk_read_table_entry (table, offset);
|
||||
|
||||
/* Do sanity checks. */
|
||||
if (!entry)
|
||||
warning (_("Trying to translate address 0x%s with empty "\
|
||||
"segment-table entry."),
|
||||
phex (vaddr, addr_size));
|
||||
else if ((entry & S390_LK_STE_TT) >> 2 != S390_LK_DAT_TT_SEGMENT)
|
||||
warning (_("Trying to translate address 0x%s with corrupt "\
|
||||
"table type in segment-table entry."),
|
||||
phex (vaddr, addr_size));
|
||||
else if (entry & S390_LK_STE_I)
|
||||
warning (_("Translating address 0x%s with invalid bit set at "\
|
||||
"segment-table entry."),
|
||||
phex (vaddr, addr_size));
|
||||
|
||||
/* Check for large page. */
|
||||
if (entry & S390_LK_STE_FC)
|
||||
{
|
||||
paddr = ((entry & S390_LK_STE_SFAA)
|
||||
+ (vaddr & ~S390_LK_STE_SFAA));
|
||||
return paddr;
|
||||
}
|
||||
|
||||
table = entry & S390_LK_STE_O;
|
||||
break;
|
||||
}
|
||||
} /* switch (table_type) */
|
||||
|
||||
offset = (vaddr & S390_LK_VADDR_PX) >> 12;
|
||||
entry = s390_lk_read_table_entry (table, offset);
|
||||
|
||||
/* Do sanity checks. */
|
||||
if (!entry)
|
||||
warning (_("Trying to translate address 0x%s with empty page-table "\
|
||||
"entry."),
|
||||
phex (vaddr, addr_size));
|
||||
else if (entry & S390_LK_PTE_I)
|
||||
warning (_("Translating address 0x%s with invalid bit set at page-table "\
|
||||
"entry."),
|
||||
phex (vaddr, addr_size));
|
||||
|
||||
paddr = ((entry & S390_LK_PTE_PFAA) + (vaddr & ~S390_LK_PTE_PFAA));
|
||||
|
||||
return paddr;
|
||||
}
|
||||
|
||||
/* Function for Linux kernel target get_module_text_offset hook. */
|
||||
|
||||
CORE_ADDR
|
||||
s390_lk_get_module_text_offset (CORE_ADDR mod)
|
||||
{
|
||||
CORE_ADDR offset, mod_arch;
|
||||
|
||||
mod_arch = mod + LK_OFFSET (module, arch);
|
||||
offset = lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific, got_size));
|
||||
offset += lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific, plt_size));
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/* Initialize s390 dependent private data for linux kernel target. */
|
||||
|
||||
static void
|
||||
s390_lk_init_private (struct gdbarch *gdbarch)
|
||||
{
|
||||
LK_DECLARE_FIELD (stack_frame, gprs);
|
||||
|
||||
LK_DECLARE_FIELD (thread_struct, ksp);
|
||||
|
||||
LK_DECLARE_STRUCT_ALIAS (_lowcore, lowcore); /* linux -4.4 */
|
||||
LK_DECLARE_STRUCT_ALIAS (lowcore, lowcore); /* linux 4.5+ */
|
||||
if (LK_STRUCT (lowcore) == NULL)
|
||||
error (_("Could not find struct lowcore. Aborting."));
|
||||
LK_DECLARE_FIELD (lowcore, percpu_offset);
|
||||
LK_DECLARE_FIELD (lowcore, current_pid);
|
||||
LK_DECLARE_FIELD (lowcore, cpu_nr);
|
||||
|
||||
LK_DECLARE_FIELD (mod_arch_specific, got_size);
|
||||
LK_DECLARE_FIELD (mod_arch_specific, plt_size);
|
||||
|
||||
LK_DECLARE_ADDR (lowcore_ptr);
|
||||
LK_DECLARE_ADDR (high_memory);
|
||||
|
||||
LK_HOOK->get_registers = s390_lk_get_registers;
|
||||
LK_HOOK->is_kvaddr = s390_lk_is_kvaddr;
|
||||
LK_HOOK->vtop = s390_lk_vtop;
|
||||
LK_HOOK->get_percpu_offset = s390_lk_get_percpu_offset;
|
||||
LK_HOOK->map_running_task_to_cpu = s390_lk_map_running_task_to_cpu;
|
||||
LK_HOOK->get_module_text_offset = s390_lk_get_module_text_offset;
|
||||
}
|
||||
|
||||
/* Initialize Linux kernel specific gdbarch hooks. */
|
||||
|
||||
void
|
||||
s390_gdbarch_lk_init (struct gdbarch_info info, struct gdbarch *gdbarch)
|
||||
{
|
||||
/* Support linux kernel debugging. */
|
||||
set_gdbarch_lk_init_private (gdbarch, s390_lk_init_private);
|
||||
}
|
||||
|
||||
extern initialize_file_ftype _initialize_s390_lk_tdep; /* -Wmissing-prototypes */
|
||||
|
||||
void
|
||||
_initialize_s390_lk_tdep (void)
|
||||
{
|
||||
/* Initialize the Linux kernel target descriptions. */
|
||||
initialize_tdesc_s390x_cr_linux64 ();
|
||||
initialize_tdesc_s390x_vxcr_linux64 ();
|
||||
}
|
||||
36
gdb/s390-lk-tdep.h
Normal file
36
gdb/s390-lk-tdep.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* Target-dependent code for linux-kernel target on S390.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef S390_LK_TDEP_H
|
||||
#define S390_LK_TDEP_H
|
||||
|
||||
extern void s390_gdbarch_lk_init (struct gdbarch_info info,
|
||||
struct gdbarch *gdbarch);
|
||||
|
||||
/* Core file privileged register sets, defined in s390-lk-tdep.c. */
|
||||
extern const struct regset s390x_gregset_lk;
|
||||
extern const struct regset s390x_cr_regset;
|
||||
extern const struct regset s390x_timer_regset;
|
||||
extern const struct regset s390x_todcmp_regset;
|
||||
extern const struct regset s390x_todpreg_regset;
|
||||
extern const struct regset s390x_prefix_regset;
|
||||
|
||||
extern struct target_desc *tdesc_s390x_cr_linux64;
|
||||
extern struct target_desc *tdesc_s390x_vxcr_linux64;
|
||||
|
||||
#endif /* S390_LK_TDEP_H */
|
||||
3404
gdb/s390-tdep.c
Normal file
3404
gdb/s390-tdep.c
Normal file
File diff suppressed because it is too large
Load Diff
381
gdb/s390-tdep.h
Normal file
381
gdb/s390-tdep.h
Normal file
@@ -0,0 +1,381 @@
|
||||
/* Target-dependent code for S390.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef S390_TDEP_H
|
||||
#define S390_TDEP_H
|
||||
|
||||
enum s390_abi_kind
|
||||
{
|
||||
ABI_LINUX_S390,
|
||||
ABI_LINUX_ZSERIES
|
||||
};
|
||||
|
||||
enum s390_vector_abi_kind
|
||||
{
|
||||
S390_VECTOR_ABI_NONE,
|
||||
S390_VECTOR_ABI_128
|
||||
};
|
||||
|
||||
/* The tdep structure. */
|
||||
|
||||
struct gdbarch_tdep
|
||||
{
|
||||
/* ABI version. */
|
||||
enum s390_abi_kind abi;
|
||||
|
||||
/* Vector ABI. */
|
||||
enum s390_vector_abi_kind vector_abi;
|
||||
|
||||
/* Pseudo register numbers. */
|
||||
int gpr_full_regnum;
|
||||
int pc_regnum;
|
||||
int cc_regnum;
|
||||
int v0_full_regnum;
|
||||
|
||||
int have_linux_v1;
|
||||
int have_linux_v2;
|
||||
int have_tdb;
|
||||
};
|
||||
|
||||
/* Named opcode values for the S/390 instructions we recognize. Some
|
||||
instructions have their opcode split across two fields; those are the
|
||||
op1_* and op2_* enums. */
|
||||
|
||||
enum
|
||||
{
|
||||
op1_lhi = 0xa7, op2_lhi = 0x08,
|
||||
op1_lghi = 0xa7, op2_lghi = 0x09,
|
||||
op1_lgfi = 0xc0, op2_lgfi = 0x01,
|
||||
op_lr = 0x18,
|
||||
op_lgr = 0xb904,
|
||||
op_l = 0x58,
|
||||
op1_ly = 0xe3, op2_ly = 0x58,
|
||||
op1_lg = 0xe3, op2_lg = 0x04,
|
||||
op_lm = 0x98,
|
||||
op1_lmy = 0xeb, op2_lmy = 0x98,
|
||||
op1_lmg = 0xeb, op2_lmg = 0x04,
|
||||
op_st = 0x50,
|
||||
op1_sty = 0xe3, op2_sty = 0x50,
|
||||
op1_stg = 0xe3, op2_stg = 0x24,
|
||||
op_std = 0x60,
|
||||
op_stm = 0x90,
|
||||
op1_stmy = 0xeb, op2_stmy = 0x90,
|
||||
op1_stmg = 0xeb, op2_stmg = 0x24,
|
||||
op1_aghi = 0xa7, op2_aghi = 0x0b,
|
||||
op1_ahi = 0xa7, op2_ahi = 0x0a,
|
||||
op1_agfi = 0xc2, op2_agfi = 0x08,
|
||||
op1_afi = 0xc2, op2_afi = 0x09,
|
||||
op1_algfi= 0xc2, op2_algfi= 0x0a,
|
||||
op1_alfi = 0xc2, op2_alfi = 0x0b,
|
||||
op_ar = 0x1a,
|
||||
op_agr = 0xb908,
|
||||
op_a = 0x5a,
|
||||
op1_ay = 0xe3, op2_ay = 0x5a,
|
||||
op1_ag = 0xe3, op2_ag = 0x08,
|
||||
op1_slgfi= 0xc2, op2_slgfi= 0x04,
|
||||
op1_slfi = 0xc2, op2_slfi = 0x05,
|
||||
op_sr = 0x1b,
|
||||
op_sgr = 0xb909,
|
||||
op_s = 0x5b,
|
||||
op1_sy = 0xe3, op2_sy = 0x5b,
|
||||
op1_sg = 0xe3, op2_sg = 0x09,
|
||||
op_nr = 0x14,
|
||||
op_ngr = 0xb980,
|
||||
op_la = 0x41,
|
||||
op1_lay = 0xe3, op2_lay = 0x71,
|
||||
op1_larl = 0xc0, op2_larl = 0x00,
|
||||
op_basr = 0x0d,
|
||||
op_bas = 0x4d,
|
||||
op_bcr = 0x07,
|
||||
op_bc = 0x0d,
|
||||
op_bctr = 0x06,
|
||||
op_bctgr = 0xb946,
|
||||
op_bct = 0x46,
|
||||
op1_bctg = 0xe3, op2_bctg = 0x46,
|
||||
op_bxh = 0x86,
|
||||
op1_bxhg = 0xeb, op2_bxhg = 0x44,
|
||||
op_bxle = 0x87,
|
||||
op1_bxleg= 0xeb, op2_bxleg= 0x45,
|
||||
op1_bras = 0xa7, op2_bras = 0x05,
|
||||
op1_brasl= 0xc0, op2_brasl= 0x05,
|
||||
op1_brc = 0xa7, op2_brc = 0x04,
|
||||
op1_brcl = 0xc0, op2_brcl = 0x04,
|
||||
op1_brct = 0xa7, op2_brct = 0x06,
|
||||
op1_brctg= 0xa7, op2_brctg= 0x07,
|
||||
op_brxh = 0x84,
|
||||
op1_brxhg= 0xec, op2_brxhg= 0x44,
|
||||
op_brxle = 0x85,
|
||||
op1_brxlg= 0xec, op2_brxlg= 0x45,
|
||||
op_svc = 0x0a,
|
||||
};
|
||||
|
||||
/* Hardware capabilities. */
|
||||
|
||||
#ifndef HWCAP_S390_HIGH_GPRS
|
||||
#define HWCAP_S390_HIGH_GPRS 512
|
||||
#endif
|
||||
|
||||
#ifndef HWCAP_S390_TE
|
||||
#define HWCAP_S390_TE 1024
|
||||
#endif
|
||||
|
||||
#ifndef HWCAP_S390_VX
|
||||
#define HWCAP_S390_VX 2048
|
||||
#endif
|
||||
|
||||
/* Register information. */
|
||||
|
||||
/* Program Status Word. */
|
||||
#define S390_PSWM_REGNUM 0
|
||||
#define S390_PSWA_REGNUM 1
|
||||
/* General Purpose Registers. */
|
||||
#define S390_R0_REGNUM 2
|
||||
#define S390_R1_REGNUM 3
|
||||
#define S390_R2_REGNUM 4
|
||||
#define S390_R3_REGNUM 5
|
||||
#define S390_R4_REGNUM 6
|
||||
#define S390_R5_REGNUM 7
|
||||
#define S390_R6_REGNUM 8
|
||||
#define S390_R7_REGNUM 9
|
||||
#define S390_R8_REGNUM 10
|
||||
#define S390_R9_REGNUM 11
|
||||
#define S390_R10_REGNUM 12
|
||||
#define S390_R11_REGNUM 13
|
||||
#define S390_R12_REGNUM 14
|
||||
#define S390_R13_REGNUM 15
|
||||
#define S390_R14_REGNUM 16
|
||||
#define S390_R15_REGNUM 17
|
||||
/* Access Registers. */
|
||||
#define S390_A0_REGNUM 18
|
||||
#define S390_A1_REGNUM 19
|
||||
#define S390_A2_REGNUM 20
|
||||
#define S390_A3_REGNUM 21
|
||||
#define S390_A4_REGNUM 22
|
||||
#define S390_A5_REGNUM 23
|
||||
#define S390_A6_REGNUM 24
|
||||
#define S390_A7_REGNUM 25
|
||||
#define S390_A8_REGNUM 26
|
||||
#define S390_A9_REGNUM 27
|
||||
#define S390_A10_REGNUM 28
|
||||
#define S390_A11_REGNUM 29
|
||||
#define S390_A12_REGNUM 30
|
||||
#define S390_A13_REGNUM 31
|
||||
#define S390_A14_REGNUM 32
|
||||
#define S390_A15_REGNUM 33
|
||||
/* Floating Point Control Word. */
|
||||
#define S390_FPC_REGNUM 34
|
||||
/* Floating Point Registers. */
|
||||
#define S390_F0_REGNUM 35
|
||||
#define S390_F1_REGNUM 36
|
||||
#define S390_F2_REGNUM 37
|
||||
#define S390_F3_REGNUM 38
|
||||
#define S390_F4_REGNUM 39
|
||||
#define S390_F5_REGNUM 40
|
||||
#define S390_F6_REGNUM 41
|
||||
#define S390_F7_REGNUM 42
|
||||
#define S390_F8_REGNUM 43
|
||||
#define S390_F9_REGNUM 44
|
||||
#define S390_F10_REGNUM 45
|
||||
#define S390_F11_REGNUM 46
|
||||
#define S390_F12_REGNUM 47
|
||||
#define S390_F13_REGNUM 48
|
||||
#define S390_F14_REGNUM 49
|
||||
#define S390_F15_REGNUM 50
|
||||
/* General Purpose Register Upper Halves. */
|
||||
#define S390_R0_UPPER_REGNUM 51
|
||||
#define S390_R1_UPPER_REGNUM 52
|
||||
#define S390_R2_UPPER_REGNUM 53
|
||||
#define S390_R3_UPPER_REGNUM 54
|
||||
#define S390_R4_UPPER_REGNUM 55
|
||||
#define S390_R5_UPPER_REGNUM 56
|
||||
#define S390_R6_UPPER_REGNUM 57
|
||||
#define S390_R7_UPPER_REGNUM 58
|
||||
#define S390_R8_UPPER_REGNUM 59
|
||||
#define S390_R9_UPPER_REGNUM 60
|
||||
#define S390_R10_UPPER_REGNUM 61
|
||||
#define S390_R11_UPPER_REGNUM 62
|
||||
#define S390_R12_UPPER_REGNUM 63
|
||||
#define S390_R13_UPPER_REGNUM 64
|
||||
#define S390_R14_UPPER_REGNUM 65
|
||||
#define S390_R15_UPPER_REGNUM 66
|
||||
/* GNU/Linux-specific optional registers. */
|
||||
#define S390_ORIG_R2_REGNUM 67
|
||||
#define S390_LAST_BREAK_REGNUM 68
|
||||
#define S390_SYSTEM_CALL_REGNUM 69
|
||||
/* Transaction diagnostic block. */
|
||||
#define S390_TDB_DWORD0_REGNUM 70
|
||||
#define S390_TDB_ABORT_CODE_REGNUM 71
|
||||
#define S390_TDB_CONFLICT_TOKEN_REGNUM 72
|
||||
#define S390_TDB_ATIA_REGNUM 73
|
||||
#define S390_TDB_R0_REGNUM 74
|
||||
#define S390_TDB_R1_REGNUM 75
|
||||
#define S390_TDB_R2_REGNUM 76
|
||||
#define S390_TDB_R3_REGNUM 77
|
||||
#define S390_TDB_R4_REGNUM 78
|
||||
#define S390_TDB_R5_REGNUM 79
|
||||
#define S390_TDB_R6_REGNUM 80
|
||||
#define S390_TDB_R7_REGNUM 81
|
||||
#define S390_TDB_R8_REGNUM 82
|
||||
#define S390_TDB_R9_REGNUM 83
|
||||
#define S390_TDB_R10_REGNUM 84
|
||||
#define S390_TDB_R11_REGNUM 85
|
||||
#define S390_TDB_R12_REGNUM 86
|
||||
#define S390_TDB_R13_REGNUM 87
|
||||
#define S390_TDB_R14_REGNUM 88
|
||||
#define S390_TDB_R15_REGNUM 89
|
||||
/* Vector registers. */
|
||||
#define S390_V0_LOWER_REGNUM 90
|
||||
#define S390_V1_LOWER_REGNUM 91
|
||||
#define S390_V2_LOWER_REGNUM 92
|
||||
#define S390_V3_LOWER_REGNUM 93
|
||||
#define S390_V4_LOWER_REGNUM 94
|
||||
#define S390_V5_LOWER_REGNUM 95
|
||||
#define S390_V6_LOWER_REGNUM 96
|
||||
#define S390_V7_LOWER_REGNUM 97
|
||||
#define S390_V8_LOWER_REGNUM 98
|
||||
#define S390_V9_LOWER_REGNUM 99
|
||||
#define S390_V10_LOWER_REGNUM 100
|
||||
#define S390_V11_LOWER_REGNUM 101
|
||||
#define S390_V12_LOWER_REGNUM 102
|
||||
#define S390_V13_LOWER_REGNUM 103
|
||||
#define S390_V14_LOWER_REGNUM 104
|
||||
#define S390_V15_LOWER_REGNUM 105
|
||||
#define S390_V16_REGNUM 106
|
||||
#define S390_V17_REGNUM 107
|
||||
#define S390_V18_REGNUM 108
|
||||
#define S390_V19_REGNUM 109
|
||||
#define S390_V20_REGNUM 110
|
||||
#define S390_V21_REGNUM 111
|
||||
#define S390_V22_REGNUM 112
|
||||
#define S390_V23_REGNUM 113
|
||||
#define S390_V24_REGNUM 114
|
||||
#define S390_V25_REGNUM 115
|
||||
#define S390_V26_REGNUM 116
|
||||
#define S390_V27_REGNUM 117
|
||||
#define S390_V28_REGNUM 118
|
||||
#define S390_V29_REGNUM 119
|
||||
#define S390_V30_REGNUM 120
|
||||
#define S390_V31_REGNUM 121
|
||||
/* Control registers. */
|
||||
#define S390_TIMER_REGNUM 122
|
||||
#define S390_TODCMP_REGNUM 123
|
||||
#define S390_TODPREG_REGNUM 124
|
||||
#define S390_CR0_REGNUM 125
|
||||
#define S390_CR1_REGNUM 126
|
||||
#define S390_CR2_REGNUM 127
|
||||
#define S390_CR3_REGNUM 128
|
||||
#define S390_CR4_REGNUM 129
|
||||
#define S390_CR5_REGNUM 130
|
||||
#define S390_CR6_REGNUM 131
|
||||
#define S390_CR7_REGNUM 132
|
||||
#define S390_CR8_REGNUM 133
|
||||
#define S390_CR9_REGNUM 134
|
||||
#define S390_CR10_REGNUM 135
|
||||
#define S390_CR11_REGNUM 136
|
||||
#define S390_CR12_REGNUM 137
|
||||
#define S390_CR13_REGNUM 138
|
||||
#define S390_CR14_REGNUM 139
|
||||
#define S390_CR15_REGNUM 140
|
||||
#define S390_PREFIX_REGNUM 141
|
||||
|
||||
/* Total. */
|
||||
#define S390_NUM_REGS 142
|
||||
#define S390_NUM_GPRS 16
|
||||
#define S390_NUM_FPRS 16
|
||||
|
||||
#define S390_MAX_INSTR_SIZE 6
|
||||
|
||||
/* Special register usage. */
|
||||
#define S390_SP_REGNUM S390_R15_REGNUM
|
||||
#define S390_RETADDR_REGNUM S390_R14_REGNUM
|
||||
#define S390_FRAME_REGNUM S390_R11_REGNUM
|
||||
|
||||
#define S390_IS_GREGSET_REGNUM(i) \
|
||||
(((i) >= S390_PSWM_REGNUM && (i) <= S390_A15_REGNUM) \
|
||||
|| ((i) >= S390_R0_UPPER_REGNUM && (i) <= S390_R15_UPPER_REGNUM) \
|
||||
|| (i) == S390_ORIG_R2_REGNUM)
|
||||
|
||||
#define S390_IS_FPREGSET_REGNUM(i) \
|
||||
((i) >= S390_FPC_REGNUM && (i) <= S390_F15_REGNUM)
|
||||
|
||||
#define S390_IS_TDBREGSET_REGNUM(i) \
|
||||
((i) >= S390_TDB_DWORD0_REGNUM && (i) <= S390_TDB_R15_REGNUM)
|
||||
|
||||
/* Definitions for address translation. */
|
||||
/* DAT Table types. */
|
||||
#define S390_LK_DAT_TT_REGION1 3
|
||||
#define S390_LK_DAT_TT_REGION2 2
|
||||
#define S390_LK_DAT_TT_REGION3 1
|
||||
#define S390_LK_DAT_TT_SEGMENT 0
|
||||
|
||||
/* Region-First-Table */
|
||||
#define S390_LK_RFTE_TL 0x3ULL /* Table-Length */
|
||||
#define S390_LK_RFTE_TT 0xcULL /* Table-Type */
|
||||
#define S390_LK_RFTE_I 0x20ULL /* Region-Invalid Bit */
|
||||
#define S390_LK_RFTE_TF 0xc0ULL /* Table Offset */
|
||||
#define S390_LK_RFTE_P 0x200ULL /* DAT-Protection Bit */
|
||||
#define S390_LK_RFTE_O ~0xfffULL /* Region-Second-Table Origin */
|
||||
|
||||
/* Region-Second-Table flags. */
|
||||
#define S390_LK_RSTE_TL 0x3ULL /* Table-Length */
|
||||
#define S390_LK_RSTE_TT 0xcULL /* Table-Type */
|
||||
#define S390_LK_RSTE_I 0x20ULL /* Region-Invalid Bit */
|
||||
#define S390_LK_RSTE_TF 0xc0ULL /* Table Offset */
|
||||
#define S390_LK_RSTE_P 0x200ULL /* DAT-Protection Bit */
|
||||
#define S390_LK_RSTE_O ~0xfffULL /* Region-Third-Table Origin */
|
||||
|
||||
/* Region-Third-Table flags. */
|
||||
#define S390_LK_RTTE_TL 0x3ULL /* Table-Length */
|
||||
#define S390_LK_RTTE_TT 0xcULL /* Table-Type */
|
||||
#define S390_LK_RTTE_CR 0x10ULL /* Common-Region Bit */
|
||||
#define S390_LK_RTTE_I 0x20ULL /* Region-Invalid Bit */
|
||||
#define S390_LK_RTTE_TF 0xc0ULL /* Table Offset */
|
||||
#define S390_LK_RTTE_P 0x200ULL /* DAT-Protection Bit */
|
||||
#define S390_LK_RTTE_FC 0x400ULL /* Format-Control Bit */
|
||||
#define S390_LK_RTTE_F 0x800ULL /* Fetch-Protection Bit */
|
||||
#define S390_LK_RTTE_ACC 0xf000ULL /* Access-Control Bits */
|
||||
#define S390_LK_RTTE_AV 0x10000ULL /* ACCF-Validity Control */
|
||||
#define S390_LK_RTTE_O ~0xfffULL /* Segment-Table Origin */
|
||||
#define S390_LK_RTTE_RFAA ~0x7fffffffULL /* Region-Frame Absolute Address */
|
||||
|
||||
/* Segment-Table flags. */
|
||||
#define S390_LK_STE_TT 0xcULL /* Table-Type */
|
||||
#define S390_LK_STE_I 0x20ULL /* Segment-Invalid Bit */
|
||||
#define S390_LK_STE_TF 0xc0ULL /* Table Offset */
|
||||
#define S390_LK_STE_P 0x200ULL /* DAT-Protection Bit */
|
||||
#define S390_LK_STE_FC 0x400ULL /* Format-Control Bit */
|
||||
#define S390_LK_STE_F 0x800ULL /* Fetch-Protection Bit */
|
||||
#define S390_LK_STE_ACC 0xf000ULL /* Access-Control Bits */
|
||||
#define S390_LK_STE_AV 0x10000ULL /* ACCF-Validity Control */
|
||||
#define S390_LK_STE_O ~0x7ffULL /* Page-Table Origin */
|
||||
#define S390_LK_STE_SFAA ~0xfffffULL /* Segment-Frame Absolute Address */
|
||||
|
||||
/* Page-Table flags. */
|
||||
#define S390_LK_PTE_P 0x200ULL /* DAT-Protection Bit */
|
||||
#define S390_LK_PTE_I 0x400ULL /* Page-Invalid Bit */
|
||||
#define S390_LK_PTE_PFAA ~0xfffULL /* Page-Frame Absolute Address */
|
||||
|
||||
/* Virtual Address Fields. */
|
||||
#define S390_LK_VADDR_RFX 0xffe0000000000000ULL
|
||||
#define S390_LK_VADDR_RSX 0x001ffc0000000000ULL
|
||||
#define S390_LK_VADDR_RTX 0x000003ff80000000ULL
|
||||
#define S390_LK_VADDR_SX 0x000000007ff00000ULL
|
||||
#define S390_LK_VADDR_PX 0x00000000000ff000ULL
|
||||
#define S390_LK_VADDR_BX 0x0000000000000fffULL
|
||||
|
||||
#endif /* S390_TDEP_H */
|
||||
@@ -107,6 +107,14 @@ show_solib_search_path (struct ui_file *file, int from_tty,
|
||||
value);
|
||||
}
|
||||
|
||||
/* see solib.h. */
|
||||
|
||||
const char *
|
||||
get_solib_search_path ()
|
||||
{
|
||||
return solib_search_path ? solib_search_path : "";
|
||||
}
|
||||
|
||||
/* Same as HAVE_DOS_BASED_FILE_SYSTEM, but useable as an rvalue. */
|
||||
#if (HAVE_DOS_BASED_FILE_SYSTEM)
|
||||
# define DOS_BASED_FILE_SYSTEM 1
|
||||
|
||||
@@ -28,6 +28,11 @@ struct program_space;
|
||||
|
||||
#include "symfile-add-flags.h"
|
||||
|
||||
/* Returns the solib_search_path. The returned string is malloc'ed and must be
|
||||
freed by the caller. */
|
||||
|
||||
extern const char *get_solib_search_path ();
|
||||
|
||||
/* Called when we free all symtabs, to free the shared library information
|
||||
as well. */
|
||||
|
||||
|
||||
@@ -39,12 +39,6 @@
|
||||
|
||||
extern void _initialize_typeprint (void);
|
||||
|
||||
static void ptype_command (char *, int);
|
||||
|
||||
static void whatis_command (char *, int);
|
||||
|
||||
static void whatis_exp (char *, int);
|
||||
|
||||
const struct type_print_options type_print_raw_options =
|
||||
{
|
||||
1, /* raw */
|
||||
@@ -389,7 +383,7 @@ type_to_string (struct type *type)
|
||||
/* Print type of EXP, or last thing in value history if EXP == NULL.
|
||||
show is passed to type_print. */
|
||||
|
||||
static void
|
||||
void
|
||||
whatis_exp (char *exp, int show)
|
||||
{
|
||||
struct value *val;
|
||||
|
||||
@@ -78,4 +78,6 @@ extern void val_print_not_allocated (struct ui_file *stream);
|
||||
|
||||
extern void val_print_not_associated (struct ui_file *stream);
|
||||
|
||||
extern void whatis_exp (char *exp, int show);
|
||||
|
||||
#endif
|
||||
|
||||
94
gdb/utils.c
94
gdb/utils.c
@@ -66,6 +66,8 @@
|
||||
#include "interps.h"
|
||||
#include "gdb_regex.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#if !HAVE_DECL_MALLOC
|
||||
extern PTR malloc (); /* ARI: PTR */
|
||||
#endif
|
||||
@@ -3156,49 +3158,71 @@ make_cleanup_free_char_ptr_vec (VEC (char_ptr) *char_ptr_vec)
|
||||
return make_cleanup (do_free_char_ptr_vec, char_ptr_vec);
|
||||
}
|
||||
|
||||
/* Substitute all occurences of string FROM by string TO in *STRINGP. *STRINGP
|
||||
must come from xrealloc-compatible allocator and it may be updated. FROM
|
||||
needs to be delimited by IS_DIR_SEPARATOR or DIRNAME_SEPARATOR (or be
|
||||
located at the start or end of *STRINGP. */
|
||||
/* See utils.h. */
|
||||
|
||||
void
|
||||
substitute_path_component (char **stringp, const char *from, const char *to)
|
||||
substitute_path_component (std::string &str, const std::string &from,
|
||||
const std::string &to)
|
||||
{
|
||||
char *string = *stringp, *s;
|
||||
const size_t from_len = strlen (from);
|
||||
const size_t to_len = strlen (to);
|
||||
|
||||
for (s = string;;)
|
||||
for (size_t pos = str.find (from); pos != std::string::npos;
|
||||
pos = str.find (from, pos + 1))
|
||||
{
|
||||
s = strstr (s, from);
|
||||
if (s == NULL)
|
||||
break;
|
||||
|
||||
if ((s == string || IS_DIR_SEPARATOR (s[-1])
|
||||
|| s[-1] == DIRNAME_SEPARATOR)
|
||||
&& (s[from_len] == '\0' || IS_DIR_SEPARATOR (s[from_len])
|
||||
|| s[from_len] == DIRNAME_SEPARATOR))
|
||||
char start, end;
|
||||
start = str[pos - 1];
|
||||
end = str[pos + from.length ()];
|
||||
if ((pos == 0 || IS_DIR_SEPARATOR (start) || start == DIRNAME_SEPARATOR)
|
||||
&& (end == '\0' || IS_DIR_SEPARATOR (end)
|
||||
|| end == DIRNAME_SEPARATOR))
|
||||
{
|
||||
char *string_new;
|
||||
|
||||
string_new
|
||||
= (char *) xrealloc (string, (strlen (string) + to_len + 1));
|
||||
|
||||
/* Relocate the current S pointer. */
|
||||
s = s - string + string_new;
|
||||
string = string_new;
|
||||
|
||||
/* Replace from by to. */
|
||||
memmove (&s[to_len], &s[from_len], strlen (&s[from_len]) + 1);
|
||||
memcpy (s, to, to_len);
|
||||
|
||||
s += to_len;
|
||||
str.replace (pos, from.length (), to);
|
||||
}
|
||||
else
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Approximate length of final path. Helper for concat_path. */
|
||||
|
||||
static inline unsigned long
|
||||
approx_path_length (std::initializer_list<std::string> args,
|
||||
std::string dir_separator)
|
||||
{
|
||||
size_t length = 0;
|
||||
|
||||
for (const std::string &arg: args)
|
||||
length += arg.length () + dir_separator.length ();
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/* See utils.h. */
|
||||
|
||||
std::string
|
||||
_concat_path (std::initializer_list<std::string> args,
|
||||
std::string dir_separator)
|
||||
{
|
||||
std::string dst;
|
||||
dst.reserve (approx_path_length (args, dir_separator));
|
||||
|
||||
for (const std::string &arg : args)
|
||||
{
|
||||
if (arg.empty ())
|
||||
continue;
|
||||
|
||||
if (startswith (arg.c_str (), dir_separator.c_str ())
|
||||
&& endswith (dst.c_str (), dir_separator.c_str ()))
|
||||
dst.erase (dst.length () - dir_separator.length (),
|
||||
dir_separator.length ());
|
||||
|
||||
else if (!dst.empty ()
|
||||
&& !startswith (arg.c_str (), dir_separator.c_str ())
|
||||
&& !endswith (dst.c_str (), dir_separator.c_str ())
|
||||
&& dst != TARGET_SYSROOT_PREFIX)
|
||||
dst += dir_separator;
|
||||
|
||||
dst += arg;
|
||||
}
|
||||
|
||||
*stringp = string;
|
||||
dst.shrink_to_fit ();
|
||||
return dst;
|
||||
}
|
||||
|
||||
#ifdef HAVE_WAITPID
|
||||
|
||||
26
gdb/utils.h
26
gdb/utils.h
@@ -24,6 +24,7 @@
|
||||
#include "exceptions.h"
|
||||
#include "common/scoped_restore.h"
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
extern void initialize_utils (void);
|
||||
|
||||
@@ -132,8 +133,29 @@ extern char *gdb_abspath (const char *);
|
||||
extern int gdb_filename_fnmatch (const char *pattern, const char *string,
|
||||
int flags);
|
||||
|
||||
extern void substitute_path_component (char **stringp, const char *from,
|
||||
const char *to);
|
||||
/* Substitute all occurences of string FROM by string TO in STR. FROM
|
||||
needs to be delimited by IS_DIR_SEPARATOR or DIRNAME_SEPARATOR (or be
|
||||
located at the start or end of STR). */
|
||||
|
||||
extern void substitute_path_component (std::string &str,
|
||||
const std::string &from,
|
||||
const std::string &to);
|
||||
|
||||
/* Concatenate an arbitrary number of path elements. Adds and removes
|
||||
directory separators as needed.
|
||||
|
||||
concat_path (/first, second) => /first/second
|
||||
concat_path (first, second) => first/second
|
||||
concat_path (first/, second) => first/second
|
||||
concat_path (first, /second) => first/second
|
||||
concat_path (first/, /second) => first/second
|
||||
concat_path (target:, second) => target:second
|
||||
*/
|
||||
|
||||
extern std::string _concat_path (std::initializer_list<std::string> args,
|
||||
std::string dir_separator);
|
||||
|
||||
#define concat_path(...) _concat_path ({__VA_ARGS__}, SLASH_STRING)
|
||||
|
||||
char *ldirname (const char *filename);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user