ctypes

Введение

Примеры

  • 0

    Основное использование

    Допустим, мы хотим использовать libc «s ntohl функцию.

    Во- первых, мы должны загрузить libc.so :

     >>> from ctypes import *
    >>> libc = cdll.LoadLibrary('libc.so.6')
    >>> libc
    <CDLL 'libc.so.6', handle baadf00d at 0xdeadbeef>
    
     

    Затем мы получаем объект функции:

     >>> ntohl = libc.ntohl
    >>> ntohl
    <_FuncPtr object at 0xbaadf00d>
    
     

    И теперь мы можем просто вызвать функцию:

     >>> ntohl(0x6C)
    1811939328
    >>> hex(_)
    '0x6c000000'
    
     

    Что делает именно то, что мы ожидаем.

  • 0

    Общие подводные камни

    Не удалось загрузить файл

    Первая возможная ошибка - не удается загрузить библиотеку. В этом случае обычно возникает OSError.

    Это либо потому, что файл не существует (или не может быть найден в ОС):

     >>> cdll.LoadLibrary("foobar.so")
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/usr/lib/python3.5/ctypes/__init__.py", line 425, in LoadLibrary
        return self._dlltype(name)
    File "/usr/lib/python3.5/ctypes/__init__.py", line 347, in __init__
        self._handle = _dlopen(self._name, mode)
    OSError: foobar.so: cannot open shared object file: No such file or directory
    
     

    Как видите, ошибка ясна и довольно показательна.

    Вторая причина в том, что файл найден, но имеет неправильный формат.

     >>> cdll.LoadLibrary("libc.so")
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/usr/lib/python3.5/ctypes/__init__.py", line 425, in LoadLibrary
        return self._dlltype(name)
    File "/usr/lib/python3.5/ctypes/__init__.py", line 347, in __init__
        self._handle = _dlopen(self._name, mode)
    OSError: /usr/lib/i386-linux-gnu/libc.so: invalid ELF header
    
     

    В этом случае файл представляет собой файл сценария , а не .so файл. Это может также произойти при попытке открыть .dll - файл на компьютере Linux или 64 - битный файл на переводчике 32bit питона. Как вы можете видеть, в этом случае ошибка немного более расплывчата и требует некоторого изучения.

    Неспособность получить доступ к функции

    Предположим , что мы успешно загрузили .so файл, затем мы должны получить доступ к нашей функции , как мы сделали на первом примере.

    Когда несуществующая функция используются, AttributeError поднимаются:

     >>> libc.foo
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/usr/lib/python3.5/ctypes/__init__.py", line 360, in __getattr__
        func = self.__getitem__(name)
    File "/usr/lib/python3.5/ctypes/__init__.py", line 365, in __getitem__
        func = self._FuncPtr((name_or_ordinal, self))
    AttributeError: /lib/i386-linux-gnu/libc.so.6: undefined symbol: foo 
  • 0

    Базовый объект ctypes

    Самый основной объект - это int:

     >>> obj = ctypes.c_int(12)
    >>> obj
    c_long(12)
    
     

    Теперь, obj относится к кусок памяти , содержащей значение 12.

    К этому значению можно получить прямой доступ и даже изменить его:

     >>> obj.value
    12
    >>> obj.value = 13
    >>> obj
    c_long(13)
    
     

    Поскольку obj относится к кусок памяти, мы также можем узнать это размер и расположение:

     >>> sizeof(obj)
    4
    >>> hex(addressof(obj))
    '0xdeadbeef' 
  • 1

    массивы типов

    Как знает любой хороший программист на Си, одно значение не поможет вам так далеко. Что действительно заставит нас работать, так это массивы!

     >>> c_int * 16
    <class '__main__.c_long_Array_16'>
    
     

    Это не фактический массив, но это чертовски близко! Мы создали класс , который обозначает массив 16 int с.

    Теперь все, что нам нужно сделать, это инициализировать его:

     >>> arr = (c_int * 16)(*range(16))
    >>> arr
    <__main__.c_long_Array_16 object at 0xbaddcafe>
    
     

    Теперь arr является актуальной массив, содержащий числа от 0 до 15.

    К ним можно получить доступ, как и к любому списку:

     >>> arr[5]
    5
    >>> arr[5] = 20
    >>> arr[5]
    20
    
     

    И точно так же , как любые другие ctypes объекта, он также имеет размер и расположение:

     >>> sizeof(arr)
    64 # sizeof(c_int) * 16
    >>> hex(addressof(arr))
    '0xc000l0ff' 
  • 1

    Функции обтекания для ctypes

    В некоторых случаях функция C принимает указатель на функцию. Как алчный ctypes пользователи, мы хотели бы использовать эти функции, и даже передать функции питона в качестве аргументов.

    Давайте определим функцию:

     >>> def max(x, y):
            return x if x >= y else y
    
     

    Теперь эта функция принимает два аргумента и возвращает результат того же типа. Для примера давайте предположим, что тип - это int.

    Как и в примере с массивом, мы можем определить объект, обозначающий этот прототип:

     >>> CFUNCTYPE(c_int, c_int, c_int)
    <CFunctionType object at 0xdeadbeef>
    
     

    Это прототип обозначает функцию , которая возвращает c_int (первый аргумент), и принимает два c_int аргумента (другие аргументы).

    Теперь давайте обернем функцию:

     >>> CFUNCTYPE(c_int, c_int, c_int)(max)
    <CFunctionType object at 0xdeadbeef>
    
    
     

    Функциональные прототипы имеют на более частом использовании: они могут обернуть ctypes функции (например , libc.ntohl ) и убедитесь , что используется правильные аргументы при вызове функции.

     >>> libc.ntohl() # garbage in - garbage out
    >>> CFUNCTYPE(c_int, c_int)(libc.ntohl)()
    Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
    TypeError: this function takes at least 1 argument (0 given) 
  • 0

    Комплексное использование

    Давайте объединить все из примеров выше , в один сложный сценарий: с помощью libc «ы lfind функции.

    Для получения более подробной информации о функции, прочитать страницу человека . Я призываю вас прочитать это, прежде чем продолжить.

    Сначала мы определим правильные прототипы:

     >>> compar_proto = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
    >>> lfind_proto = CFUNCTYPE(c_void_p, c_void_p, c_void_p, POINTER(c_uint), c_uint, compar_proto)
    
     

    Затем давайте создадим переменные:

     >>> key = c_int(12)
    >>> arr = (c_int * 16)(*range(16))
    >>> nmemb = c_uint(16)
    
     

    И теперь мы определяем функцию сравнения:

     >>> def compar(x, y):
            return x.contents.value - y.contents.value
    
     

    Обратите внимание на то, что x и y являются POINTER(c_int) , так что мы должны разыменовать их и принимать их значения для того , чтобы реально сравнить значение , хранящееся в памяти.

    Теперь мы можем объединить все вместе:

     >>> lfind = lfind_proto(libc.lfind)
    >>> ptr = lfind(byref(key), byref(arr), byref(nmemb), sizeof(c_int), compar_proto(compar))
    
     

    ptr является возвращаемым недействительным указателем. Если key не был найден в arr , значение не будет None , но в данном случае мы получили действительное значение.

    Теперь мы можем преобразовать его и получить доступ к значению:

     >>> cast(ptr, POINTER(c_int)).contents
    c_long(12)
    
     

    Кроме того , мы можем видеть , что ptr указывает на правильное значение внутри arr :

     >>> addressof(arr) + 12 * sizeof(c_int) == ptr
    True 

Синтаксис

Параметры

Примечания