수색…


소개

ctypes 는 원시 컴파일 된 라이브러리에서 내 보낸 함수를 호출하는 파이썬 내장 라이브러리입니다.

참고 : 이 라이브러리는 컴파일 된 코드를 처리하므로 상대적으로 OS 의존적입니다.

기본 사용법

libcntohl 함수를 사용하고자한다고 가정 해 봅시다.

먼저 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'

정확히 우리가 기대하는 바를 수행합니다.

일반적인 함정

파일을로드하지 못했습니다.

첫 x 째 가능한 오류로 라이브러리를로드 할 수 없습니다. 이 경우 일반적으로 OSError가 발생합니다.

파일이 없거나 (또는 ​​OS에서 찾을 수 없기 때문입니다.)

>>> 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 파일이 아닌 스크립트 파일입니다. 이것은 Linux 컴퓨터에서 .dll 파일을 열거 나 32 비트 파이썬 인터프리터에서 64 비트 파일을 열려고 할 때도 발생할 수 있습니다. 보시다시피,이 경우 오류가 좀 더 모호해지며 주변을 파다가 필요합니다.

함수에 액세스하지 못했습니다.

.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

기본 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'

ctypes 배열

좋은 C 프로그래머가 알고 있듯이, 단일 값으로는 그렇게까지 얻을 수 없습니다. 실제로 우리에게가는 것은 배열입니다!

>>> 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'

ctypes의 래핑 함수

경우에 따라 C 함수는 함수 포인터를 허용합니다. 열렬한 ctypes 사용자로서 우리는 이러한 함수를 사용하고 파이썬 함수를 인수로 전달하기를 원합니다.

함수를 정의 해 봅시다 :

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

이제이 함수는 두 개의 인수를 사용하여 같은 유형의 결과를 반환합니다. 예를 들어, type이 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)

복잡한 사용법

위의 모든 예제를 하나의 복잡한 시나리오 ( libclfind 함수 사용)로 결합합시다.

이 함수에 대한 자세한 내용은 맨 페이지를 참조하십시오 . 계속하기 전에 읽으시기 바랍니다.

먼저 적절한 프로토 타입을 정의합니다.

>>> 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

xyPOINTER(c_int) 이므로, 메모리에 저장된 값을 실제로 비교하기 위해서는 값을 POINTER(c_int) 역 참조 할 필요가 있습니다.

이제 우리는 모든 것을 함께 결합 할 수 있습니다.

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

ptr 은 리턴 된 void 포인터입니다. arr key 가 없으면 값은 None 이지만이 경우 유효한 값을 얻습니다.

이제 변환하고 값에 액세스 할 수 있습니다.

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

또한 ptr arr 내부의 올바른 값을 가리키는 것을 볼 수 있습니다.

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


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow