To help gdb reload symbol files from /proc/<pid>/maps, making it
possible to load debug info for ELF and PE modules for Wine processes.
When sourced (from ~/.gdbinit for instance), this adds a new
"load-symbol-files" command (with an "lsf" alias), which automatically
calls add-symbol-file on every mapped file that can be read as ELF or
PE, with the correct section offset.
The command has to be run manually, for instance after executing for
a while, to load new modules that may have been loaded, as there's no
way for gdb to be notified of such changes automatically.
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
This is some script I've been using for a while, and more or less broken
versions are also used by other people, so I figured maybe it would be
better to have a proper working version officially distributed with Wine
source instead, as it's pretty useful for debugging Wine under gdb.
It's still a bit manual to use, as gdb cannot easily be notified of
dynamic library loading [1], but I think it's much better than having
nothing.
[1] in theory there's ways to do it using systemtap probes, but it's
going to be hard to make it work, especially for PE modules.
tools/gdbinit.py | 108 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 108 insertions(+)
create mode 100644 tools/gdbinit.py
diff --git a/tools/gdbinit.py b/tools/gdbinit.py
new file mode 100644
index 00000000000..265d97722b7
--- /dev/null
+++ b/tools/gdbinit.py
@@ -0,0 +1,108 @@
+#!/bin/env python3
+
+# Copyright 2021 Rémi Bernon for CodeWeavers
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+from __future__ import print_function
+
+import gdb
+import re
+import subprocess
+import sys
+
+class LoadSymbolFiles(gdb.Command):
+ 'Command to load symbol files directly from /proc/<pid>/maps.'
+
+ def __init__(self):
+ sup = super(LoadSymbolFiles, self)
+ sup.__init__('load-symbol-files', gdb.COMMAND_FILES, gdb.COMPLETE_NONE,
+ False)
+
+ self.libs = {}
+ gdb.execute('alias -a lsf = load-symbol-files', True)
+
+ def invoke(self, arg, from_tty):
+ pid = gdb.selected_inferior().pid
+ if not pid in self.libs: self.libs[pid] = {}
+
+ def command(cmd, confirm=from_tty):
+ to_string = not from_tty
+ gdb.execute(cmd, from_tty=confirm, to_string=to_string)
+
+ def execute(cmd):
+ return subprocess.check_output(cmd, stderr=subprocess.STDOUT) \
+ .decode('utf-8')
+
+ # load mappings addresses
+ libs = {}
+ with open('/proc/{}/maps'.format(pid), 'r') as maps:
+ for line in maps:
+ addr, _, _, _, node, path = re.split(r'\s+', line, 5)
+ path = path.strip()
+ if node == '0': continue
+ if path in libs: continue
+ libs[path] = int(addr.split('-')[0], 16)
+
+ # unload symbol file if address changed
+ for k in set(libs) & set(self.libs[pid]):
+ if libs[k] != self.libs[pid][k]:
+ command('remove-symbol-file "{}"'.format(k), confirm=False)
+ del self.libs[k]
+
+ # load symbol file for new mappings
+ for k in set(libs) - set(self.libs[pid]):
+ if arg is not None and re.search(arg, k) is None: continue
+ addr = self.libs[pid][k] = libs[k]
+ offs = None
+
+ try:
+ out = execute(['file', k])
+ except:
+ continue
+
+ # try loading mapping as ELF
+ try:
+ out = execute(['readelf', '-l', k])
+ for line in out.split('\n'):
+ if not 'LOAD' in line: continue
+ base = int(line.split()[2], 16)
+ break
+
+ out = execute(['objdump', '-h', k])
+ for line in out.split('\n'):
+ if not '.text' in line: continue
+ offs = int(line.split()[3], 16) - base
+ break
+ if offs is None: continue
+
+ # try again, assuming mapping is PE
+ except:
+ try:
+ out = execute(['objdump', '-h', k])
+ for line in out.split('\n'):
+ if not '.text' in line: continue
+ offs = int(line.split()[5], 16)
+ break
+ if offs is None: continue
+
+ except:
+ continue
+
+ command('add-symbol-file "{}" 0x{:x}'.format(k, addr + offs),
+ confirm=False)
+
+
+LoadSymbolFiles()
--
2.31.0
I would love any and all feedback on updated versions of my "Reparse
Point" patches, which are now available in staging:
https://github.com/wine-staging/wine-staging/tree/master/patches/ntdll-Junc…
Major changes since the last RFC version are:
1) Deletion of symlinks is now atomic (where supported).
2) Regular Unix symlinks are now reported as WSL Unix symlinks.
3) FILE_OPEN_REPARSE_POINT is now properly supported for applications
that wish to access reparse points directly (symlink operations by
file descriptor instead of by path).
4) Wine's cmd "dir" command now shows reparse point types and targets.
5) Wine's cmd "mklink" command now supports creating junction points
and NT symlinks.
6) Absolute reparse points that are outside of the prefix no longer
contain the path to drive Z (or appropriate drive letter).
7) Absolute reparse points now contain a string identifying the end of
the Wine prefix that is used to modify the target path on file access
if a prefix has been relocated (if the target is within the prefix).
8) Many, many bug fixes.
Thank you in advance to those who look these patches over, and
previous iterations, your feedback is greatly appreciated! I know
that these patches have been a long time coming, but I believe that
we're finally getting to a place where we've figured out all the
gotchas. Hopefully other folks agree ;)
===
Context:
For those of you that wonder "why do these patches exist?", I have
been putting these together over the past few years for several
reasons:
1) More modern Windows programs use NT symlinks to avoid duplicating
files and taking up extra disk space (particularly .NET).
2) Wine currently has no mechanism to make symlinks on the "PE" side
of the dividing line, these patches allow this for Windows file paths
and a small additional patch (creating WSL Unix symlinks) allows the
same behavior for Unix paths.
3) Wine currently takes up a lot of space with system DLLs that are
duplicates of the system-wide files, a logical extension to these
patches is a Wine-specific reparse tag that allows these system DLLs
to be "copy-on-write" so that Wine prefixes don't take up unnecessary
disk space. Please note that this feature is not a focus of the
patchset (not included), as I would like to get the infrastructure in
place before tackling that problem.
Not currently supported:
1) Reparse points of type other than junction points, NT symlinks, and
WSL Unix symlinks.
2) Creating WSL Unix symlinks.
3) Changing ownership permissions (lrwxrwxrwx) on the symlink instead
of the target (currently a Linux limitation).
Implementation overview:
• Reparse points are implemented as Unix symlinks where relative
symlinks always start with "./" and absolute symlinks start with "/".
• Following the relative/absolute flag the 32-bit reparse point tag
(junction point or NT symlink) is encoded as a relative path where
each bit is encoded as "/" (0) or "./" (1). When creating WSL Unix
symlinks (future feature) this tag encoding will be skipped.
• For NT symlinks this is followed by a file/directory flag ("./" for
directories, "/" for files) so that dangling symlinks can still be
deleted properly with RemoveFile or RemoveDirectory (as appropriate).
• After the reparse tag (and file/directory flag) the Unix path is
then appended, relative paths are included unmodified and absolute
paths have some minor modifications.
• If the path is an absolute path and points outside the prefix then
the portion of the path to drive Z (or appropriate drive letter) is
removed.
• If the path is an absolute path that points inside the prefix then
after the prefix path an 8-bit 'P' is encoded the same way that the
reparse point tag is ("/"=0, "./"=1).
• If an absolute symlink is opened by the wineserver then the prefix
path is checked (if appropriate) and if it doesn't match the path of
the current Wine prefix then the symlink is modified to replace the
old prefix path with the current prefix path.
• Because reparse points involve a modification to an existing empty
file/directory, symlink creation and "removal" is implemented by
creating the symlink at a temporary location and then replacing the
original with an atomic renameat2 call (on supported systems),
equivalent call (BSD/Mac), or by unlinking the original and moving the
symlink into place (for legacy systems that do not support atomic
replacement).
Best,
Erich
I'm sending this as an RFC for now as there's been some discussion
already about how to handle PE dependencies. I think the approach I'm
taking here, by including FAudio headers directly in the source and
loading it dynamically is the easiest way, and the least disruptive for
building and running Wine, if not the cleanest. All the other solutions
have higher friction, either on third party packages, or on the system
distribution.
Upstream FAudio now has a pure Win32 backend, which makes it much easier
to build as PE. This was never a requirement, and this series should
work too with the SDL2 backend, as it tries to load Wine Mono SDL2 and
FAudio as a fallback after trying from the prefix system libraries.
It could help the transition though, and is going to be required anyway
to support WMA decoding. Right now, with or without the Win32 backend,
this would also introduce a functional regression as although supported
by the new FAudio Win32 backend, it also depends on the WMA decoder MF
transform to be implemented, which isn't the case yet in Wine.
Cheers,
Rémi Bernon (5):
xactengine3_7: Move sources to xaudio2_7.
xaudio2_7: Load FAudio dynamically.
xaudio2_7: Import FAudio headers for private use.
xaudio2_7: Build with msvcrt.
xaudio2_7: Use msvcrt allocation functions.
configure.ac | 55 -
dlls/x3daudio1_0/Makefile.in | 4 +-
dlls/x3daudio1_1/Makefile.in | 4 +-
dlls/x3daudio1_2/Makefile.in | 4 +-
dlls/x3daudio1_3/Makefile.in | 4 +-
dlls/x3daudio1_4/Makefile.in | 4 +-
dlls/x3daudio1_5/Makefile.in | 4 +-
dlls/x3daudio1_6/Makefile.in | 4 +-
dlls/x3daudio1_7/Makefile.in | 4 +-
dlls/xactengine2_0/Makefile.in | 6 +-
dlls/xactengine2_4/Makefile.in | 6 +-
dlls/xactengine2_7/Makefile.in | 6 +-
dlls/xactengine2_9/Makefile.in | 6 +-
dlls/xactengine3_0/Makefile.in | 6 +-
dlls/xactengine3_1/Makefile.in | 6 +-
dlls/xactengine3_2/Makefile.in | 6 +-
dlls/xactengine3_3/Makefile.in | 6 +-
dlls/xactengine3_4/Makefile.in | 6 +-
dlls/xactengine3_5/Makefile.in | 6 +-
dlls/xactengine3_6/Makefile.in | 6 +-
dlls/xactengine3_7/Makefile.in | 5 +-
dlls/xapofx1_1/Makefile.in | 4 +-
dlls/xapofx1_2/Makefile.in | 4 +-
dlls/xapofx1_3/Makefile.in | 4 +-
dlls/xapofx1_4/Makefile.in | 4 +-
dlls/xapofx1_5/Makefile.in | 4 +-
dlls/xaudio2_0/Makefile.in | 4 +-
dlls/xaudio2_1/Makefile.in | 4 +-
dlls/xaudio2_2/Makefile.in | 4 +-
dlls/xaudio2_3/Makefile.in | 4 +-
dlls/xaudio2_4/Makefile.in | 4 +-
dlls/xaudio2_5/Makefile.in | 4 +-
dlls/xaudio2_6/Makefile.in | 4 +-
dlls/xaudio2_7/FAudio/F3DAudio.h | 262 ++++
dlls/xaudio2_7/FAudio/FACT.h | 814 ++++++++++
dlls/xaudio2_7/FAudio/FACT3D.h | 127 ++
dlls/xaudio2_7/FAudio/FAPO.h | 207 +++
dlls/xaudio2_7/FAudio/FAPOBase.h | 264 ++++
dlls/xaudio2_7/FAudio/FAPOFX.h | 178 +++
dlls/xaudio2_7/FAudio/FAudio.h | 1322 +++++++++++++++++
dlls/xaudio2_7/FAudio/FAudioFX.h | 308 ++++
dlls/xaudio2_7/Makefile.in | 4 +-
dlls/xaudio2_7/compat.c | 92 +-
dlls/xaudio2_7/faudio.c | 326 ++++
dlls/xaudio2_7/x3daudio.c | 35 +-
.../xact_classes.idl | 0
dlls/{xactengine3_7 => xaudio2_7}/xact_dll.c | 182 ++-
dlls/xaudio2_7/xapo.c | 29 +-
dlls/xaudio2_7/xapofx.c | 21 +-
dlls/xaudio2_7/xaudio_allocator.c | 2 -
dlls/xaudio2_7/xaudio_dll.c | 267 ++--
dlls/xaudio2_7/xaudio_private.h | 149 +-
dlls/xaudio2_8/Makefile.in | 4 +-
dlls/xaudio2_9/Makefile.in | 4 +-
54 files changed, 4298 insertions(+), 505 deletions(-)
create mode 100644 dlls/xaudio2_7/FAudio/F3DAudio.h
create mode 100644 dlls/xaudio2_7/FAudio/FACT.h
create mode 100644 dlls/xaudio2_7/FAudio/FACT3D.h
create mode 100644 dlls/xaudio2_7/FAudio/FAPO.h
create mode 100644 dlls/xaudio2_7/FAudio/FAPOBase.h
create mode 100644 dlls/xaudio2_7/FAudio/FAPOFX.h
create mode 100644 dlls/xaudio2_7/FAudio/FAudio.h
create mode 100644 dlls/xaudio2_7/FAudio/FAudioFX.h
create mode 100644 dlls/xaudio2_7/faudio.c
rename dlls/{xactengine3_7 => xaudio2_7}/xact_classes.idl (100%)
rename dlls/{xactengine3_7 => xaudio2_7}/xact_dll.c (87%)
--
2.31.0
Over the years, Wine prefixes have gotten bigger and bigger, for a
number of reasons. Creating a new Wine prefix for each application is
still the current recommendation, as despite the best efforts of Wine
developers, some applications still require system-wide workarounds.
This leads to significant bloat for each application installed. With a
MinGW build of Wine without Mono or Gecko, new 32-bit prefixes are over
150 MB, and new 64-bit prefixes are over 300 MB. The vast majority of
these files are byte-for-byte identical to Wine's central DLL copies.
This patch set implements reflink support in Wine via the
copy_file_range syscall. The reasons for selecting copy_file_range over
FICLONE are outlined in patch 2. A previous unpublished version of this
patch set used FICLONERANGE, but it was less convenient to use from
setupapi and has inferior system support.
When reflink is supported by the underlying filesystem, new Wine prefix
sizes with Mono and Gecko disabled are reduced to less than 1 MB. The
resulting Wine prefix is byte-for-byte identical to one created without
reflink, but occupies less space on disk. If hard links or symlinks were
used, if an application such as winetricks writes to a system file, it
would overwrite the central copy. With reflink, the file blocks will be
transparently copied by the Linux kernel so that each Wine prefix can be
independent.
Some files cannot be deduplicated in the current Wine system, as they
are dynamically generated during the Wine prefix installation process.
These include 16-bit fake DLLs and manifest files. In theory, it should
be possible to pre-generate these files, but considering the Wine prefix
size is already reduced to less than 1 MB, the extra space savings are
likely not worth the effort.
Changes since v2:
Fix/improve errno handling.
Alex Xu (Hello71) (5):
ntdll: add support for IOCTL_COPYCHUNK.
kernelbase: use IOCTL_COPYCHUNK in CopyFile*
setupapi: Use IOCTL_COPYCHUNK, avoid buffering whole file
lmshare.h: define STYPE_TEMPORARY
kernel32/tests: add IOCTL_COPYCHUNK test
configure | 1 +
configure.ac | 1 +
dlls/kernel32/tests/file.c | 137 +++++++++++++++++++++++++
dlls/kernelbase/file.c | 44 ++++----
dlls/ntdll/unix/file.c | 121 ++++++++++++++++++++++
dlls/setupapi/fakedll.c | 202 ++++++++++++++++++++-----------------
include/config.h.in | 3 +
include/lmshare.h | 11 +-
include/winioctl.h | 34 +++++++
9 files changed, 431 insertions(+), 123 deletions(-)
--
2.33.0
From: Connor McAdams <conmanx360(a)gmail.com>
When RPC is done in a multi-threaded apartment environment, this method
may be called in a thread other than the one that the object was created
in. To account for this, make sure to check the UI thread the HWND
belongs to rather than the one belonging to the current thread.
Signed-off-by: Connor McAdams <conmanx360(a)gmail.com>
---
dlls/oleacc/client.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c
index 3b33be55a21..a726c607109 100644
--- a/dlls/oleacc/client.c
+++ b/dlls/oleacc/client.c
@@ -227,6 +227,7 @@ static HRESULT WINAPI Client_get_accRole(IAccessible *iface, VARIANT varID, VARI
static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VARIANT *pvarState)
{
Client *This = impl_from_Client(iface);
+ GUITHREADINFO info;
LONG style;
TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pvarState);
@@ -244,7 +245,10 @@ static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VAR
V_I4(pvarState) |= STATE_SYSTEM_UNAVAILABLE;
else if(IsWindow(This->hwnd))
V_I4(pvarState) |= STATE_SYSTEM_FOCUSABLE;
- if(GetFocus() == This->hwnd)
+
+ info.cbSize = sizeof(info);
+ if(GetGUIThreadInfo(GetWindowThreadProcessId(This->hwnd, NULL), &info) &&
+ info.hwndFocus == This->hwnd)
V_I4(pvarState) |= STATE_SYSTEM_FOCUSED;
if(!(style & WS_VISIBLE))
V_I4(pvarState) |= STATE_SYSTEM_INVISIBLE;
--
2.25.1