aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Szorc <gregory.szorc@gmail.com>2012-03-10 04:41:24 +0000
committerGregory Szorc <gregory.szorc@gmail.com>2012-03-10 04:41:24 +0000
commit61e22cd85cd4c84fff391da67018c92bf21a8e19 (patch)
tree9f3f6b06d25b5735247ea51b665ed16a858cab3b
parent51cf8661637c114e4b4f178bd2677a6bb246be0d (diff)
[llvm.py] Implement interface to object files
It is now possible to load object files and scan over sections, symbols, and relocations! Includes test code with partial coverage. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@152482 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--bindings/python/README.txt67
-rw-r--r--bindings/python/llvm/common.py72
-rw-r--r--bindings/python/llvm/core.py27
-rw-r--r--bindings/python/llvm/object.py473
-rw-r--r--bindings/python/llvm/tests/__init__.py0
-rw-r--r--bindings/python/llvm/tests/base.py29
-rw-r--r--bindings/python/llvm/tests/test_core.py9
-rw-r--r--bindings/python/llvm/tests/test_object.py67
-rw-r--r--bindings/python/tests/test_core.py11
-rw-r--r--bindings/python/tests/test_object.py9
10 files changed, 620 insertions, 144 deletions
diff --git a/bindings/python/README.txt b/bindings/python/README.txt
index e69de29bb2..96e334319b 100644
--- a/bindings/python/README.txt
+++ b/bindings/python/README.txt
@@ -0,0 +1,67 @@
+This directory contains Python bindings for LLVM's C library.
+
+The bindings are currently a work in progress and are far from complete.
+Use at your own risk.
+
+Developer Info
+==============
+
+The single Python package is "llvm." Modules inside this package roughly
+follow the names of the modules/headers defined by LLVM's C API.
+
+Testing
+-------
+
+All test code is location in llvm/tests. Tests are written as classes
+which inherit from llvm.tests.base.TestBase, which is a convenience base
+class that provides common functionality.
+
+Tests can be executed by installing nose:
+
+ pip install nosetests
+
+Then by running nosetests:
+
+ nosetests
+
+To see more output:
+
+ nosetests -v
+
+To step into the Python debugger while running a test, add the following
+to your test at the point you wish to enter the debugger:
+
+ import pdb; pdb.set_trace()
+
+Then run nosetests:
+
+ nosetests -s -v
+
+You should strive for high code coverage. To see current coverage:
+
+ pip install coverage
+ nosetests --with-coverage --cover-html
+
+Then open cover/index.html in your browser of choice to see the code coverage.
+
+Style Convention
+----------------
+
+All code should pass PyFlakes. First, install PyFlakes:
+
+ pip install pyflakes
+
+Then at any time run it to see a report:
+
+ pyflakes .
+
+Eventually we'll provide a Pylint config file. In the meantime, install
+Pylint:
+
+ pip install pylint
+
+And run:
+
+ pylint llvm
+
+And try to keep the number of violations to a minimum.
diff --git a/bindings/python/llvm/common.py b/bindings/python/llvm/common.py
index fe35bf124f..9911e694ba 100644
--- a/bindings/python/llvm/common.py
+++ b/bindings/python/llvm/common.py
@@ -14,23 +14,85 @@ from ctypes import cdll
import ctypes.util
__all__ = [
- 'LLVMObject',
+ 'c_object_p',
'find_library',
'get_library',
]
-LLVMObject = POINTER(c_void_p)
+c_object_p = POINTER(c_void_p)
+
+class LLVMObject(object):
+ """Base class for objects that are backed by an LLVM data structure.
+
+ This class should never be instantiated outside of this package.
+ """
+ def __init__(self, ptr, ownable=True, disposer=None):
+ assert isinstance(ptr, c_object_p)
+
+ self._ptr = self._as_parameter_ = ptr
+
+ self._self_owned = True
+ self._ownable = ownable
+ self._disposer = disposer
+
+ self._owned_objects = []
+
+ def take_ownership(self, obj):
+ """Take ownership of another object.
+
+ When you take ownership of another object, you are responsible for
+ destroying that object. In addition, a reference to that object is
+ placed inside this object so the Python garbage collector will not
+ collect the object while it is still alive in libLLVM.
+
+ This method should likely only be called from within modules inside
+ this package.
+ """
+ assert isinstance(obj, LLVMObject)
+
+ self._owned_objects.append(obj)
+ obj._self_owned = False
+
+ def from_param(self):
+ """ctypes function that converts this object to a function parameter."""
+ return self._as_parameter_
+
+ def __del__(self):
+ if self._self_owned and self._disposer:
+ self._disposer(self)
+
+class CachedProperty(object):
+ """Decorator that caches the result of a property lookup.
+
+ This is a useful replacement for @property. It is recommended to use this
+ decorator on properties that invoke C API calls for which the result of the
+ call will be idempotent.
+ """
+ def __init__(self, wrapped):
+ self.wrapped = wrapped
+ try:
+ self.__doc__ = wrapped.__doc__
+ except: # pragma: no cover
+ pass
+
+ def __get__(self, instance, instance_type=None):
+ if instance is None:
+ return self
+
+ value = self.wrapped(instance)
+ setattr(instance, self.wrapped.__name__, value)
+
+ return value
def find_library():
# FIXME should probably have build system define absolute path of shared
# library at install time.
- for lib in ['LLVM-3.1svn', 'LLVM']:
+ for lib in ['LLVM-3.1svn', 'libLLVM-3.1svn', 'LLVM', 'libLLVM']:
result = ctypes.util.find_library(lib)
if result:
return result
- # FIXME This is a local hack to ease development.
- return "/usr/local/llvm/lib/libLLVM-3.1svn.so"
+ return None
def get_library():
"""Obtain a reference to the llvm library."""
diff --git a/bindings/python/llvm/core.py b/bindings/python/llvm/core.py
index bd9f8aaa2b..e0f35203ba 100644
--- a/bindings/python/llvm/core.py
+++ b/bindings/python/llvm/core.py
@@ -8,21 +8,21 @@
#===------------------------------------------------------------------------===#
from .common import LLVMObject
+from .common import c_object_p
from .common import get_library
from ctypes import POINTER
from ctypes import byref
from ctypes import c_char_p
-from ctypes import c_void_p
__all__ = [
"lib",
- "MemoryBufferRef",
+ "MemoryBuffer",
]
lib = get_library()
-class MemoryBuffer(object):
+class MemoryBuffer(LLVMObject):
"""Represents an opaque memory buffer."""
def __init__(self, filename=None):
@@ -34,7 +34,7 @@ class MemoryBuffer(object):
if filename is None:
raise Exception("filename argument must be defined")
- memory = LLVMObject()
+ memory = c_object_p()
out = c_char_p(None)
result = lib.LLVMCreateMemoryBufferWithContentsOfFile(filename,
@@ -43,26 +43,13 @@ class MemoryBuffer(object):
if result:
raise Exception("Could not create memory buffer: %s" % out.value)
- self._memory = memory
- self._as_parameter_ = self._memory
- self._owned = True
-
- def __del__(self):
- if self._owned:
- lib.LLVMDisposeMemoryBuffer(self._memory)
-
- def from_param(self):
- return self._as_parameter_
-
- def release_ownership(self):
- self._owned = False
-
+ LLVMObject.__init__(self, memory, disposer=lib.LLVMDisposeMemoryBuffer)
def register_library(library):
library.LLVMCreateMemoryBufferWithContentsOfFile.argtypes = [c_char_p,
- POINTER(LLVMObject), POINTER(c_char_p)]
+ POINTER(c_object_p), POINTER(c_char_p)]
library.LLVMCreateMemoryBufferWithContentsOfFile.restype = bool
- library.LLVMDisposeMemoryBuffer.argtypes = [c_void_p]
+ library.LLVMDisposeMemoryBuffer.argtypes = [MemoryBuffer]
register_library(lib)
diff --git a/bindings/python/llvm/object.py b/bindings/python/llvm/object.py
index f633f60953..473aa3a108 100644
--- a/bindings/python/llvm/object.py
+++ b/bindings/python/llvm/object.py
@@ -7,11 +7,82 @@
#
#===------------------------------------------------------------------------===#
+r"""
+Object File Interface
+=====================
+
+This module provides an interface for reading information from object files
+(e.g. binary executables and libraries).
+
+Using this module, you can obtain information about an object file's sections,
+symbols, and relocations. These are represented by the classes ObjectFile,
+Section, Symbol, and Relocation, respectively.
+
+Usage
+-----
+
+The only way to use this module is to start by creating an ObjectFile. You can
+create an ObjectFile by loading a file (specified by its path) or by creating a
+llvm.core.MemoryBuffer and loading that.
+
+Once you have an object file, you can inspect its sections and symbols directly
+by calling get_sections() and get_symbols() respectively. To inspect
+relocations, call get_relocations() on a Section instance.
+
+Iterator Interface
+------------------
+
+The LLVM bindings expose iteration over sections, symbols, and relocations in a
+way that only allows one instance to be operated on at a single time. This is
+slightly annoying from a Python perspective, as it isn't very Pythonic to have
+objects that "expire" but are still active from a dynamic language.
+
+To aid working around this limitation, each Section, Symbol, and Relocation
+instance caches its properties after first access. So, if the underlying
+iterator is advanced, the properties can still be obtained provided they have
+already been retrieved.
+
+In addition, we also provide a "cache" method on each class to cache all
+available data. You can call this on each obtained instance. Or, you can pass
+cache=True to the appropriate get_XXX() method to have this done for you.
+
+Here are some examples on how to perform iteration:
+
+ obj = ObjectFile(filename='/bin/ls')
+
+ # This is OK. Each Section is only accessed inside its own iteration slot.
+ section_names = []
+ for section in obj.get_sections():
+ section_names.append(section.name)
+
+ # This is NOT OK. You perform a lookup after the object has expired.
+ symbols = list(obj.get_symbols())
+ for symbol in symbols:
+ print symbol.name # This raises because the object has expired.
+
+ # In this example, we mix a working and failing scenario.
+ symbols = []
+ for symbol in obj.get_symbols():
+ symbols.append(symbol)
+ print symbol.name
+
+ for symbol in symbols:
+ print symbol.name # OK
+ print symbol.address # NOT OK. We didn't look up this property before.
+
+ # Cache everything up front.
+ symbols = list(obj.get_symbols(cache=True))
+ for symbol in symbols:
+ print symbol.name # OK
+
+"""
+
from ctypes import c_char_p
from ctypes import c_uint64
-from ctypes import c_void_p
+from .common import CachedProperty
from .common import LLVMObject
+from .common import c_object_p
from .common import get_library
from .core import MemoryBuffer
@@ -23,7 +94,7 @@ __all__ = [
"Symbol",
]
-class ObjectFile(object):
+class ObjectFile(LLVMObject):
"""Represents an object/binary file."""
def __init__(self, filename=None, contents=None):
@@ -39,209 +110,413 @@ class ObjectFile(object):
if filename is not None:
contents = MemoryBuffer(filename=filename)
- self._memory = contents
- self._obj = lib.LLVMCreateObjectFile(contents)
- contents.release_ownership()
- self._as_parameter_ = self._obj
-
- def __del__(self):
- lib.LLVMDisposeObjectFile(self)
+ if contents is None:
+ raise Exception('No input found.')
- def from_param(self):
- return self._as_parameter_
+ ptr = lib.LLVMCreateObjectFile(contents)
+ LLVMObject.__init__(self, ptr, disposer=lib.LLVMDisposeObjectFile)
+ self.take_ownership(contents)
- def get_sections(self):
+ def get_sections(self, cache=False):
"""Obtain the sections in this object file.
- This is an iterator for llvm.object.Section instances.
+ This is a generator for llvm.object.Section instances.
+
+ Sections are exposed as limited-use objects. See the module's
+ documentation on iterators for more.
"""
- pass
+ sections = lib.LLVMGetSections(self)
+ last = None
+ while True:
+ if lib.LLVMIsSectionIteratorAtEnd(self, sections):
+ break
+
+ last = Section(sections)
+ if cache:
+ last.cache()
- def get_symbols(self):
+ yield last
+
+ lib.LLVMMoveToNextSection(sections)
+ last.expire()
+
+ if last is not None:
+ last.expire()
+
+ lib.LLVMDisposeSectionIterator(sections)
+
+ def get_symbols(self, cache=False):
"""Obtain the symbols in this object file.
- This is an iterator for llvm.object.Symbol instances.
+ This is a generator for llvm.object.Symbol instances.
+
+ Each Symbol instance is a limited-use object. See this module's
+ documentation on iterators for more.
"""
+ symbols = lib.LLVMGetSymbols(self)
+ last = None
+ while True:
+ if lib.LLVMIsSymbolIteratorAtEnd(self, symbols):
+ break
+
+ last = Symbol(symbols, self)
+ if cache:
+ last.cache()
+
+ yield last
+
+ lib.LLVMMoveToNextSymbol(symbols)
+ last.expire()
+
+ if last is not None:
+ last.expire()
+
+ lib.LLVMDisposeSymbolIterator(symbols)
-class Section(object):
+class Section(LLVMObject):
"""Represents a section in an object file."""
- def __init__(self, obj=None):
+ def __init__(self, ptr):
"""Construct a new section instance.
Section instances can currently only be created from an ObjectFile
instance. Therefore, this constructor should not be used outside of
this module.
"""
- pass
+ LLVMObject.__init__(self, ptr)
- def __del__(self):
- pass
+ self.expired = False
- @property
+ @CachedProperty
def name(self):
- pass
+ """Obtain the string name of the section.
- @property
+ This is typically something like '.dynsym' or '.rodata'.
+ """
+ if self.expired:
+ raise Exception('Section instance has expired.')
+
+ return lib.LLVMGetSectionName(self)
+
+ @CachedProperty
def size(self):
- pass
+ """The size of the section, in long bytes."""
+ if self.expired:
+ raise Exception('Section instance has expired.')
- @property
+ return lib.LLVMGetSectionSize(self)
+
+ @CachedProperty
def contents(self):
- pass
+ if self.expired:
+ raise Exception('Section instance has expired.')
+
+ return lib.LLVMGetSectionContents(self)
- @property
+ @CachedProperty
def address(self):
- pass
+ """The address of this section, in long bytes."""
+ if self.expired:
+ raise Exception('Section instance has expired.')
+
+ return lib.LLVMGetSectionAddress(self)
- # TODO consider exposing more Pythonic interface, like __contains__
def has_symbol(self, symbol):
- pass
+ """Returns whether a Symbol instance is present in this Section."""
+ if self.expired:
+ raise Exception('Section instance has expired.')
+
+ assert isinstance(symbol, Symbol)
+ return lib.LLVMGetSectionContainsSymbol(self, symbol)
+
+ def get_relocations(self, cache=False):
+ """Obtain the relocations in this Section.
- def get_relocations(self):
- pass
+ This is a generator for llvm.object.Relocation instances.
+
+ Each instance is a limited used object. See this module's documentation
+ on iterators for more.
+ """
+ if self.expired:
+ raise Exception('Section instance has expired.')
-class Symbol(object):
- def __init__(self):
- pass
+ relocations = lib.LLVMGetRelocations(self)
+ last = None
+ while True:
+ if lib.LLVMIsRelocationIteratorAtEnd(self, relocations):
+ break
- @property
+ last = Relocation(relocations)
+ if cache:
+ last.cache()
+
+ yield last
+
+ lib.LLVMMoveToNextRelocation(relocations)
+ last.expire()
+
+ if last is not None:
+ last.expire()
+
+ lib.LLVMDisposeRelocationIterator(relocations)
+
+ def cache(self):
+ """Cache properties of this Section.
+
+ This can be called as a workaround to the single active Section
+ limitation. When called, the properties of the Section are fetched so
+ they are still available after the Section has been marked inactive.
+ """
+ getattr(self, 'name')
+ getattr(self, 'size')
+ getattr(self, 'contents')
+ getattr(self, 'address')
+
+ def expire(self):
+ """Expire the section.
+
+ This is called internally by the section iterator.
+ """
+ self.expired = True
+
+class Symbol(LLVMObject):
+ """Represents a symbol in an object file."""
+ def __init__(self, ptr, object_file):
+ assert isinstance(ptr, c_object_p)
+ assert isinstance(object_file, ObjectFile)
+
+ LLVMObject.__init__(self, ptr)
+
+ self.expired = False
+ self._object_file = object_file
+
+ @CachedProperty
def name(self):
- pass
+ """The str name of the symbol.
+
+ This is often a function or variable name. Keep in mind that name
+ mangling could be in effect.
+ """
+ if self.expired:
+ raise Exception('Symbol instance has expired.')
+
+ return lib.LLVMGetSymbolName(self)
- @property
+ @CachedProperty
def address(self):
- pass
+ """The address of this symbol, in long bytes."""
+ if self.expired:
+ raise Exception('Symbol instance has expired.')
+
+ return lib.LLVMGetSymbolAddress(self)
- @property
+ @CachedProperty
def file_offset(self):
- pass
+ """The offset of this symbol in the file, in long bytes."""
+ if self.expired:
+ raise Exception('Symbol instance has expired.')
- @property
+ return lib.LLVMGetSymbolFileOffset(self)
+
+ @CachedProperty
def size(self):
- pass
+ """The size of the symbol, in long bytes."""
+ if self.expired:
+ raise Exception('Symbol instance has expired.')
+
+ return lib.LLVMGetSymbolSize(self)
-class Relocation(object):
- def __init__(self):
- pass
+ @CachedProperty
+ def section(self):
+ """The Section to which this Symbol belongs.
- @property
+ The returned Section instance does not expire, unlike Sections that are
+ commonly obtained through iteration.
+
+ Because this obtains a new section iterator each time it is accessed,
+ calling this on a number of Symbol instances could be expensive.
+ """
+ sections = lib.LLVMGetSections(self._object_file)
+ lib.LLVMMoveToContainingSection(sections, self)
+
+ return Section(sections)
+
+ def cache(self):
+ """Cache all cacheable properties."""
+ getattr(self, 'name')
+ getattr(self, 'address')
+ getattr(self, 'file_offset')
+ getattr(self, 'size')
+
+ def expire(self):
+ """Mark the object as expired to prevent future API accesses.
+
+ This is called internally by this module and it is unlikely that
+ external callers have a legitimate reason for using it.
+ """
+ self.expired = True
+
+class Relocation(LLVMObject):
+ """Represents a relocation definition."""
+ def __init__(self, ptr):
+ """Create a new relocation instance.
+
+ Relocations are created from objects derived from Section instances.
+ Therefore, this constructor should not be called outside of this
+ module. See Section.get_relocations() for the proper method to obtain
+ a Relocation instance.
+ """
+ assert isinstance(ptr, c_object_p)
+
+ LLVMObject.__init__(self, ptr)
+
+ self.expired = False
+
+ @CachedProperty
def address(self):
- pass
+ """The address of this relocation, in long bytes."""
+ if self.expired:
+ raise Exception('Relocation instance has expired.')
- @property
+ return lib.LLVMGetRelocationAddress(self)
+
+ @CachedProperty
def offset(self):
- pass
+ """The offset of this relocation, in long bytes."""
+ if self.expired:
+ raise Exception('Relocation instance has expired.')
+
+ return lib.LLVMGetRelocationOffset(self)
- @property
+ @CachedProperty
def symbol(self):
- pass
+ """The Symbol corresponding to this Relocation."""
+ if self.expired:
+ raise Exception('Relocation instance has expired.')
- @property
- def type(self):
- pass
+ ptr = lib.LLVMGetRelocationSymbol(self)
+ return Symbol(ptr)
- @property
+ @CachedProperty
+ def type_number(self):
+ """The relocation type, as a long."""
+ if self.expired:
+ raise Exception('Relocation instance has expired.')
+
+ return lib.LLVMGetRelocationType(self)
+
+ @CachedProperty
def type_name(self):
- pass
+ """The relocation type's name, as a str."""
+ if self.expired:
+ raise Exception('Relocation instance has expired.')
+
+ return lib.LLVMGetRelocationTypeName(self)
- @property
+ @CachedProperty
def value_string(self):
- pass
+ if self.expired:
+ raise Exception('Relocation instance has expired.')
+
+ return lib.LLVMGetRelocationValueString(self)
+
+ def expire(self):
+ """Expire this instance, making future API accesses fail."""
+ self.expired = True
-SectionIteratorRef = c_void_p
-SymbolIteratorRef = c_void_p
-RelocationIteratorRef = c_void_p
+ def cache(self):
+ """Cache all cacheable properties on this instance."""
+ getattr(self, 'address')
+ getattr(self, 'offset')
+ getattr(self, 'symbol')
+ getattr(self, 'type')
+ getattr(self, 'type_name')
+ getattr(self, 'value_string')
def register_library(library):
"""Register function prototypes with LLVM library instance."""
# Object.h functions
library.LLVMCreateObjectFile.argtypes = [MemoryBuffer]
- library.LLVMCreateObjectFile.restype = LLVMObject
+ library.LLVMCreateObjectFile.restype = c_object_p
library.LLVMDisposeObjectFile.argtypes = [ObjectFile]
library.LLVMGetSections.argtypes = [ObjectFile]
- library.LLVMGetSections.restype = SectionIteratorRef
+ library.LLVMGetSections.restype = c_object_p
- library.LLVMDisposeSectionIterator.argtypes = [SectionIteratorRef]
+ library.LLVMDisposeSectionIterator.argtypes = [c_object_p]
- library.LLVMIsSectionIteratorAtEnd.argtypes = [ObjectFile,
- SectionIteratorRef]
+ library.LLVMIsSectionIteratorAtEnd.argtypes = [ObjectFile, c_object_p]
library.LLVMIsSectionIteratorAtEnd.restype = bool
- library.LLVMMoveToNextSection.argtypes = [SectionIteratorRef]
+ library.LLVMMoveToNextSection.argtypes = [c_object_p]
- library.LLVMMoveToContainingSection.argtypes = [SectionIteratorRef,
- SymbolIteratorRef]
+ library.LLVMMoveToContainingSection.argtypes = [c_object_p, c_object_p]
library.LLVMGetSymbols.argtypes = [ObjectFile]
- library.LLVMGetSymbols.restype = SymbolIteratorRef
+ library.LLVMGetSymbols.restype = c_object_p
- library.LLVMDisposeSymbolIterator.argtypes = [SymbolIteratorRef]
+ library.LLVMDisposeSymbolIterator.argtypes = [c_object_p]
- library.LLVMIsSymbolIteratorAtEnd.argtypes = [ObjectFile,
- SymbolIteratorRef]
+ library.LLVMIsSymbolIteratorAtEnd.argtypes = [ObjectFile, c_object_p]
library.LLVMIsSymbolIteratorAtEnd.restype = bool
- library.LLVMMoveToNextSymbol.argtypes = [SymbolIteratorRef]
+ library.LLVMMoveToNextSymbol.argtypes = [c_object_p]
- library.LLVMGetSectionName.argtypes = [SectionIteratorRef]
+ library.LLVMGetSectionName.argtypes = [c_object_p]
library.LLVMGetSectionName.restype = c_char_p
- library.LLVMGetSectionSize.argtypes = [SectionIteratorRef]
+ library.LLVMGetSectionSize.argtypes = [c_object_p]
library.LLVMGetSectionSize.restype = c_uint64
- library.LLVMGetSectionContents.argtypes = [SectionIteratorRef]
+ library.LLVMGetSectionContents.argtypes = [c_object_p]
library.LLVMGetSectionContents.restype = c_char_p
- library.LLVMGetSectionAddress.argtypes = [SectionIteratorRef]
+ library.LLVMGetSectionAddress.argtypes = [c_object_p]
library.LLVMGetSectionAddress.restype = c_uint64
- library.LLVMGetSectionContainsSymbol.argtypes = [SectionIteratorRef,
- SymbolIteratorRef]
+ library.LLVMGetSectionContainsSymbol.argtypes = [c_object_p, c_object_p]
library.LLVMGetSectionContainsSymbol.restype = bool
- library.LLVMGetRelocations.argtypes = [SectionIteratorRef]
- library.LLVMGetRelocations.restype = RelocationIteratorRef
+ library.LLVMGetRelocations.argtypes = [c_object_p]
+ library.LLVMGetRelocations.restype = c_object_p
- library.LLVMDisposeRelocationIterator.argtypes = [RelocationIteratorRef]
+ library.LLVMDisposeRelocationIterator.argtypes = [c_object_p]
- library.LLVMIsRelocationIteratorAtEnd.argtypes = [SectionIteratorRef,
- RelocationIteratorRef]
+ library.LLVMIsRelocationIteratorAtEnd.argtypes = [c_object_p, c_object_p]
library.LLVMIsRelocationIteratorAtEnd.restype = bool
- library.LLVMMoveToNextRelocation.argtypes = [RelocationIteratorRef]
+ library.LLVMMoveToNextRelocation.argtypes = [c_object_p]
- library.LLVMGetSymbolName.argtypes = [SymbolIteratorRef]
+ library.LLVMGetSymbolName.argtypes = [Symbol]
library.LLVMGetSymbolName.restype = c_char_p
- library.LLVMGetSymbolAddress.argtypes = [SymbolIteratorRef]
+ library.LLVMGetSymbolAddress.argtypes = [Symbol]
library.LLVMGetSymbolAddress.restype = c_uint64
- library.LLVMGetSymbolFileOffset.argtypes = [SymbolIteratorRef]
+ library.LLVMGetSymbolFileOffset.argtypes = [Symbol]
library.LLVMGetSymbolFileOffset.restype = c_uint64
- library.LLVMGetSymbolSize.argtypes = [SymbolIteratorRef]
+ library.LLVMGetSymbolSize.argtypes = [Symbol]
library.LLVMGetSymbolSize.restype = c_uint64
- library.LLVMGetRelocationAddress.argtypes = [SymbolIteratorRef]
+ library.LLVMGetRelocationAddress.argtypes = [c_object_p]
library.LLVMGetRelocationAddress.restype = c_uint64
- library.LLVMGetRelocationOffset.argtypes = [RelocationIteratorRef]
+ library.LLVMGetRelocationOffset.argtypes = [c_object_p]
library.LLVMGetRelocationOffset.restype = c_uint64
- library.LLVMGetRelocationSymbol.argtypes = [RelocationIteratorRef]
- library.LLVMGetRelocationSymbol.restype = SymbolIteratorRef
+ library.LLVMGetRelocationSymbol.argtypes = [c_object_p]
+ library.LLVMGetRelocationSymbol.restype = c_object_p
- library.LLVMGetRelocationType.argtypes = [RelocationIteratorRef]
+ library.LLVMGetRelocationType.argtypes = [c_object_p]
library.LLVMGetRelocationType.restype = c_uint64
- library.LLVMGetRelocationTypeName.argtypes = [RelocationIteratorRef]
+ library.LLVMGetRelocationTypeName.argtypes = [c_object_p]
library.LLVMGetRelocationTypeName.restype = c_char_p
- library.LLVMGetRelocationValueString.argtypes = [RelocationIteratorRef]
+ library.LLVMGetRelocationValueString.argtypes = [c_object_p]
library.LLVMGetRelocationValueString.restype = c_char_p
lib = get_library()
diff --git a/bindings/python/llvm/tests/__init__.py b/bindings/python/llvm/tests/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/bindings/python/llvm/tests/__init__.py
diff --git a/bindings/python/llvm/tests/base.py b/bindings/python/llvm/tests/base.py
new file mode 100644
index 0000000000..420423cd18
--- /dev/null
+++ b/bindings/python/llvm/tests/base.py
@@ -0,0 +1,29 @@
+import os.path
+import unittest
+
+POSSIBLE_TEST_BINARIES = [
+ 'libreadline.so.5',
+ 'libreadline.so.6',
+]
+
+POSSIBLE_TEST_BINARY_PATHS = [
+ '/lib',
+ '/usr/lib',
+ '/usr/local/lib',
+]
+
+class TestBase(unittest.TestCase):
+ def get_test_binary(self):
+ """Helper to obtain a test binary for object file testing.
+
+ FIXME Support additional, highly-likely targets or create one
+ ourselves.
+ """
+ for d in POSSIBLE_TEST_BINARY_PATHS:
+ for lib in POSSIBLE_TEST_BINARIES:
+ path = os.path.join(d, lib)
+
+ if os.path.exists(path):
+ return path
+
+ raise Exception('No suitable test binaries available!')
diff --git a/bindings/python/llvm/tests/test_core.py b/bindings/python/llvm/tests/test_core.py
new file mode 100644
index 0000000000..be3df66c26
--- /dev/null
+++ b/bindings/python/llvm/tests/test_core.py
@@ -0,0 +1,9 @@
+from llvm.core import MemoryBuffer
+
+from .base import TestBase
+
+class TestCore(TestBase):
+ def test_memory_buffer_create_from_file(self):
+ source = self.get_test_binary()
+
+ MemoryBuffer(filename=source)
diff --git a/bindings/python/llvm/tests/test_object.py b/bindings/python/llvm/tests/test_object.py
new file mode 100644
index 0000000000..7ff981b6a2
--- /dev/null
+++ b/bindings/python/llvm/tests/test_object.py
@@ -0,0 +1,67 @@
+from .base import TestBase
+from ..object import ObjectFile
+from ..object import Relocation
+from ..object import Section
+from ..object import Symbol
+
+class TestObjectFile(TestBase):
+ def get_object_file(self):
+ source = self.get_test_binary()
+ return ObjectFile(filename=source)
+
+ def test_create_from_file(self):
+ self.get_object_file()
+
+ def test_get_sections(self):
+ o = self.get_object_file()
+
+ count = 0
+ for section in o.get_sections():
+ count += 1
+ assert isinstance(section, Section)
+ assert isinstance(section.name, str)
+ assert isinstance(section.size, long)
+ assert isinstance(section.contents, str)
+ assert isinstance(section.address, long)
+
+ self.assertGreater(count, 0)
+
+ for section in o.get_sections():
+ section.cache()
+
+ def test_get_symbols(self):
+ o = self.get_object_file()
+
+ count = 0
+ for symbol in o.get_symbols():
+ count += 1
+ assert isinstance(symbol, Symbol)
+ assert isinstance(symbol.name, str)
+ assert isinstance(symbol.address, long)
+ assert isinstance(symbol.size, long)
+ assert isinstance(symbol.file_offset, long)
+
+ self.assertGreater(count, 0)
+
+ for symbol in o.get_symbols():
+ symbol.cache()
+
+ def test_symbol_section_accessor(self):
+ o = self.get_object_file()
+
+ for symbol in o.get_symbols():
+ section = symbol.section
+ assert isinstance(section, Section)
+
+ break
+
+ def test_get_relocations(self):
+ o = self.get_object_file()
+ for section in o.get_sections():
+ for relocation in section.get_relocations():
+ assert isinstance(relocation, Relocation)
+ assert isinstance(relocation.address, long)
+ assert isinstance(relocation.offset, long)
+ assert isinstance(relocation.type_number, long)
+ assert isinstance(relocation.type_name, str)
+ assert isinstance(relocation.value_string, str)
diff --git a/bindings/python/tests/test_core.py b/bindings/python/tests/test_core.py
deleted file mode 100644
index 8c4e933705..0000000000
--- a/bindings/python/tests/test_core.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from llvm.common import find_library
-from llvm.core import MemoryBuffer
-
-import unittest
-
-class TestCore(unittest.TestCase):
- def test_memory_buffer_create_from_file(self):
- source = find_library()
- self.assertIsNotNone(source)
-
- mb = MemoryBuffer(filename=source)
diff --git a/bindings/python/tests/test_object.py b/bindings/python/tests/test_object.py
deleted file mode 100644
index afb0201967..0000000000
--- a/bindings/python/tests/test_object.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from llvm.common import find_library
-from llvm.object import ObjectFile
-
-import unittest
-
-class TestObjectFile(unittest.TestCase):
- def test_create_from_file(self):
- source = find_library()
- of = ObjectFile(filename=source)