[PATCH v2 10/47] gdb, gdbserver, ze: in-memory libraries

Tankut Baris Aktemur
2024-12-13T16:18:54Z

From: Markus Metzger <markus.t.metzger@intel.com>

For Intel GPU devices, device libraries live in the host memory and are
loaded onto the device from there.

Add support for reporting such in-memory shared libraries via

qXfer:libraries:read

and have GDB read them from target memory.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/NEWS | 6 +++
gdb/doc/gdb.texinfo | 40 +++++++++++-----
gdb/features/library-list.dtd | 8 +++-
gdb/remote.c | 3 ++
gdb/solib-target.c | 85 +++++++++++++++++++++++++++++++--
gdb/solib.c | 27 +++++++++--
gdb/solist.h | 13 +++++-
gdbserver/dll.cc | 59 +++++++++++++++++++++++
gdbserver/dll.h | 19 +++++++-
gdbserver/server.cc | 106 +++++++++++++++++++++++++++++++++++++++---
gdbserver/server.h | 3 ++
11 files changed, 340 insertions(+), 29 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 8f8aa1462f5f757bda3d25ca60e2eba80660bcb6..b60f66403116e73b4c5d2b2b4d847578da6e701a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -174,6 +174,12 @@ vFile:stat
vFile:fstat but takes a filename rather than an open file
descriptor.

+qXfer:libraries:read's response
+
+ The qXfer:libraries:read query supports reporting in-memory libraries. GDB
+ indicates support by supplying qXfer:libraries:read:in-memory-library+ in the
+ qSupported packet.
+
* Changed remote packets

qXfer:features:read:target.xml
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index ddd414659fcc554813fc2a12a79e581ad7a188b9..c84a8372c223e724e3042322a8bee07b12423050 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -48026,9 +48026,10 @@ queries the target's operating system and reports which libraries
are loaded.

The @samp{qXfer:libraries:read} packet returns an XML document which
-lists loaded libraries and their offsets. Each library has an
-associated name and one or more segment or section base addresses,
-which report where the library was loaded in memory.
+lists loaded libraries and their offsets. Each library has either an
+associated name or begin and end addresses and one or more segment or
+section base addresses, which report where the library was loaded in
+memory.

For the common case of libraries that are fully linked binaries, the
library should have a list of segments. If the target supports
@@ -48040,6 +48041,10 @@ depend on the library's link-time base addresses.
@value{GDBN} must be linked with the Expat library to support XML
library lists. @xref{Expat}.

+@value{GDBN} indicates support for in-memory library elements by
+supplying the @code{qXfer:libraries:read:in-memory-library+}
+@samp{qSupported} feature (@pxref{qSupported}).
+
A simple memory map, with one loaded library relocated by a single
offset, looks like this:

@@ -48051,6 +48056,16 @@ offset, looks like this:
</library-list>
@end smallexample

+A corresponding memory map for an in-memory library looks like this:
+
+@smallexample
+<library-list>
+ <in-memory-library begin="0xa000000" end="0xa001000">
+ <segment address="0x10000000"/>
+ </library>
+</library-list>
+@end smallexample
+
Another simple memory map, with one loaded library with three
allocated sections (.text, .data, .bss), looks like this:

@@ -48068,14 +48083,17 @@ The format of a library list is described by this DTD:

@smallexample

-<!ELEMENT library-list (library)*>
-<!ATTLIST library-list version CDATA #FIXED "1.0">
-<!ELEMENT library (segment*, section*)>
-<!ATTLIST library name CDATA #REQUIRED>
-<!ELEMENT segment EMPTY>
-<!ATTLIST segment address CDATA #REQUIRED>
-<!ELEMENT section EMPTY>
-<!ATTLIST section address CDATA #REQUIRED>
+<!ELEMENT library-list (library | in-memory-library)*>
+<!ATTLIST library-list version CDATA #FIXED "1.1">
+<!ELEMENT library (segment*, section*)>
+<!ATTLIST library name CDATA #REQUIRED>
+<!ELEMENT in-memory-library (segment*, section*)>
+<!ATTLIST in-memory-library begin CDATA #REQUIRED
+ end CDATA #REQUIRED>
+<!ELEMENT segment EMPTY>
+<!ATTLIST segment address CDATA #REQUIRED>
+<!ELEMENT section EMPTY>
+<!ATTLIST section address CDATA #REQUIRED>
@end smallexample

In addition, segments and section descriptors cannot be mixed within a
diff --git a/gdb/features/library-list.dtd b/gdb/features/library-list.dtd
index 98ed7bc28dcc4562ef4259fdd78138fe69d69d29..f55071c8e906f091752e8ca78ec29bcd76028433 100644
--- a/gdb/features/library-list.dtd
+++ b/gdb/features/library-list.dtd
@@ -5,12 +5,16 @@
notice and this notice are preserved. -->


-<!ELEMENT library-list (library)*>
-<!ATTLIST library-list version CDATA #FIXED "1.0">
+<!ELEMENT library-list (library | in-memory-library)*>
+<!ATTLIST library-list version CDATA #FIXED "1.1">

<!ELEMENT library (segment*, section*)>
<!ATTLIST library name CDATA #REQUIRED>

+<!ELEMENT in-memory-library (segment*, section*)>
+<!ATTLIST in-memory-library begin CDATA #REQUIRED
+ end CDATA #REQUIRED>
+
<!ELEMENT segment EMPTY>
<!ATTLIST segment address CDATA #REQUIRED>

diff --git a/gdb/remote.c b/gdb/remote.c
index f41129915683194237d1bba56d17df61ae89c063..7d074a5df322d68ded8f96c4832bc8c247435a4f 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5956,6 +5956,9 @@ remote_target::remote_query_supported ()
!= AUTO_BOOLEAN_FALSE)
remote_query_supported_append (&q, "memory-tagging+");

+ remote_query_supported_append
+ (&q, "qXfer:libraries:read:in-memory-library+");
+
/* Keep this one last to work around a gdbserver <= 7.10 bug in
the qSupported:xmlRegisters=i386 handling. */
if (remote_support_xml != NULL
diff --git a/gdb/solib-target.c b/gdb/solib-target.c
index 8f73d5df55bddcc32708f7f96f35ae42e0730053..7165aed7e22f87c96a3716397e9380aff03a2a61 100644
--- a/gdb/solib-target.c
+++ b/gdb/solib-target.c
@@ -23,16 +23,36 @@
#include "symfile.h"
#include "target.h"
#include "solib-target.h"
+#include "gdbsupport/filestuff.h"
+#include "gdb_bfd.h"
#include <vector>
#include "inferior.h"

+/* The location of a loaded library. */
+
+enum lm_location_t
+{
+ lm_on_disk,
+ lm_in_memory
+};
+
/* Private data for each loaded library. */
struct lm_info_target final : public lm_info
{
+ /* The library's location. */
+ lm_location_t location;
+
/* The library's name. The name is normally kept in the struct
- so_list; it is only here during XML parsing. */
+ so_list; it is only here during XML parsing.
+
+ This is only valid if location == lm_on_disk. */
std::string name;

+ /* The library's begin and end memory addresses.
+
+ This is only valid if location == lm_in_memory. */
+ CORE_ADDR begin = 0ull, end = 0ull;
+
/* The target can either specify segment bases or section bases, not
both. */

@@ -122,12 +142,32 @@ library_list_start_library (struct gdb_xml_parser *parser,
{
auto *list = (std::vector<lm_info_target_up> *) user_data;
lm_info_target *item = new lm_info_target;
+ item->location = lm_on_disk;
item->name
= (const char *) xml_find_attribute (attributes, "name")->value.get ();

list->emplace_back (item);
}

+/* Handle the start of a <in-memory-library> element. */
+
+static void
+in_memory_library_list_start_library (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data,
+ std::vector<gdb_xml_value> &attributes)
+{
+ auto *list = (std::vector<lm_info_target_up> *) user_data;
+ lm_info_target *item = new lm_info_target;
+ item->location = lm_in_memory;
+ item->begin = (CORE_ADDR) *(ULONGEST *)
+ xml_find_attribute (attributes, "begin")->value.get ();
+ item->end = (CORE_ADDR) *(ULONGEST *)
+ xml_find_attribute (attributes, "end")->value.get ();
+
+ list->emplace_back (item);
+}
+
static void
library_list_end_library (struct gdb_xml_parser *parser,
const struct gdb_xml_element *element,
@@ -156,7 +196,7 @@ library_list_start_list (struct gdb_xml_parser *parser,
{
const char *string = (const char *) version->value.get ();

- if (strcmp (string, "1.0") != 0)
+ if ((strcmp (string, "1.0") != 0) && (strcmp (string, "1.1") != 0))
gdb_xml_error (parser,
_("Library list has unsupported version \"%s\""),
string);
@@ -191,10 +231,19 @@ static const struct gdb_xml_attribute library_attributes[] = {
{ NULL, GDB_XML_AF_NONE, NULL, NULL }
};

+static const struct gdb_xml_attribute in_memory_library_attributes[] = {
+ { "begin", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+ { "end", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
static const struct gdb_xml_element library_list_children[] = {
{ "library", library_attributes, library_children,
GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
library_list_start_library, library_list_end_library },
+ { "in-memory-library", in_memory_library_attributes, library_children,
+ GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+ in_memory_library_list_start_library, library_list_end_library },
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
};

@@ -246,10 +295,35 @@ solib_target_current_sos (void)
for (lm_info_target_up &info : library_list)
{
auto &new_solib = sos.emplace_back ();
+ switch (info->location)
+ {
+ case lm_on_disk:
+ /* We don't need a copy of the name in INFO anymore. */
+ new_solib.so_name = std::move (info->name);
+ new_solib.so_original_name = new_solib.so_name;
+ break;
+
+ case lm_in_memory:
+ {
+ if (info->end <= info->begin)
+ error (_("bad in-memory-library location: begin=%s, end=%s"),
+ core_addr_to_string_nz (info->begin),
+ core_addr_to_string_nz (info->end));
+
+ /* Give it a name although this isn't really needed. */
+ std::string orig_name
+ = std::string ("in-memory-")
+ + core_addr_to_string_nz (info->begin)
+ + "-"
+ + core_addr_to_string_nz (info->end);
+
+ new_solib.so_original_name = orig_name;
+ new_solib.begin = info->begin;
+ new_solib.end = info->end;
+ }
+ break;
+ }

- /* We don't need a copy of the name in INFO anymore. */
- new_solib.so_name = std::move (info->name);
- new_solib.so_original_name = new_solib.so_name;
new_solib.lm_info = std::move (info);
}

@@ -414,4 +488,5 @@ const solib_ops solib_target_so_ops =
nullptr,
nullptr,
default_find_solib_addr,
+ gdb_bfd_open_from_target_memory,
};
diff --git a/gdb/solib.c b/gdb/solib.c
index fdefdf0b1423032fa39f8ee9aae7f1c35f13e1d1..cb302f9215257ddff18c94f5be39e189b02d84e6 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -485,8 +485,29 @@ solib_map_sections (solib &so)
{
const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ());

- gdb::unique_xmalloc_ptr<char> filename (tilde_expand (so.so_name.c_str ()));
- gdb_bfd_ref_ptr abfd (ops->bfd_open (filename.get ()));
+ gdb_bfd_ref_ptr abfd;
+ if (so.so_name[0] != '\0')
+ {
+ gdb::unique_xmalloc_ptr<char> filename
+ (tilde_expand (so.so_name.c_str ()));
+ abfd = ops->bfd_open (filename.get ());
+ }
+ else if (so.begin != 0 && so.end != 0)
+ {
+ if (ops->bfd_open_from_target_memory == nullptr)
+ error (_("Target does not support in-memory shared libraries."));
+
+ if (so.end <= so.begin)
+ error (_("Bad address range [%s; %s) for in-memory shared library."),
+ core_addr_to_string_nz (so.begin),
+ core_addr_to_string_nz (so.end));
+
+ abfd = ops->bfd_open_from_target_memory (so.begin,
+ so.end - so.begin,
+ gnutarget);
+ }
+ else
+ internal_error (_("bad so_list"));

/* If we have a core target then the core target might have some helpful
information (i.e. build-ids) about the shared libraries we are trying
@@ -533,7 +554,7 @@ solib_map_sections (solib &so)
{
warning (_ ("Build-id of %ps does not match core file."),
styled_string (file_name_style.style (),
- filename.get ()));
+ so.so_name.c_str ()));
abfd = nullptr;
}
}
diff --git a/gdb/solist.h b/gdb/solist.h
index 8ad0aefd9e32b264b08a853eb3577829a8e74767..c4d053a5c6c0261f1176121b2bc1f904ac2424a4 100644
--- a/gdb/solist.h
+++ b/gdb/solist.h
@@ -67,9 +67,14 @@ struct solib : intrusive_list_node<solib>
map we've already loaded. */
std::string so_original_name;

- /* Shared object file name, expanded to something GDB can open. */
+ /* Shared object file name, expanded to something GDB can open. This is
+ an empty string for in-memory shared objects. */
std::string so_name;

+ /* The address range of an in-memory shared object. Both BEGIN and END
+ are zero for on-disk shared objects. */
+ CORE_ADDR begin, end;
+
/* The following fields of the structure are built from
information gathered from the shared object file itself, and
are set when we actually add it to our symbol tables.
@@ -180,6 +185,12 @@ struct solib_ops
name). */

std::optional<CORE_ADDR> (*find_solib_addr) (solib &so);
+
+ /* Open an in-memory shared library at ADDR of at most SIZE bytes. The
+ TARGET string is used to identify the target. */
+ gdb_bfd_ref_ptr (*bfd_open_from_target_memory) (CORE_ADDR addr,
+ CORE_ADDR size,
+ const char *target);
};

/* A unique pointer to a so_list. */
diff --git a/gdbserver/dll.cc b/gdbserver/dll.cc
index f49aa560aec57e246948d944ec377b0beb8d1dc5..c2c63e9a69a54ddede77e40ea33b4e19cbdc71af 100644
--- a/gdbserver/dll.cc
+++ b/gdbserver/dll.cc
@@ -40,6 +40,17 @@ loaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
proc->dlls_changed = true;
}

+/* Record a newly loaded in-memory DLL at BASE_ADDR for PROC. */
+
+void
+loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr)
+{
+ gdb_assert (proc != nullptr);
+ proc->all_dlls.emplace_back (begin, end, base_addr);
+ proc->dlls_changed = true;
+}
+
/* Record that the DLL with NAME and BASE_ADDR has been unloaded
from the current process. */

@@ -58,6 +69,9 @@ unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
gdb_assert (proc != nullptr);
auto pred = [&] (const dll_info &dll)
{
+ if (dll.location != dll_info::on_disk)
+ return false;
+
if (base_addr != UNSPECIFIED_CORE_ADDR
&& base_addr == dll.base_addr)
return true;
@@ -89,3 +103,48 @@ unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
proc->dlls_changed = true;
}
}
+
+/* Record that the in-memory DLL from BEGIN to END loaded at BASE_ADDR has been
+ unloaded. */
+
+void
+unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr)
+{
+ gdb_assert (proc != nullptr);
+ auto pred = [&] (const dll_info &dll)
+ {
+ if (dll.location != dll_info::in_memory)
+ return false;
+
+ if (base_addr != UNSPECIFIED_CORE_ADDR
+ && base_addr == dll.base_addr)
+ return true;
+
+ /* We do not require the end address to be specified - we don't
+ support partially unloaded libraries, anyway. */
+ if (begin != UNSPECIFIED_CORE_ADDR
+ && begin == dll.begin
+ && (end == UNSPECIFIED_CORE_ADDR
+ || end == dll.end))
+ return true;
+
+ return false;
+ };
+
+ auto iter = std::find_if (proc->all_dlls.begin (), proc->all_dlls.end (),
+ pred);
+
+ if (iter == proc->all_dlls.end ())
+ /* For some inferiors we might get unloaded_dll events without having
+ a corresponding loaded_dll. In that case, the dll cannot be found
+ in ALL_DLL, and there is nothing further for us to do. */
+ return;
+ else
+ {
+ /* DLL has been found so remove the entry and free associated
+ resources. */
+ proc->all_dlls.erase (iter);
+ proc->dlls_changed = 1;
+ }
+}
diff --git a/gdbserver/dll.h b/gdbserver/dll.h
index e603df4be2b26017fa5b795f861d8d4111779d85..9bce28e70cf3939b5b062e4b9c0f0d2d10a1d1d7 100644
--- a/gdbserver/dll.h
+++ b/gdbserver/dll.h
@@ -24,19 +24,36 @@ struct process_info;

struct dll_info
{
+ enum location_t
+ {
+ on_disk,
+ in_memory
+ };
+
dll_info (const std::string &name_, CORE_ADDR base_addr_)
- : name (name_), base_addr (base_addr_)
+ : location (on_disk), name (name_), base_addr (base_addr_)
+ {}
+
+ dll_info (CORE_ADDR begin_, CORE_ADDR end_, CORE_ADDR base_addr_)
+ : location (in_memory), begin (begin_), end (end_), base_addr (base_addr_)
{}

+ location_t location;
std::string name;
+ CORE_ADDR begin;
+ CORE_ADDR end;
CORE_ADDR base_addr;
};

extern void loaded_dll (const char *name, CORE_ADDR base_addr);
extern void loaded_dll (process_info *proc, const char *name,
CORE_ADDR base_addr);
+extern void loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr);
extern void unloaded_dll (const char *name, CORE_ADDR base_addr);
extern void unloaded_dll (process_info *proc, const char *name,
CORE_ADDR base_addr);
+extern void unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr);

#endif /* GDBSERVER_DLL_H */
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 264fac44b74db69746fdecb1cdd1fd748139cc4f..90902099a1b5a84fe86cb8391bf983a5943e78c5 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -1884,6 +1884,97 @@ handle_qxfer_features (const char *annex,
return len;
}

+static std::string
+dll_to_tmpfile (dll_info &dll)
+{
+ if (dll.end <= dll.begin)
+ error (_("bad in-memory-library location: begin=%s, end=%s"),
+ core_addr_to_string_nz (dll.begin),
+ core_addr_to_string_nz (dll.end));
+
+ gdb::byte_vector buffer (dll.end - dll.begin);
+ int errcode = gdb_read_memory (dll.begin, buffer.data (), buffer.size ());
+ if (errcode != 0)
+ error (_("failed to read in-memory library at %s..%s"),
+ core_addr_to_string_nz (dll.begin),
+ core_addr_to_string_nz (dll.end));
+
+ std::string name
+ = std::string ("gdb-in-memory-solib-")
+ + core_addr_to_string_nz (dll.begin)
+ + "-"
+ + core_addr_to_string_nz (dll.end);
+
+ gdb_file_up file = gdb_create_tmpfile (name);
+
+ size_t written = fwrite (buffer.data (), buffer.size (), 1, file.get ());
+ if (written != 1)
+ error (_("failed to write into %s"), name.c_str ());
+
+ return name;
+}
+
+/* Print a qXfer:libraries:read entry for DLL. */
+
+static std::string
+print_qxfer_libraries_entry (dll_info &dll)
+{
+ switch (dll.location)
+ {
+ case dll_info::in_memory:
+ if (get_client_state ().in_memory_library_supported)
+ return string_printf
+ (" <in-memory-library begin=\"0x%s\" end=\"0x%s\">"
+ "<segment address=\"0x%s\"/></in-memory-library>\n",
+ paddress (dll.begin), paddress (dll.end),
+ paddress (dll.base_addr));
+
+ /* GDB does not support in-memory-library. Fall back to storing it in a
+ temporary file and report that file to GDB. */
+ dll.name = dll_to_tmpfile (dll);
+ [[fallthrough]];
+
+ case dll_info::on_disk:
+ return string_printf
+ (" <library name=\"%s\"><segment address=\"0x%s\"/></library>\n",
+ dll.name.c_str (), paddress (dll.base_addr));
+ }
+
+ warning (_("unknown dll location: %x"), dll.location);
+ return std::string ();
+}
+
+/* Determine the library-list version required for communicating the shared
+ libraries. */
+
+static std::string
+library_list_version_needed (const std::list<dll_info> &dlls)
+{
+ const client_state &cs = get_client_state ();
+ int major = 1, minor = 0;
+
+ for (const dll_info &dll : dlls)
+ {
+ switch (dll.location)
+ {
+ case dll_info::on_disk:
+ major = std::max (major, 1);
+ minor = std::max (minor, 0);
+ break;
+
+ case dll_info::in_memory:
+ if (cs.in_memory_library_supported)
+ {
+ major = std::max (major, 1);
+ minor = std::max (minor, 1);
+ }
+ break;
+ }
+ }
+
+ return std::to_string (major) + std::string (".") + std::to_string (minor);
+}
+
/* Handle qXfer:libraries:read. */

static int
@@ -1897,13 +1988,13 @@ handle_qxfer_libraries (const char *annex,
if (annex[0] != '\0' || current_thread == NULL)
return -1;

- std::string document = "<library-list version=\"1.0\">\n";
-
process_info *proc = current_process ();
- for (const dll_info &dll : proc->all_dlls)
- document += string_printf
- (" <library name=\"%s\"><segment address=\"0x%s\"/></library>\n",
- dll.name.c_str (), paddress (dll.base_addr));
+ std::string document = "<library-list version=\""
+ + library_list_version_needed (proc->all_dlls)
+ + "\">\n";
+
+ for (dll_info &dll : proc->all_dlls)
+ document += print_qxfer_libraries_entry (dll);

document += "</library-list>\n";

@@ -2761,6 +2852,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
}
else if (feature == "error-message+")
cs.error_message_supported = true;
+ else if (feature == "qXfer:libraries:read:in-memory-library+")
+ cs.in_memory_library_supported = true;
else
{
/* Move the unknown features all together. */
@@ -2791,6 +2884,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
/* We do not have any hook to indicate whether the non-SVR4 target
backend supports qXfer:libraries:read, so always report it. */
strcat (own_buf, ";qXfer:libraries:read+");
+ strcat (own_buf, ";qXfer:libraries:read:in-memory-library+");
}

if (the_target->supports_read_auxv ())
diff --git a/gdbserver/server.h b/gdbserver/server.h
index e1297d41f8406dab670397f5600e4e08558f23b7..3bacd1062f8bf47720189d2d02b36ddb6089e6bc 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -168,6 +168,9 @@ struct client_state
space randomization feature before starting an inferior. */
int disable_randomization = 1;

+ /* True if qXfer:libraries:read supports in-memory-library. */
+ bool in_memory_library_supported = false;
+
int pass_signals[GDB_SIGNAL_LAST];
int program_signals[GDB_SIGNAL_LAST];
int program_signals_p = 0;