1

I'm trying to create simple bindings to the MD4C project but get a weird segmentation fault thrown when I attempt to call md_parse. I'm not too well versed in C so the code bellow describes my best attempt to tackle this issue. I acquired libmd4c.dylib by cloning and building the project with cmake according to their instructions:

brew install cmake
git clone https://github.com/mity/md4c.git
cd md4c
mkdir build
cd build
cmake ..
make

My best guess about what's wrong is the actual arguments I'm passing to the final call. Here is the full code:

import ctypes
import sys

lib = ctypes.CDLL('libmd4c.dylib')

def generic_cb(*args, **kwargs):

    print(args, kwargs)

def block_cb(code, detail, udata):

    generic_cb(code, details, udata)

    return 0

c_func_block_cb = ctypes.CFUNCTYPE(
    ctypes.c_int,
    ctypes.c_uint,
    ctypes.c_void_p,
    ctypes.c_void_p
)

c_func_block_cb_p = ctypes.POINTER(
    c_func_block_cb
)


c_func_text_cb = ctypes.CFUNCTYPE(
    ctypes.c_int,
    ctypes.c_wchar_p,
    ctypes.c_uint,
    ctypes.c_void_p
)

c_func_text_cb_p = ctypes.POINTER(
    c_func_text_cb
)


c_func_debug_log_cb = ctypes.CFUNCTYPE(
    ctypes.c_void_p,
    ctypes.c_char_p,
    ctypes.c_void_p,
)

c_func_debug_log_cb_p = ctypes.POINTER(
    c_func_debug_log_cb
)


c_func_syntax_cb = ctypes.CFUNCTYPE(
    ctypes.c_void_p,
    ctypes.c_void_p,
)

c_func_syntax_cb_p = ctypes.POINTER(
    c_func_syntax_cb
)


class Structure(ctypes.Structure):

    _fields_ = [
        (
            'abi_version',
            ctypes.c_uint
        ),
        (
            'flags',
            ctypes.c_uint
        ),

        (
            'enter_block',
            c_func_block_cb_p
        ),
        (
            'leave_block',
            c_func_block_cb_p
        ),
        (
            'enter_span',
            c_func_block_cb_p
        ),
        (
            'leave_span',
            c_func_block_cb_p
        ),

        (
            'text',
            c_func_text_cb_p
        ),
        (
            'debug_log',
            c_func_debug_log_cb_p
        ),
        (
            'syntax',
            c_func_syntax_cb_p
        )
    ]

StructurePointer = ctypes.POINTER(Structure)


c_block_cb = c_func_block_cb(block_cb)

c_block_cb_p = c_func_block_cb_p(c_block_cb)


c_text_cb = c_func_text_cb(generic_cb)

c_text_cb_p = c_func_text_cb_p(c_text_cb)


c_debug_log_cb = c_func_debug_log_cb(generic_cb)

c_debug_log_cb_p = c_func_debug_log_cb_p(c_debug_log_cb)


c_syntax_cb = c_func_syntax_cb(generic_cb)

c_syntax_cb_p = c_func_syntax_cb_p(c_syntax_cb)


struct = Structure(
    0,
    0,
    c_block_cb_p,
    c_block_cb_p,
    c_block_cb_p,
    c_block_cb_p,
    c_text_cb_p,
    c_debug_log_cb_p,
    None
)

value = '**hello**'

value_p = ctypes.c_wchar_p(value)

struct_p = StructurePointer(struct)

size = ctypes.c_uint(len(value))

func = lib.md_parse

func.argtypes = [
    ctypes.c_wchar_p,
    ctypes.c_uint,
    StructurePointer,
    ctypes.c_void_p
]

func.restype = ctypes.c_int

func.errcheck = print

udata = ctypes.c_int()

udata_p = ctypes.byref(udata)

result = lib.md_parse(value_p, size, struct_p, udata_p)

print(result)

Please let me know if you have any insights to share.

4
  • udata_p is a reference to a c_int, but func.argtypes earlier indicates it should be a StructurePointer. Without an minimal reproducible example that's all I see. Commented Jan 19, 2020 at 19:31
  • The code I provide is my full script @MarkTolonen, I added a few details about how I acquired the library right above and also updated the code with my newest attempt to get this to work, but to no avail. Commented Jan 19, 2020 at 19:36
  • Can you provide instructions on how to build this so others may be able to answer your question? Commented Jan 22, 2020 at 17:54
  • Added building instructions :) @S.S.Anne Commented Jan 22, 2020 at 18:24

1 Answer 1

1
+50

The following changes to the python code in this question prevent various segfaults and exceptions.

--- md_parse.py     2020-01-22 22:47:31.802934477 -0500
+++ md_parse.py     2020-01-22 23:56:50.874006725 -0500
@@ -7,7 +7,9 @@

     print(args, kwargs)

-def block_cb(code, detail, udata):
+    return 0
+
+def block_cb(code, details, udata):

     generic_cb(code, details, udata)

@@ -27,7 +29,7 @@

 c_func_text_cb = ctypes.CFUNCTYPE(
     ctypes.c_int,
-    ctypes.c_wchar_p,
+    ctypes.c_wchar,
     ctypes.c_uint,
     ctypes.c_void_p
 )
@@ -72,28 +74,28 @@

         (
             'enter_block',
-            c_func_block_cb_p
+            c_func_block_cb
         ),
         (
             'leave_block',
-            c_func_block_cb_p
+            c_func_block_cb
         ),
         (
             'enter_span',
-            c_func_block_cb_p
+            c_func_block_cb
         ),
         (
             'leave_span',
-            c_func_block_cb_p
+            c_func_block_cb
         ),

         (
             'text',
-            c_func_text_cb_p
+            c_func_text_cb
         ),
         (
             'debug_log',
-            c_func_debug_log_cb_p
+            c_func_debug_log_cb
         ),
         (
             'syntax',
@@ -127,12 +129,12 @@
struct = Structure(
     0,
     0,
-    c_block_cb_p,
-    c_block_cb_p,
-    c_block_cb_p,
-    c_block_cb_p,
-    c_text_cb_p,
-    c_debug_log_cb_p,
+    c_block_cb,
+    c_block_cb,
+    c_block_cb,
+    c_block_cb,
+    c_text_cb,
+    c_debug_log_cb,
     None
 )

The first patch chunk fixes a discrepancy in the block_cb argument list, and fixes generic_cb to return the expected type (omitting the changes in the first chunk will reveal some exceptions hidden behind the segfaults).

The other changes in the patch prevent a couple of different segfaults, and amount to not using pointer types in various places. There is some dead code left behind by those changes, but removing that dead code would make the patch bigger, with no relevance to the segfaults which are the focus of this question.

I performed my testing on a Debian system, but I wouldn't expect the results to vary materially on other platforms.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.