mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-03-06 05:33:28 +00:00
[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
This commit is contained in:
parent
51cf866163
commit
61e22cd85c
@ -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.
|
@ -14,23 +14,85 @@ from ctypes import cdll
|
|||||||
import ctypes.util
|
import ctypes.util
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'LLVMObject',
|
'c_object_p',
|
||||||
'find_library',
|
'find_library',
|
||||||
'get_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():
|
def find_library():
|
||||||
# FIXME should probably have build system define absolute path of shared
|
# FIXME should probably have build system define absolute path of shared
|
||||||
# library at install time.
|
# 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)
|
result = ctypes.util.find_library(lib)
|
||||||
if result:
|
if result:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# FIXME This is a local hack to ease development.
|
return None
|
||||||
return "/usr/local/llvm/lib/libLLVM-3.1svn.so"
|
|
||||||
|
|
||||||
def get_library():
|
def get_library():
|
||||||
"""Obtain a reference to the llvm library."""
|
"""Obtain a reference to the llvm library."""
|
||||||
|
@ -8,21 +8,21 @@
|
|||||||
#===------------------------------------------------------------------------===#
|
#===------------------------------------------------------------------------===#
|
||||||
|
|
||||||
from .common import LLVMObject
|
from .common import LLVMObject
|
||||||
|
from .common import c_object_p
|
||||||
from .common import get_library
|
from .common import get_library
|
||||||
|
|
||||||
from ctypes import POINTER
|
from ctypes import POINTER
|
||||||
from ctypes import byref
|
from ctypes import byref
|
||||||
from ctypes import c_char_p
|
from ctypes import c_char_p
|
||||||
from ctypes import c_void_p
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"lib",
|
"lib",
|
||||||
"MemoryBufferRef",
|
"MemoryBuffer",
|
||||||
]
|
]
|
||||||
|
|
||||||
lib = get_library()
|
lib = get_library()
|
||||||
|
|
||||||
class MemoryBuffer(object):
|
class MemoryBuffer(LLVMObject):
|
||||||
"""Represents an opaque memory buffer."""
|
"""Represents an opaque memory buffer."""
|
||||||
|
|
||||||
def __init__(self, filename=None):
|
def __init__(self, filename=None):
|
||||||
@ -34,7 +34,7 @@ class MemoryBuffer(object):
|
|||||||
if filename is None:
|
if filename is None:
|
||||||
raise Exception("filename argument must be defined")
|
raise Exception("filename argument must be defined")
|
||||||
|
|
||||||
memory = LLVMObject()
|
memory = c_object_p()
|
||||||
out = c_char_p(None)
|
out = c_char_p(None)
|
||||||
|
|
||||||
result = lib.LLVMCreateMemoryBufferWithContentsOfFile(filename,
|
result = lib.LLVMCreateMemoryBufferWithContentsOfFile(filename,
|
||||||
@ -43,26 +43,13 @@ class MemoryBuffer(object):
|
|||||||
if result:
|
if result:
|
||||||
raise Exception("Could not create memory buffer: %s" % out.value)
|
raise Exception("Could not create memory buffer: %s" % out.value)
|
||||||
|
|
||||||
self._memory = memory
|
LLVMObject.__init__(self, memory, disposer=lib.LLVMDisposeMemoryBuffer)
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def register_library(library):
|
def register_library(library):
|
||||||
library.LLVMCreateMemoryBufferWithContentsOfFile.argtypes = [c_char_p,
|
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.LLVMCreateMemoryBufferWithContentsOfFile.restype = bool
|
||||||
|
|
||||||
library.LLVMDisposeMemoryBuffer.argtypes = [c_void_p]
|
library.LLVMDisposeMemoryBuffer.argtypes = [MemoryBuffer]
|
||||||
|
|
||||||
register_library(lib)
|
register_library(lib)
|
||||||
|
@ -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_char_p
|
||||||
from ctypes import c_uint64
|
from ctypes import c_uint64
|
||||||
from ctypes import c_void_p
|
|
||||||
|
|
||||||
|
from .common import CachedProperty
|
||||||
from .common import LLVMObject
|
from .common import LLVMObject
|
||||||
|
from .common import c_object_p
|
||||||
from .common import get_library
|
from .common import get_library
|
||||||
from .core import MemoryBuffer
|
from .core import MemoryBuffer
|
||||||
|
|
||||||
@ -23,7 +94,7 @@ __all__ = [
|
|||||||
"Symbol",
|
"Symbol",
|
||||||
]
|
]
|
||||||
|
|
||||||
class ObjectFile(object):
|
class ObjectFile(LLVMObject):
|
||||||
"""Represents an object/binary file."""
|
"""Represents an object/binary file."""
|
||||||
|
|
||||||
def __init__(self, filename=None, contents=None):
|
def __init__(self, filename=None, contents=None):
|
||||||
@ -39,209 +110,413 @@ class ObjectFile(object):
|
|||||||
if filename is not None:
|
if filename is not None:
|
||||||
contents = MemoryBuffer(filename=filename)
|
contents = MemoryBuffer(filename=filename)
|
||||||
|
|
||||||
self._memory = contents
|
if contents is None:
|
||||||
self._obj = lib.LLVMCreateObjectFile(contents)
|
raise Exception('No input found.')
|
||||||
contents.release_ownership()
|
|
||||||
self._as_parameter_ = self._obj
|
|
||||||
|
|
||||||
def __del__(self):
|
ptr = lib.LLVMCreateObjectFile(contents)
|
||||||
lib.LLVMDisposeObjectFile(self)
|
LLVMObject.__init__(self, ptr, disposer=lib.LLVMDisposeObjectFile)
|
||||||
|
self.take_ownership(contents)
|
||||||
|
|
||||||
def from_param(self):
|
def get_sections(self, cache=False):
|
||||||
return self._as_parameter_
|
|
||||||
|
|
||||||
def get_sections(self):
|
|
||||||
"""Obtain the sections in this object file.
|
"""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.
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_symbols(self):
|
Sections are exposed as limited-use objects. See the module's
|
||||||
|
documentation on iterators for more.
|
||||||
|
"""
|
||||||
|
sections = lib.LLVMGetSections(self)
|
||||||
|
last = None
|
||||||
|
while True:
|
||||||
|
if lib.LLVMIsSectionIteratorAtEnd(self, sections):
|
||||||
|
break
|
||||||
|
|
||||||
|
last = Section(sections)
|
||||||
|
if cache:
|
||||||
|
last.cache()
|
||||||
|
|
||||||
|
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.
|
"""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.
|
||||||
"""
|
|
||||||
|
|
||||||
class Section(object):
|
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(LLVMObject):
|
||||||
"""Represents a section in an object file."""
|
"""Represents a section in an object file."""
|
||||||
|
|
||||||
def __init__(self, obj=None):
|
def __init__(self, ptr):
|
||||||
"""Construct a new section instance.
|
"""Construct a new section instance.
|
||||||
|
|
||||||
Section instances can currently only be created from an ObjectFile
|
Section instances can currently only be created from an ObjectFile
|
||||||
instance. Therefore, this constructor should not be used outside of
|
instance. Therefore, this constructor should not be used outside of
|
||||||
this module.
|
this module.
|
||||||
"""
|
"""
|
||||||
pass
|
LLVMObject.__init__(self, ptr)
|
||||||
|
|
||||||
def __del__(self):
|
self.expired = False
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@CachedProperty
|
||||||
def name(self):
|
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):
|
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):
|
def contents(self):
|
||||||
pass
|
if self.expired:
|
||||||
|
raise Exception('Section instance has expired.')
|
||||||
|
|
||||||
@property
|
return lib.LLVMGetSectionContents(self)
|
||||||
|
|
||||||
|
@CachedProperty
|
||||||
def address(self):
|
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):
|
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.')
|
||||||
|
|
||||||
def get_relocations(self):
|
assert isinstance(symbol, Symbol)
|
||||||
pass
|
return lib.LLVMGetSectionContainsSymbol(self, symbol)
|
||||||
|
|
||||||
class Symbol(object):
|
def get_relocations(self, cache=False):
|
||||||
def __init__(self):
|
"""Obtain the relocations in this Section.
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
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.')
|
||||||
|
|
||||||
|
relocations = lib.LLVMGetRelocations(self)
|
||||||
|
last = None
|
||||||
|
while True:
|
||||||
|
if lib.LLVMIsRelocationIteratorAtEnd(self, relocations):
|
||||||
|
break
|
||||||
|
|
||||||
|
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):
|
def name(self):
|
||||||
pass
|
"""The str name of the symbol.
|
||||||
|
|
||||||
@property
|
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)
|
||||||
|
|
||||||
|
@CachedProperty
|
||||||
def address(self):
|
def address(self):
|
||||||
pass
|
"""The address of this symbol, in long bytes."""
|
||||||
|
if self.expired:
|
||||||
|
raise Exception('Symbol instance has expired.')
|
||||||
|
|
||||||
@property
|
return lib.LLVMGetSymbolAddress(self)
|
||||||
|
|
||||||
|
@CachedProperty
|
||||||
def file_offset(self):
|
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):
|
def size(self):
|
||||||
pass
|
"""The size of the symbol, in long bytes."""
|
||||||
|
if self.expired:
|
||||||
|
raise Exception('Symbol instance has expired.')
|
||||||
|
|
||||||
class Relocation(object):
|
return lib.LLVMGetSymbolSize(self)
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@CachedProperty
|
||||||
|
def section(self):
|
||||||
|
"""The Section to which this Symbol belongs.
|
||||||
|
|
||||||
|
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):
|
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):
|
def offset(self):
|
||||||
pass
|
"""The offset of this relocation, in long bytes."""
|
||||||
|
if self.expired:
|
||||||
|
raise Exception('Relocation instance has expired.')
|
||||||
|
|
||||||
@property
|
return lib.LLVMGetRelocationOffset(self)
|
||||||
|
|
||||||
|
@CachedProperty
|
||||||
def symbol(self):
|
def symbol(self):
|
||||||
pass
|
"""The Symbol corresponding to this Relocation."""
|
||||||
|
if self.expired:
|
||||||
|
raise Exception('Relocation instance has expired.')
|
||||||
|
|
||||||
@property
|
ptr = lib.LLVMGetRelocationSymbol(self)
|
||||||
def type(self):
|
return Symbol(ptr)
|
||||||
pass
|
|
||||||
|
|
||||||
@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):
|
def type_name(self):
|
||||||
pass
|
"""The relocation type's name, as a str."""
|
||||||
|
if self.expired:
|
||||||
|
raise Exception('Relocation instance has expired.')
|
||||||
|
|
||||||
@property
|
return lib.LLVMGetRelocationTypeName(self)
|
||||||
|
|
||||||
|
@CachedProperty
|
||||||
def value_string(self):
|
def value_string(self):
|
||||||
pass
|
if self.expired:
|
||||||
|
raise Exception('Relocation instance has expired.')
|
||||||
|
|
||||||
SectionIteratorRef = c_void_p
|
return lib.LLVMGetRelocationValueString(self)
|
||||||
SymbolIteratorRef = c_void_p
|
|
||||||
RelocationIteratorRef = c_void_p
|
def expire(self):
|
||||||
|
"""Expire this instance, making future API accesses fail."""
|
||||||
|
self.expired = True
|
||||||
|
|
||||||
|
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):
|
def register_library(library):
|
||||||
"""Register function prototypes with LLVM library instance."""
|
"""Register function prototypes with LLVM library instance."""
|
||||||
|
|
||||||
# Object.h functions
|
# Object.h functions
|
||||||
library.LLVMCreateObjectFile.argtypes = [MemoryBuffer]
|
library.LLVMCreateObjectFile.argtypes = [MemoryBuffer]
|
||||||
library.LLVMCreateObjectFile.restype = LLVMObject
|
library.LLVMCreateObjectFile.restype = c_object_p
|
||||||
|
|
||||||
library.LLVMDisposeObjectFile.argtypes = [ObjectFile]
|
library.LLVMDisposeObjectFile.argtypes = [ObjectFile]
|
||||||
|
|
||||||
library.LLVMGetSections.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,
|
library.LLVMIsSectionIteratorAtEnd.argtypes = [ObjectFile, c_object_p]
|
||||||
SectionIteratorRef]
|
|
||||||
library.LLVMIsSectionIteratorAtEnd.restype = bool
|
library.LLVMIsSectionIteratorAtEnd.restype = bool
|
||||||
|
|
||||||
library.LLVMMoveToNextSection.argtypes = [SectionIteratorRef]
|
library.LLVMMoveToNextSection.argtypes = [c_object_p]
|
||||||
|
|
||||||
library.LLVMMoveToContainingSection.argtypes = [SectionIteratorRef,
|
library.LLVMMoveToContainingSection.argtypes = [c_object_p, c_object_p]
|
||||||
SymbolIteratorRef]
|
|
||||||
|
|
||||||
library.LLVMGetSymbols.argtypes = [ObjectFile]
|
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,
|
library.LLVMIsSymbolIteratorAtEnd.argtypes = [ObjectFile, c_object_p]
|
||||||
SymbolIteratorRef]
|
|
||||||
library.LLVMIsSymbolIteratorAtEnd.restype = bool
|
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.LLVMGetSectionName.restype = c_char_p
|
||||||
|
|
||||||
library.LLVMGetSectionSize.argtypes = [SectionIteratorRef]
|
library.LLVMGetSectionSize.argtypes = [c_object_p]
|
||||||
library.LLVMGetSectionSize.restype = c_uint64
|
library.LLVMGetSectionSize.restype = c_uint64
|
||||||
|
|
||||||
library.LLVMGetSectionContents.argtypes = [SectionIteratorRef]
|
library.LLVMGetSectionContents.argtypes = [c_object_p]
|
||||||
library.LLVMGetSectionContents.restype = c_char_p
|
library.LLVMGetSectionContents.restype = c_char_p
|
||||||
|
|
||||||
library.LLVMGetSectionAddress.argtypes = [SectionIteratorRef]
|
library.LLVMGetSectionAddress.argtypes = [c_object_p]
|
||||||
library.LLVMGetSectionAddress.restype = c_uint64
|
library.LLVMGetSectionAddress.restype = c_uint64
|
||||||
|
|
||||||
library.LLVMGetSectionContainsSymbol.argtypes = [SectionIteratorRef,
|
library.LLVMGetSectionContainsSymbol.argtypes = [c_object_p, c_object_p]
|
||||||
SymbolIteratorRef]
|
|
||||||
library.LLVMGetSectionContainsSymbol.restype = bool
|
library.LLVMGetSectionContainsSymbol.restype = bool
|
||||||
|
|
||||||
library.LLVMGetRelocations.argtypes = [SectionIteratorRef]
|
library.LLVMGetRelocations.argtypes = [c_object_p]
|
||||||
library.LLVMGetRelocations.restype = RelocationIteratorRef
|
library.LLVMGetRelocations.restype = c_object_p
|
||||||
|
|
||||||
library.LLVMDisposeRelocationIterator.argtypes = [RelocationIteratorRef]
|
library.LLVMDisposeRelocationIterator.argtypes = [c_object_p]
|
||||||
|
|
||||||
library.LLVMIsRelocationIteratorAtEnd.argtypes = [SectionIteratorRef,
|
library.LLVMIsRelocationIteratorAtEnd.argtypes = [c_object_p, c_object_p]
|
||||||
RelocationIteratorRef]
|
|
||||||
library.LLVMIsRelocationIteratorAtEnd.restype = bool
|
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.LLVMGetSymbolName.restype = c_char_p
|
||||||
|
|
||||||
library.LLVMGetSymbolAddress.argtypes = [SymbolIteratorRef]
|
library.LLVMGetSymbolAddress.argtypes = [Symbol]
|
||||||
library.LLVMGetSymbolAddress.restype = c_uint64
|
library.LLVMGetSymbolAddress.restype = c_uint64
|
||||||
|
|
||||||
library.LLVMGetSymbolFileOffset.argtypes = [SymbolIteratorRef]
|
library.LLVMGetSymbolFileOffset.argtypes = [Symbol]
|
||||||
library.LLVMGetSymbolFileOffset.restype = c_uint64
|
library.LLVMGetSymbolFileOffset.restype = c_uint64
|
||||||
|
|
||||||
library.LLVMGetSymbolSize.argtypes = [SymbolIteratorRef]
|
library.LLVMGetSymbolSize.argtypes = [Symbol]
|
||||||
library.LLVMGetSymbolSize.restype = c_uint64
|
library.LLVMGetSymbolSize.restype = c_uint64
|
||||||
|
|
||||||
library.LLVMGetRelocationAddress.argtypes = [SymbolIteratorRef]
|
library.LLVMGetRelocationAddress.argtypes = [c_object_p]
|
||||||
library.LLVMGetRelocationAddress.restype = c_uint64
|
library.LLVMGetRelocationAddress.restype = c_uint64
|
||||||
|
|
||||||
library.LLVMGetRelocationOffset.argtypes = [RelocationIteratorRef]
|
library.LLVMGetRelocationOffset.argtypes = [c_object_p]
|
||||||
library.LLVMGetRelocationOffset.restype = c_uint64
|
library.LLVMGetRelocationOffset.restype = c_uint64
|
||||||
|
|
||||||
library.LLVMGetRelocationSymbol.argtypes = [RelocationIteratorRef]
|
library.LLVMGetRelocationSymbol.argtypes = [c_object_p]
|
||||||
library.LLVMGetRelocationSymbol.restype = SymbolIteratorRef
|
library.LLVMGetRelocationSymbol.restype = c_object_p
|
||||||
|
|
||||||
library.LLVMGetRelocationType.argtypes = [RelocationIteratorRef]
|
library.LLVMGetRelocationType.argtypes = [c_object_p]
|
||||||
library.LLVMGetRelocationType.restype = c_uint64
|
library.LLVMGetRelocationType.restype = c_uint64
|
||||||
|
|
||||||
library.LLVMGetRelocationTypeName.argtypes = [RelocationIteratorRef]
|
library.LLVMGetRelocationTypeName.argtypes = [c_object_p]
|
||||||
library.LLVMGetRelocationTypeName.restype = c_char_p
|
library.LLVMGetRelocationTypeName.restype = c_char_p
|
||||||
|
|
||||||
library.LLVMGetRelocationValueString.argtypes = [RelocationIteratorRef]
|
library.LLVMGetRelocationValueString.argtypes = [c_object_p]
|
||||||
library.LLVMGetRelocationValueString.restype = c_char_p
|
library.LLVMGetRelocationValueString.restype = c_char_p
|
||||||
|
|
||||||
lib = get_library()
|
lib = get_library()
|
||||||
|
0
bindings/python/llvm/tests/__init__.py
Normal file
0
bindings/python/llvm/tests/__init__.py
Normal file
29
bindings/python/llvm/tests/base.py
Normal file
29
bindings/python/llvm/tests/base.py
Normal file
@ -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!')
|
9
bindings/python/llvm/tests/test_core.py
Normal file
9
bindings/python/llvm/tests/test_core.py
Normal file
@ -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)
|
67
bindings/python/llvm/tests/test_object.py
Normal file
67
bindings/python/llvm/tests/test_object.py
Normal file
@ -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)
|
@ -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)
|
|
@ -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)
|
|
Loading…
x
Reference in New Issue
Block a user