pylink icon indicating copy to clipboard operation
pylink copied to clipboard

JLink instantiation looking for DLL on linux platform

Open swohlerLL opened this issue 7 years ago • 14 comments

After installing JLink tools v6.32d for ARM on Raspbian, I am able to access the device using command line tools. Using python 2.7.9 under a virtual environment and ipython session, pylink cannot create a JLink object:

Python 2.7.9 (default, Sep 17 2016, 20:26:04)
Type "copyright", "credits" or "license" for more information.

IPython 5.7.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import pylink

In [2]: jlink = pylink.JLink()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-2e2659994c13> in <module>()
----> 1 jlink = pylink.JLink()

/home/pi/jlink/JLink_Linux_V632d_arm/venv/local/lib/python2.7/site-packages/pylink/jlink.pyc in __init__(self, lib, log, detailed_log, error, warn, unsecure_hook)
    250
    251         if lib.dll() is None:
--> 252             raise TypeError('Expected to be given a valid DLL.')
    253
    254         self._library = lib

TypeError: Expected to be given a valid DLL.

I have already copied the libjlinkarm.so file to /usr/local/lib, as well as creating a symlink in the same directory to libjlinkarm.so.6.32.4 from the JLink installation.

I also tried exporting the library path: export LD_LIBRARY_PATH=~/jlink/JLink_Linux_V632d_arm/:$LD_LIBRARY_PATH

swohlerLL avatar May 30 '18 16:05 swohlerLL

Is this Linux for ARM? What does sys.platform report, and what's the name of the DLL? On Linux platforms, the logic first searches for libjlinkarm.so (no version in name) using ctypes.util.find_library, then using a platform-specific searching method as defined here: https://github.com/square/pylink/blob/master/pylink/library.py#L264

hkpeprah avatar May 30 '18 17:05 hkpeprah

Yes, linux for ARM:

In [3]: import sys

In [4]: sys.platform
Out[4]: 'linux2'

There is no DLL.

After the previous issue I confirmed I can pass in the library:

In [1]: import pylink

In [2]: lib = pylink.library.Library('./libjlinkarm.so')

In [3]: jlink = pylink.JLink(lib)

swohlerLL avatar May 30 '18 19:05 swohlerLL

Does ctypes.util.find_library('libjlinkarm.so') work when you export the LD_LIBRARY_PATH`?

hkpeprah avatar May 30 '18 19:05 hkpeprah

Doesn't appear to:

In [1]: import ctypes

In [2]: ctypes.util.find_library('libjlinkarm.so')

In [3]: ls
99-jlink.rules  GDBServer/        JLinkExe*             JLinkLicenseManager@  JLinkRemoteServerCLExe*  JLinkSTM32*      libjlinkarm.so@         README.txt                     Samples/
Devices/        JFlashSPI_CL*     JLinkGDBServer@       JLinkRegistration@    JLinkRTTClient*          JLinkSWOViewer*  libjlinkarm.so.6@       RTT__201805301206.log          venv/
Doc/            JLinkDevices.xml  JLinkGDBServerCLExe*  JLinkRemoteServer@    JLinkRTTLogger*          JTAGLoadExe*     libjlinkarm.so.6.32.4*  RTT_Terminal_201805301207.log
(venv) pi@raspberrypi:~/jlink/JLink_Linux_V632d_arm $ echo $LD_LIBRARY_PATH
/home/pi/jlink/JLink_Linux_V632d_arm:

swohlerLL avatar May 30 '18 19:05 swohlerLL

Could this have to do with the version of Python you're using? 64-bit vs 32-bit?

hkpeprah avatar May 30 '18 19:05 hkpeprah

I had this same problem and managed to fix it. Pylink doesn't correctly find installed J-Link libraries on Linux. There are two bugs in Pylink, as follows:

  1. The code in library.py searches for "libjlinkarm.so" on Linux platforms. However, when searching for a library on Linux using the ctypes.util.find_library function, the 'lib' prefix must be omitted. For example:
Python 2.7.13 (default, Nov 24 2017, 17:33:09) 
[GCC 6.3.0 20170516] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from ctypes.util import find_library
>>> find_library('libm.so')
>>> find_library('m.so')
'libm.so.6'
>>> find_library('libjlinkarm.so')
>>> find_library('jlinkarm.so')
'libjlinkarm.so.6'
>>>
  1. The string returned from find_library does not contain the full path to the shared library in Linux. This breaks the code in library.load that attempts to copy the shared library to a temporary file. I have no idea why the shared library is being copied to a temporary file, but removing this operation and calling ctypes.cdll.LoadLibrary on the value returned from ctypes.util.find_library just works.

dpalchak avatar Jul 02 '18 17:07 dpalchak

Re: the second thing. Up until a certain version, the DLL inherently prevented accessing two or more J-Links at the same time. Copying to a temporary file provided a work-around for that.

hkpeprah avatar Jul 02 '18 17:07 hkpeprah

Here's a patch with changes that fix things for Linux. Note that this patch completely removes the entire "temporary DLL copy", which based on the last comment may cause problems when accessing multiple J-Link simultaneously.

diff --git a/pylink/library.py b/pylink/library.py
index 68d7efd..239f80a 100644
--- a/pylink/library.py
+++ b/pylink/library.py
@@ -82,7 +82,7 @@ class Library(object):
         'JLINK_SetFlashProgProgressCallback'
     ]
 
-    JLINK_SDK_NAME = 'libjlinkarm'
+    JLINK_SDK_NAME = 'jlinkarm'
 
     WINDOWS_JLINK_SDK_NAME = 'JLinkARM'
 
@@ -315,20 +315,8 @@ class Library(object):
         else:
             suffix = '.so'
 
-        # Copy the J-Link DLL to a temporary file.  This will be cleaned up the
-        # next time we load a DLL using this library or if this library is
-        # cleaned up.
-        tf = tempfile.NamedTemporaryFile(delete=False, suffix=suffix)
-        with open(tf.name, 'wb') as outputfile:
-            with open(self._path, 'rb') as inputfile:
-                outputfile.write(inputfile.read())
-
-        # This is needed to work around a WindowsError where the file is not
-        # being properly cleaned up after exiting the with statement.
-        tf.close()
-
-        self._temp = tf
-        self._lib = ctypes.cdll.LoadLibrary(tf.name)
+        self._temp = None
+        self._lib = ctypes.cdll.LoadLibrary(self._path)
 
         if self._windows:
             # The J-Link library uses a mix of __cdecl and __stdcall function

dpalchak avatar Jul 02 '18 17:07 dpalchak

+1 worked for me on arch linux with JLink V6.40 - thank you @dpalchak

markfink avatar Dec 22 '18 16:12 markfink

The fix above works for Linux but would break other OS, do I understand that correctly? Otherwise, is there something else that hinders creating a PR for that?

fleutot avatar May 08 '19 16:05 fleutot

The rename would probably break OSX, and the copy workaround would break multiple device access.

hkpeprah avatar May 08 '19 17:05 hkpeprah

A solution that doesn't require any modification to the code:

jlink = pylink.JLink(lib=pylink.library.Library(dllpath='/path/to/libjlinkarm.so'), ...)

rdaforno avatar Jun 02 '20 08:06 rdaforno

jlink = pylink.JLink(lib=pylink.library.Library

This worked for me with no code changes on a raspberry pi 4

Thomas-Haley-Bose avatar Jun 11 '20 16:06 Thomas-Haley-Bose

I know this is two years old, but we might have a concrete fix for this as outlined in #132, which removes the need for the workaround; this should be available in v0.14.0.

hkpeprah avatar Jul 20 '22 21:07 hkpeprah