Skip to content

Commit e0dbd02

Browse files
committed
[lldb/test] Make TestLoadUnload compatible with windows
Summary: This patch introduces a header "dylib.h" which can be used in tests to handle shared libraries semi-portably. The shared library APIs on windows and posix systems look very different, but their underlying functionality is relatively similar, so the mapping is not difficult. It also introduces two new macros to wrap the functinality necessary to export/import function across the dll boundary on windows. Previously we had the LLDB_TEST_API macro for this purpose, which automagically changed meaning depending on whether we were building the shared library or the executable. While convenient for simple cases, this approach was not sufficient for the more complicated setups where one deals with multiple shared libraries. Lastly it rewrites TestLoadUnload, to make use of the new APIs. The trickiest aspect there is the handling of DYLD_LIBRARY_PATH on macos -- previously setting this variable was not needed as the test used @executable_path-relative dlopens, but the new generic api does not support that. Other systems do not support such dlopens either so the test already contained support for setting the appropriate path variable, and this patch just makes that logic more generic. In doesn't seem that the purpose of this test was to exercise @executable_path imports, so this should not be a problem. These changes are sufficient to make some of the TestLoadUnload tests pass on windows. Two other tests will start to pass once D77287 lands. Reviewers: amccarth, jingham, JDevlieghere, compnerd Subscribers: lldb-commits Tags: #lldb Differential Revision: https://reviews.llvm.org/D77662
1 parent 0c61e91 commit e0dbd02

File tree

10 files changed

+149
-130
lines changed

10 files changed

+149
-130
lines changed

lldb/packages/Python/lldbsuite/test/lldbplatformutil.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,19 +166,20 @@ def findMainThreadCheckerDylib():
166166
class _PlatformContext(object):
167167
"""Value object class which contains platform-specific options."""
168168

169-
def __init__(self, shlib_environment_var, shlib_prefix, shlib_extension):
169+
def __init__(self, shlib_environment_var, shlib_path_separator, shlib_prefix, shlib_extension):
170170
self.shlib_environment_var = shlib_environment_var
171+
self.shlib_path_separator = shlib_path_separator
171172
self.shlib_prefix = shlib_prefix
172173
self.shlib_extension = shlib_extension
173174

174175

175176
def createPlatformContext():
176177
if platformIsDarwin():
177-
return _PlatformContext('DYLD_LIBRARY_PATH', 'lib', 'dylib')
178+
return _PlatformContext('DYLD_LIBRARY_PATH', ':', 'lib', 'dylib')
178179
elif getPlatform() in ("freebsd", "linux", "netbsd"):
179-
return _PlatformContext('LD_LIBRARY_PATH', 'lib', 'so')
180+
return _PlatformContext('LD_LIBRARY_PATH', ':', 'lib', 'so')
180181
else:
181-
return None
182+
return _PlatformContext('PATH', ';', '', 'dll')
182183

183184

184185
def hasChattyStderr(test_case):

lldb/packages/Python/lldbsuite/test/make/Makefile.rules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ endif
474474
# Additional system libraries
475475
#----------------------------------------------------------------------
476476
ifeq (1,$(USE_LIBDL))
477-
ifneq ($(OS),NetBSD)
477+
ifeq (,$(filter $(OS), NetBSD Windows_NT))
478478
LDFLAGS += -ldl
479479
endif
480480
endif
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#ifndef LLDB_TEST_DYLIB_H
2+
#define LLDB_TEST_DYLIB_H
3+
4+
#include <stdio.h>
5+
6+
#ifdef _WIN32
7+
#include <Windows.h>
8+
9+
#define dylib_get_symbol(handle, name) GetProcAddress((HMODULE)handle, name)
10+
#define dylib_close(handle) (!FreeLibrary((HMODULE)handle))
11+
#else
12+
#include <dlfcn.h>
13+
14+
#define dylib_get_symbol(handle, name) dlsym(handle, name)
15+
#define dylib_close(handle) dlclose(handle)
16+
#endif
17+
18+
19+
inline void *dylib_open(const char *name) {
20+
char dylib_prefix[] =
21+
#ifdef _WIN32
22+
"";
23+
#else
24+
"lib";
25+
#endif
26+
char dylib_suffix[] =
27+
#ifdef _WIN32
28+
".dll";
29+
#elif defined(__APPLE__)
30+
".dylib";
31+
#else
32+
".so";
33+
#endif
34+
char fullname[1024];
35+
snprintf(fullname, sizeof(fullname), "%s%s%s", dylib_prefix, name, dylib_suffix);
36+
#ifdef _WIN32
37+
return LoadLibraryA(fullname);
38+
#else
39+
return dlopen(fullname, RTLD_NOW);
40+
#endif
41+
}
42+
43+
inline const char *dylib_last_error() {
44+
#ifndef _WIN32
45+
return dlerror();
46+
#else
47+
DWORD err = GetLastError();
48+
char *msg;
49+
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
50+
NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *)&msg, 0, NULL);
51+
return msg;
52+
#endif
53+
}
54+
55+
#endif

lldb/packages/Python/lldbsuite/test/make/test_common.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
// This header is included in all the test programs (C and C++) and provides a
22
// hook for dealing with platform-specifics.
3+
34
#if defined(_WIN32) || defined(_WIN64)
4-
#ifdef COMPILING_LLDB_TEST_DLL
5-
#define LLDB_TEST_API __declspec(dllexport)
5+
#define LLDB_DYLIB_EXPORT __declspec(dllexport)
6+
#define LLDB_DYLIB_IMPORT __declspec(dllimport)
67
#else
7-
#define LLDB_TEST_API __declspec(dllimport)
8+
#define LLDB_DYLIB_EXPORT
9+
#define LLDB_DYLIB_IMPORT
810
#endif
11+
12+
#ifdef COMPILING_LLDB_TEST_DLL
13+
#define LLDB_TEST_API LLDB_DYLIB_EXPORT
914
#else
10-
#define LLDB_TEST_API
15+
#define LLDB_TEST_API LLDB_DYLIB_IMPORT
1116
#endif
1217

1318
#if defined(_WIN32)

lldb/test/API/functionalities/load_unload/Makefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ ifeq ($(OS),Darwin)
2626
install_name_tool -id @executable_path/libloadunload_d.dylib libloadunload_d.dylib
2727
endif
2828

29-
hidden_lib_d:
30-
mkdir -p hidden
29+
hidden_lib_d: hidden
3130
$(MAKE) VPATH=$(SRCDIR)/hidden -C hidden -f $(MAKEFILE_RULES) \
3231
DYLIB_ONLY=YES DYLIB_CXX_SOURCES=d.cpp DYLIB_NAME=loadunload_d

lldb/test/API/functionalities/load_unload/TestLoadUnload.py

Lines changed: 21 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from lldbsuite.test import lldbutil
1414

1515

16-
@skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently
1716
class LoadUnloadTestCase(TestBase):
1817

1918
mydir = TestBase.compute_mydir(__file__)
@@ -35,25 +34,15 @@ def setUp(self):
3534

3635
def setup_test(self):
3736
lldbutil.mkdir_p(self.getBuildArtifact("hidden"))
38-
if not self.platformIsDarwin():
39-
if not lldb.remote_platform and "LD_LIBRARY_PATH" in os.environ:
40-
self.runCmd(
41-
"settings set target.env-vars " +
42-
self.dylibPath +
43-
"=" +
44-
os.environ["LD_LIBRARY_PATH"] +
45-
":" +
46-
self.getBuildDir())
47-
else:
48-
if lldb.remote_platform:
49-
wd = lldb.remote_platform.GetWorkingDirectory()
50-
else:
51-
wd = self.getBuildDir()
52-
self.runCmd(
53-
"settings set target.env-vars " +
54-
self.dylibPath +
55-
"=" +
56-
wd)
37+
if lldb.remote_platform:
38+
path = lldb.remote_platform.GetWorkingDirectory()
39+
else:
40+
path = self.getBuildDir()
41+
if self.dylibPath in os.environ:
42+
sep = self.platformContext.shlib_path_separator
43+
path = os.environ[self.dylibPath] + sep + path
44+
self.runCmd("settings append target.env-vars '{}={}'".format(self.dylibPath, path))
45+
self.default_path = path
5746

5847
def copy_shlibs_to_remote(self, hidden_dir=False):
5948
""" Copies the shared libs required by this test suite to remote.
@@ -142,16 +131,12 @@ def test_modules_search_paths(self):
142131
# Obliterate traces of libd from the old ___location.
143132
os.remove(old_dylib)
144133
# Inform (DY)LD_LIBRARY_PATH of the new path, too.
145-
env_cmd_string = "settings set target.env-vars " + self.dylibPath + "=" + new_dir
134+
env_cmd_string = "settings replace target.env-vars " + self.dylibPath + "=" + new_dir
146135
if self.TraceOn():
147136
print("Set environment to: ", env_cmd_string)
148137
self.runCmd(env_cmd_string)
149138
self.runCmd("settings show target.env-vars")
150139

151-
remove_dyld_path_cmd = "settings remove target.env-vars " + self.dylibPath
152-
self.addTearDownHook(
153-
lambda: self.dbg.HandleCommand(remove_dyld_path_cmd))
154-
155140
self.runCmd("run")
156141

157142
self.expect(
@@ -195,17 +180,14 @@ def test_dyld_library_path(self):
195180
new_dir = os.path.join(wd, special_dir)
196181
old_dylib = os.path.join(old_dir, dylibName)
197182

198-
remove_dyld_path_cmd = "settings remove target.env-vars " \
199-
+ self.dylibPath
200-
self.addTearDownHook(
201-
lambda: self.dbg.HandleCommand(remove_dyld_path_cmd))
202-
203183
# For now we don't track (DY)LD_LIBRARY_PATH, so the old
204184
# library will be in the modules list.
205185
self.expect("target modules list",
206186
substrs=[os.path.basename(old_dylib)],
207187
matching=True)
208188

189+
self.runCmd(env_cmd_string)
190+
209191
lldbutil.run_break_set_by_file_and_line(
210192
self, "d.cpp", self.line_d_function, num_expected_locations=1)
211193
# After run, make sure the non-hidden library is picked up.
@@ -214,10 +196,9 @@ def test_dyld_library_path(self):
214196
self.runCmd("continue")
215197

216198
# Add the hidden directory first in the search path.
217-
env_cmd_string = ("settings set target.env-vars %s=%s" %
218-
(self.dylibPath, new_dir))
219-
if not self.platformIsDarwin():
220-
env_cmd_string += ":" + wd
199+
env_cmd_string = ("settings set target.env-vars %s=%s%s%s" %
200+
(self.dylibPath, new_dir,
201+
self.platformContext.shlib_path_separator, self.default_path))
221202
self.runCmd(env_cmd_string)
222203

223204
# This time, the hidden library should be picked up.
@@ -228,7 +209,7 @@ def test_dyld_library_path(self):
228209
hostoslist=["windows"],
229210
triple='.*-android')
230211
@skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
231-
@skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently
212+
@expectedFailureAll(oslist=["windows"]) # process load not implemented
232213
def test_lldb_process_load_and_unload_commands(self):
233214
self.setSvr4Support(False)
234215
self.run_lldb_process_load_and_unload_commands()
@@ -238,7 +219,7 @@ def test_lldb_process_load_and_unload_commands(self):
238219
hostoslist=["windows"],
239220
triple='.*-android')
240221
@skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
241-
@skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently
222+
@expectedFailureAll(oslist=["windows"]) # process load not implemented
242223
def test_lldb_process_load_and_unload_commands_with_svr4(self):
243224
self.setSvr4Support(True)
244225
self.run_lldb_process_load_and_unload_commands()
@@ -314,11 +295,13 @@ def run_lldb_process_load_and_unload_commands(self):
314295
self.runCmd("process continue")
315296

316297
@skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
298+
@expectedFailureAll(oslist=["windows"]) # breakpoint not hit
317299
def test_load_unload(self):
318300
self.setSvr4Support(False)
319301
self.run_load_unload()
320302

321303
@skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
304+
@expectedFailureAll(oslist=["windows"]) # breakpoint not hit
322305
def test_load_unload_with_svr4(self):
323306
self.setSvr4Support(True)
324307
self.run_load_unload()
@@ -362,15 +345,13 @@ def run_load_unload(self):
362345
substrs=[' resolved, hit count = 2'])
363346

364347
@skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
365-
@skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently
366348
@expectedFailureAll(archs="aarch64", oslist="linux",
367349
bugnumber="https://bugs.llvm.org/show_bug.cgi?id=27806")
368350
def test_step_over_load(self):
369351
self.setSvr4Support(False)
370352
self.run_step_over_load()
371353

372354
@skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
373-
@skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently
374355
@expectedFailureAll(archs="aarch64", oslist="linux",
375356
bugnumber="https://bugs.llvm.org/show_bug.cgi?id=27806")
376357
def test_step_over_load_with_svr4(self):
@@ -408,7 +389,6 @@ def run_step_over_load(self):
408389
# executable dependencies are resolved relative to the debuggers PWD. Bug?
409390
@expectedFailureAll(oslist=["linux"], triple=no_match('aarch64-.*-android'))
410391
@skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
411-
@skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently
412392
@expectedFailureNetBSD
413393
def test_static_init_during_load(self):
414394
"""Test that we can set breakpoints correctly in static initializers"""
@@ -438,7 +418,7 @@ def test_static_init_during_load(self):
438418
'stop reason = breakpoint %d' % b_init_bp_num])
439419
self.expect("thread backtrace",
440420
substrs=['b_init',
441-
'dlopen',
421+
'dylib_open',
442422
'main'])
443423

444424
self.runCmd("continue")
@@ -448,5 +428,5 @@ def test_static_init_during_load(self):
448428
'stop reason = breakpoint %d' % a_init_bp_num])
449429
self.expect("thread backtrace",
450430
substrs=['a_init',
451-
'dlopen',
431+
'dylib_open',
452432
'main'])

lldb/test/API/functionalities/load_unload/a.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
extern int b_function ();
1+
extern LLDB_DYLIB_IMPORT int b_function();
22

33
int a_init()
44
{

lldb/test/API/functionalities/load_unload/b.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,4 @@ int b_init()
55

66
int b_global = b_init();
77

8-
int
9-
b_function ()
10-
{
11-
return 500;
12-
}
8+
int LLDB_DYLIB_EXPORT b_function() { return 500; }

lldb/test/API/functionalities/load_unload/d.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ int d_init()
55

66
int d_global = d_init();
77

8-
int
9-
d_function ()
10-
{ // Find this line number within d_dunction().
11-
return 700;
8+
int LLDB_DYLIB_EXPORT d_function() {
9+
return 700; // Find this line number within d_dunction().
1210
}

0 commit comments

Comments
 (0)