Skip to content

Commit cf39b5b

Browse files
committed
test: Add ctypes null dlsym case
Signed-off-by: Georgios Alexopoulos <[email protected]>
1 parent cd7d5d7 commit cf39b5b

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import os.path
2+
import sys
3+
import unittest
4+
from ctypes import CDLL
5+
6+
FOO_C = r"""
7+
#include <stdio.h>
8+
9+
/* This is a 'GNU indirect function' (IFUNC) that will be called by
10+
dlsym() to resolve the symbol "foo" to an address. Typically, such
11+
a function would return the address of an actual function, but it
12+
can also just return NULL. For some background on IFUNCs, see
13+
https://willnewton.name/uncategorized/using-gnu-indirect-functions/
14+
15+
Adapted from Michael Kerrisk's answer: https://stackoverflow.com/a/53590014
16+
*/
17+
18+
asm (".type foo, @gnu_indirect_function");
19+
20+
void *foo(void)
21+
{
22+
fprintf(stderr, "foo IFUNC called\n");
23+
return NULL;
24+
}
25+
"""
26+
27+
28+
@unittest.skipUnless(sys.platform.startswith('linux'),
29+
'Test only valid for Linux')
30+
class TestNullDlsym(unittest.TestCase):
31+
def test_null_dlsym(self):
32+
import subprocess
33+
import tempfile
34+
35+
try:
36+
p = subprocess.Popen(['gcc', '--version'], stdout=subprocess.PIPE,
37+
stderr=subprocess.DEVNULL)
38+
out, _ = p.communicate()
39+
except OSError:
40+
raise unittest.SkipTest('gcc, needed for test, not available')
41+
with tempfile.TemporaryDirectory() as d:
42+
# Create a source file foo.c, that uses
43+
# a GNU Indirect Function. See FOO_C.
44+
srcname = os.path.join(d, 'foo.c')
45+
libname = 'py_ctypes_test_null_dlsym'
46+
dstname = os.path.join(d, 'lib%s.so' % libname)
47+
with open(srcname, 'w') as f:
48+
f.write(FOO_C)
49+
self.assertTrue(os.path.exists(srcname))
50+
# Compile the file to a shared library
51+
cmd = ['gcc', '-fPIC', '-shared', '-o', dstname, srcname]
52+
out = subprocess.check_output(cmd)
53+
self.assertTrue(os.path.exists(dstname))
54+
# Load the shared library
55+
L = CDLL(dstname)
56+
57+
with self.assertRaises(AttributeError) as cm:
58+
# Try accessing the 'foo' symbol.
59+
# It should resolve via dlsym() to NULL,
60+
# and since we subjectively treat NULL
61+
# addresses as errors, we should get
62+
# an error.
63+
L.foo
64+
65+
self.assertEqual(str(cm.exception),
66+
"function 'foo' not found")
67+
68+
if __name__ == "__main__":
69+
unittest.main()

0 commit comments

Comments
 (0)