From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id qcsXFQR3fGpXmCAAWB0awg (envelope-from ) for ; Wed, 12 Aug 2026 09:37:08 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=fYnere9+; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 507E41E166; Wed, 12 Aug 2026 09:37:08 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-6.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [IPv6:2620:52:6:3111::32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 068231E033 for ; Wed, 12 Aug 2026 09:37:07 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 5B3264B9DB6E for ; Wed, 12 Aug 2026 13:37:05 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 5B3264B9DB6E Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=fYnere9+ Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.10]) by sourceware.org (Postfix) with ESMTPS id 2D1684BA9035 for ; Wed, 12 Aug 2026 13:29:55 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 2D1684BA9035 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=intel.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 2D1684BA9035 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=192.198.163.10 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1786541395; cv=none; b=XOZK0CyF39Xsaj4jMtfpslr8pbk6cDOfi0i8rA7YBQ/9WDTLKfRdDYbRx9bJvReS7xLNawhHiA8dAIoK5pdw2KWI2VwfFzfDDYnkcYUkc1vkaSQvEYdx4rqFFjL0NJX4CLIOg8OLNznkxENGfCPeC5D+3/LqwJs+p2bRNJ8J7Ec= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1786541395; c=relaxed/simple; bh=/Jw5ukSpBTPid5b08x5pRYcOSV/UwFEqSo6f77Cp8LM=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=Y67pzbTgMa4XO1gg0YBz7rJLuSct1Iw+2VGHhOYQmTQwJVL2FSDUsJszUcgBb8asayF6mf//3G6S3eMy0YFFFI/tPsarBkfx2Faga8X0ZTR49rLtEesQSXGa3/0BIBwsALBsajn9Pc7gUS/c9a5z6g/f/iCZuQWePNZGIUJBtss= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=fYnere9+ DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2D1684BA9035 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1786541395; x=1818077395; h=from:to:subject:date:message-id:in-reply-to:references: mime-version:content-transfer-encoding; bh=/Jw5ukSpBTPid5b08x5pRYcOSV/UwFEqSo6f77Cp8LM=; b=fYnere9+nInnVUCWTTXq2AWdOmieDMvkZdYMAAJ0NROLHlbOSfpLsAge D1w/T75NHJ7Yiik6cG6s1m9udV055Xu8EI2dlnlfPVl5KsSucWQS5U3R3 xf/LI0rb7ZvYyex0DK3HXlRprpz73Cv0LBqtHNgZjZMbYRgK0skPmVVbZ qsUItpIKmp0EM2upTlO8qiWVUWcO7AXN4sXanFes34uLWX91kmGVRLZtK HYuNqFXDcrKkhlNNdoL0j/n5Hd8Zhey2Awnw6jJ+oXaSz83dsFWYF399k Ej++LFvOrvexOLT6ZXE93hCInkX2oOLummz3AZpiwfA9WQUlMFboAHbrW Q==; X-CSE-ConnectionGUID: KAv/5rlGQoisB6eKYeirtA== X-CSE-MsgGUID: manw/sXBShW7ERb6+n+P4A== X-IronPort-AV: E=McAfee;i="6800,10657,11872"; a="98457808" X-IronPort-AV: E=Sophos;i="6.25,219,1779174000"; d="scan'208";a="98457808" Received: from orviesa004.jf.intel.com ([10.64.159.144]) by fmvoesa104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Aug 2026 06:29:54 -0700 X-CSE-ConnectionGUID: LYPn2xcuS8ehoPHBZ9f5PQ== X-CSE-MsgGUID: UUXwuM7PSJaCQC/w6TkkSw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,219,1779174000"; d="scan'208";a="267510165" Received: from gkldtt-dev-004.igk.intel.com (HELO localhost) ([10.123.221.202]) by orviesa004-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Aug 2026 06:29:54 -0700 From: Markus Metzger To: gdb-patches@sourceware.org Subject: [PATCH v4 34/44] gdb, gdbserver: library notifications Date: Wed, 12 Aug 2026 15:27:54 +0200 Message-ID: <20260812132805.380163-35-markus.t.metzger@intel.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260812132805.380163-1-markus.t.metzger@intel.com> References: <20260812132805.380163-1-markus.t.metzger@intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org Support asynchronous library notifications for non-stop remote targets. Libraries for GPU targets are loaded onto the device by the host process. The IntelGT remote target models devices as separate inferiors. This allows debugging only the device code of a process or only a particular device. To learn about changes in loaded device libraries, the target receives events from the Level-Zero debug API. Such events do not bind to a stop on the device (there may not even be any threads dispatched). To ensure that GDB had the chance to insert breakpoint into newly loaded libraries, the host thread that loaded a library will be blocked inside a system call until the library load is acknowledged by GDB. Add a new notification %Library: to asynchronously indicate changes in loaded libraries. This is similar to the 'library' stop reason. In the traditional flow, the thread that loaded or unloaded a library would hit a magic breakpoint and gdbserver would report the stop to GDB with the library stop reason. GDB would then query the list of libraries of the current process, insert breakpoints that bind into newly loaded libraries, and, when it is done, resume the thread. In this new flow, gdbserver would receive a library event and notify GDB. GDB would then query the list of libraries for the process indicated by the notification, insert breakpoints that bind into newly loaded libraries, and, when it is done, acknowledge the notification. Since libraries are changed from the outside, new libraries may be added and gdbserver may receive new library load events. To ensure that we acknowledge a library only when it has been processed by GDB, we hide newly added libraries until we send a %Library notification. When GDB acknowledges the notification, we know which libraries GDB has seen. A remote target needs to choose whether it wants to use library stop reasons or library notifications. --- gdb/NEWS | 4 ++ gdb/doc/gdb.texinfo | 8 ++++ gdb/remote-notif.c | 1 + gdb/remote-notif.h | 2 + gdb/remote.c | 92 +++++++++++++++++++++++++++++++++++++++ gdbserver/dll.cc | 46 ++++++++++++++++++++ gdbserver/dll.h | 21 +++++++++ gdbserver/notif.cc | 4 ++ gdbserver/notif.h | 4 ++ gdbserver/remote-utils.cc | 3 +- gdbserver/server.cc | 56 +++++++++++++++++++++++- gdbserver/server.h | 3 ++ gdbserver/target.cc | 18 ++++++++ gdbserver/target.h | 34 +++++++++++++++ 14 files changed, 294 insertions(+), 2 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 879f8e8865d..e6ed63128ec 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -18,6 +18,10 @@ qXfer:libraries:read's response The qXfer:libraries:read query supports reporting in-memory libraries. Older GDB will silently ignore them. +%Library + This notification informs about asynchronous changes in the loaded + libraries of a process. + *** Changes in GDB 18 * Support for the Common Trace Format (CTF) has been removed. GDB now diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 4cc755a0999..b36b7d01b23 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -47592,6 +47592,14 @@ for information on how these notifications are acknowledged by @value{GDBN}. @tab Report an asynchronous stop event in non-stop mode. +@item Library +@tab vLibrary +@tab @var{pid}. The process for which libraries have changed. +@tab Report asynchronous changes in the loaded libraries. +@value{GDBN} should use @samp{qXfer:libraries:read} to fetch a new +list of loaded libraries and acknowledge the notification when the +changes have been processed. + @end multitable @node Remote Non-Stop diff --git a/gdb/remote-notif.c b/gdb/remote-notif.c index 4884761f3ad..7fd9ad80a3d 100644 --- a/gdb/remote-notif.c +++ b/gdb/remote-notif.c @@ -48,6 +48,7 @@ bool notif_debug = false; static const notif_client *const notifs[] = { ¬if_client_stop, + ¬if_client_library, }; static_assert (ARRAY_SIZE (notifs) == REMOTE_NOTIF_LAST); diff --git a/gdb/remote-notif.h b/gdb/remote-notif.h index cec7db8025b..85b49eee245 100644 --- a/gdb/remote-notif.h +++ b/gdb/remote-notif.h @@ -41,6 +41,7 @@ using notif_event_up = std::unique_ptr; enum REMOTE_NOTIF_ID { REMOTE_NOTIF_STOP = 0, + REMOTE_NOTIF_LIBRARY, REMOTE_NOTIF_LAST, }; @@ -128,6 +129,7 @@ void remote_notif_process (struct remote_notif_state *state, remote_notif_state *remote_notif_state_allocate (remote_target *remote); extern const notif_client notif_client_stop; +extern const notif_client notif_client_library; extern bool notif_debug; diff --git a/gdb/remote.c b/gdb/remote.c index 10bceb2c798..20447fb5480 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -108,7 +108,9 @@ struct protocol_feature; struct packet_reg; struct stop_reply; +struct library_reply; using stop_reply_up = std::unique_ptr; +using library_reply_up = std::unique_ptr; /* Generic configuration support for packets the stub optionally supports. Allows the user to specify the use of the packet as well @@ -1641,6 +1643,17 @@ struct stop_reply : public notif_event int core; }; +struct library_reply : public notif_event +{ + /* The identifier of the process about this event. */ + int pid; + + /* The remote state this event is associated with. When the remote + connection, represented by a remote_state object, is closed, + all the associated library_reply events should be released. */ + struct remote_state *rs; +}; + /* Return TARGET as a remote_target if it is one, else nullptr. */ static remote_target * @@ -5312,6 +5325,16 @@ as_stop_reply_up (notif_event_up event) return stop_reply_up (stop_reply); } +/* Transfer ownership of the library_reply owned by EVENT to a + library_reply_up object. */ + +static library_reply_up +as_library_reply_up (notif_event_up event) +{ + library_reply *reply = static_cast (event.release ()); + return library_reply_up (reply); +} + /* Read, decode, and return a hex-encoded string from *PTR. Update *PTR to point at the first character past the end of the string that was read in. */ @@ -8161,6 +8184,75 @@ const notif_client notif_client_stop = REMOTE_NOTIF_STOP, }; +static void +remote_notif_library_parse (remote_target *remote, + const notif_client *self, const char *buf, + notif_event *event) +{ + library_reply *reply = (library_reply *) event; + + ULONGEST pid; + const char *endp = unpack_varlen_hex (buf, &pid); + if (endp == buf) + error (_("No process id in Library notification: %s."), buf); + if (endp[0] != 0) + error (_("Trailing junk in Library notification: %s."), buf); + + reply->pid = (int) pid; + if (((ULONGEST) reply->pid) != pid) + error (_("Bad Library notification process id: %s."), buf); +} + +static void +remote_notif_library_ack (remote_target *remote, + const notif_client *self, const char *buf, + notif_event_up event) +{ + library_reply_up reply = as_library_reply_up (std::move (event)); + + scoped_restore_current_thread restore_thread; + inferior *process = find_inferior_pid (remote, reply->pid); + if (process == nullptr) + { + process = remote->remote_add_inferior (false, reply->pid, -1, 1); + process->needs_setup = 1; + } + + switch_to_inferior_no_thread (process); + if (process->needs_setup) + setup_inferior (0); + + handle_solib_event (); + + putpkt (remote, self->ack_command); +} + +static int +remote_notif_library_can_get_pending_events (remote_target *remote, + const notif_client *self) +{ + return 1; +} + +static notif_event_up +remote_notif_library_alloc_reply () +{ + return notif_event_up (new library_reply ()); +} + +/* A client of notification Library. */ + +const notif_client notif_client_library = +{ + "Library", + "vLibrary", + remote_notif_library_parse, + remote_notif_library_ack, + remote_notif_library_can_get_pending_events, + remote_notif_library_alloc_reply, + REMOTE_NOTIF_LIBRARY, +}; + /* If CONTEXT contains any fork/vfork/clone child threads that have not been reported yet, remove them from the CONTEXT list. If such a thread exists it is because we are stopped at a fork/vfork/clone diff --git a/gdbserver/dll.cc b/gdbserver/dll.cc index 32d6dfb21a7..c2accb1ab7e 100644 --- a/gdbserver/dll.cc +++ b/gdbserver/dll.cc @@ -154,3 +154,49 @@ unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end, return false; }); } + +/* See dll.h. */ + +void +notify_dlls (process_info *proc) +{ + if (proc == nullptr) + return; + + for (dll_info &dll : proc->all_dlls) + dll.hidden = false; +} + +/* Acknowledge DLL in PROC. */ + +static void +ack_dll (process_info *proc, const dll_info &dll) +{ + switch (dll.location) + { + case dll_info::on_disk: + target_ack_library (proc, dll.name.c_str ()); + return; + + case dll_info::in_memory: + target_ack_in_memory_library (proc, dll.begin, dll.end); + return; + } + + gdb_assert_not_reached ("unknown dll location: %x", dll.location); +} + +/* See dll.h. */ + +void +ack_dlls (process_info *proc) +{ + if (proc == nullptr) + return; + + for (dll_info &dll : proc->all_dlls) + { + if (!dll.hidden) + ack_dll (proc, dll); + } +} diff --git a/gdbserver/dll.h b/gdbserver/dll.h index ab8ffdc1d8c..6a54ce3ece3 100644 --- a/gdbserver/dll.h +++ b/gdbserver/dll.h @@ -54,6 +54,21 @@ struct dll_info /* The base address at which the library is loaded. */ CORE_ADDR base_addr; + + /* Whether to tell GDB about this library. + + We use this when library notifications are used to track which + libraries belong to the current library event, so when GDB + acknowledges the event, we know which libraries GDB has acknowledged. + + New libraries that were added in the meantime need to wait for the + next library event. + + For targets that do not use library notifications, this will be + ignored and GDB will always get the full list of libraries. This + means that library annotations in stop replies cannot be mixed with + library notifications. */ + bool hidden = true; }; extern void loaded_dll (const char *name, CORE_ADDR base_addr); @@ -67,4 +82,10 @@ extern void unloaded_dll (process_info *proc, const char *name, extern void unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end, CORE_ADDR base_addr); +/* Clear the hidden flag for all libraries in PROC. */ +extern void notify_dlls (process_info *proc); + +/* Acknowledge all non-hidden libraries. */ +extern void ack_dlls (process_info *proc); + #endif /* GDBSERVER_DLL_H */ diff --git a/gdbserver/notif.cc b/gdbserver/notif.cc index 1745a96a051..c5649173eb6 100644 --- a/gdbserver/notif.cc +++ b/gdbserver/notif.cc @@ -52,6 +52,7 @@ static struct notif_server *notifs[] = { ¬if_stop, + ¬if_library, }; /* Write another event or an OK, if there are no more left, to @@ -104,6 +105,9 @@ handle_notif_ack (char *own_buf, int packet_len) remote_debug_printf ("%s: acking %d", np->ack_name, (int) np->queue.size ()); + if (np->ack != nullptr) + np->ack (head); + delete head; } diff --git a/gdbserver/notif.h b/gdbserver/notif.h index 18b507dce5b..49805b0048f 100644 --- a/gdbserver/notif.h +++ b/gdbserver/notif.h @@ -54,10 +54,14 @@ struct notif_server /* Write event EVENT to OWN_BUF. */ void (*write) (struct notif_event *event, char *own_buf); + + /* EVENT has been acknowledged by GDB. */ + void (*ack) (struct notif_event *event); }; using notif_server_p = struct notif_server *; extern struct notif_server notif_stop; +extern struct notif_server notif_library; int handle_notif_ack (char *own_buf, int packet_len); void notif_write_event (struct notif_server *notif, char *own_buf); diff --git a/gdbserver/remote-utils.cc b/gdbserver/remote-utils.cc index ffde4956776..ffd7d3b0fd5 100644 --- a/gdbserver/remote-utils.cc +++ b/gdbserver/remote-utils.cc @@ -1239,7 +1239,8 @@ prepare_resume_reply (char *buf, ptid_t ptid, const target_waitstatus &status) } } - if (current_process ()->dlls_changed) + if (current_process ()->dlls_changed + && !target_uses_library_notifications ()) { strcpy (buf, "library:;"); buf += strlen (buf); diff --git a/gdbserver/server.cc b/gdbserver/server.cc index 67ef448780f..880c077286d 100644 --- a/gdbserver/server.cc +++ b/gdbserver/server.cc @@ -272,9 +272,60 @@ in_queued_stop_replies (ptid_t ptid) struct notif_server notif_stop = { - "vStopped", "Stop", {}, vstop_notif_reply, + "vStopped", "Stop", {}, vstop_notif_reply, nullptr, }; +/* Library notifications. */ + +struct vlibrary_notif : public notif_event +{ + vlibrary_notif (ptid_t::pid_type pid_) : pid (pid_) + {} + + /* The process that got the event. */ + int pid; +}; + +static void +vlibrary_notif_reply (struct notif_event *event, char *own_buf) +{ + gdb_assert (target_uses_library_notifications ()); + + vlibrary_notif *notif = (vlibrary_notif *) event; + int pid = notif->pid; + + process_info *process = find_process_pid (pid); + notify_dlls (process); + + if (pid < 0) + sprintf (own_buf, "-%x", pid); + else + sprintf (own_buf, "%x", pid); +} + +static void +vlibrary_notif_ack (struct notif_event *event) +{ + vlibrary_notif *notif = (vlibrary_notif *) event; + process_info *process = find_process_pid (notif->pid); + ack_dlls (process); +} + +struct notif_server notif_library = +{ + "vLibrary", "Library", {}, vlibrary_notif_reply, vlibrary_notif_ack, +}; + +void +push_notif_library (process_info *process) +{ + if (!process->dlls_changed) + return; + + notif_push (¬if_library, new vlibrary_notif (process->pid)); + process->dlls_changed = false; +} + static int target_running (void) { @@ -1906,6 +1957,9 @@ handle_qxfer_features (const char *annex, static std::string print_qxfer_libraries_entry (const dll_info &dll) { + if (dll.hidden && target_uses_library_notifications ()) + return ""; + switch (dll.location) { case dll_info::in_memory: diff --git a/gdbserver/server.h b/gdbserver/server.h index bbba3650de7..23ac219138e 100644 --- a/gdbserver/server.h +++ b/gdbserver/server.h @@ -92,6 +92,9 @@ extern void discard_queued_stop_replies (ptid_t ptid); the vStopped notifications queue. */ extern int in_queued_stop_replies (ptid_t ptid); +/* Push a notification to GDB for library changes of PROCESS. */ +void push_notif_library (process_info *process); + #include "remote-utils.h" #include "utils.h" diff --git a/gdbserver/target.cc b/gdbserver/target.cc index 5f0d9000254..3c569c8c66c 100644 --- a/gdbserver/target.cc +++ b/gdbserver/target.cc @@ -875,3 +875,21 @@ process_stratum_target::get_ipa_tdesc_idx () { return 0; } + +bool +process_stratum_target::uses_library_notifications () +{ + return false; +} + +void +process_stratum_target::ack_library (process_info *process, const char *name) +{ +} + +void +process_stratum_target::ack_in_memory_library (process_info *process, + CORE_ADDR begin, + CORE_ADDR end) +{ +} diff --git a/gdbserver/target.h b/gdbserver/target.h index e38570dabb2..685bdb8d113 100644 --- a/gdbserver/target.h +++ b/gdbserver/target.h @@ -535,6 +535,21 @@ class process_stratum_target Returns true if successful and false otherwise. */ virtual bool store_memtags (CORE_ADDR address, size_t len, const gdb::byte_vector &tags, int type); + + /* Whether the target uses library notifications instead of library + clauses in stop replies. */ + virtual bool uses_library_notifications (); + + /* Acknowledge a library reported by name. + + This if only relevant if using library notifications. */ + virtual void ack_library (process_info *process, const char *name); + + /* Acknowledge an in-memory library reported by address. + + This if only relevant if using library notifications. */ + virtual void ack_in_memory_library (process_info *process, CORE_ADDR begin, + CORE_ADDR end); }; extern process_stratum_target *the_target; @@ -732,6 +747,25 @@ target_thread_pending_child (thread_info *thread, target_waitkind *kind) return the_target->thread_pending_child (thread, kind); } +static inline bool +target_uses_library_notifications () +{ + return the_target->uses_library_notifications (); +} + +static inline void +target_ack_library (process_info *process, const char *name) +{ + the_target->ack_library (process, name); +} + +static inline void +target_ack_in_memory_library (process_info *process, CORE_ADDR begin, + CORE_ADDR end) +{ + the_target->ack_in_memory_library (process, begin, end); +} + /* Read LEN bytes from MEMADDR in the buffer MYADDR. Return 0 if the read is successful, otherwise, return a non-zero error code. */ -- 2.43.0 ________________________________________ Intel Deutschland GmbH Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany Tel: +49 (89) 99143-0 www.intel.de Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman Chairperson of the Supervisory Board: Sonja Pierer Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928 This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.