Python Language
유형
수색…
소개
ctypes
는 원시 컴파일 된 라이브러리에서 내 보낸 함수를 호출하는 파이썬 내장 라이브러리입니다.
참고 : 이 라이브러리는 컴파일 된 코드를 처리하므로 상대적으로 OS 의존적입니다.
기본 사용법
libc
의 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'
정확히 우리가 기대하는 바를 수행합니다.
일반적인 함정
파일을로드하지 못했습니다.
첫 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)
복잡한 사용법
위의 모든 예제를 하나의 복잡한 시나리오 ( 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)
이므로, 메모리에 저장된 값을 실제로 비교하기 위해서는 값을 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