This is a small example on how to bind nasm and python together with the use of your own made (or aleady available) shared library. It's like always: what you can do in a programming language, you can do in assembly language too. (and even more ...) After some lookup on the internet I found a way to use shared libs in C with Python. Taking the previous quote in consideration (what you can do ....) I've translated it to assembly language. In time I have to figure out how to deal with strings and floating point numbers but I'm launched .... First create a shared library, let us call it pyfunctions.asm and implement a square function which take an integer and returns the square of it. Following the ABI for linux and x64 the integer is passed in rdi and the result is given back in rax.

;name: pyfunctions.asm
;
;build: release version:
;           nasm -felf64 -o pyfunctions.o pyfunctions.asm
;           ld -lc --dynamic-linker /lib64/ld-linux-x86-64.so.2 -shared -soname pyfunctions.so -o pyfunctions.so.1.0 pyfunctions.o -R .
;           ln -sf pyfunctions.so.1.0 pyfunctions.so
;       debug/development version
;           nasm -felf64 -Fdwarf -g -o sharedlib-dev.o sharedlib.asm
;           ld -lc --dynamic-linker /lib64/ld-linux-x86-64.so.2 -shared -soname sharedlib-dev.so -o sharedlib-dev.so.1.0 sharedlib-dev.o -R .
;           ln -sf sharedlib-dev.so.1.0 sharedlib-dev.so
;
;description: A simple shared library to use with python

bits 64

extern  _GLOBAL_OFFSET_TABLE_

;global functions
global  square:function

section .data
    
section .rodata

section .text
_start:
    
;a global function to get the version number returned in rax
square:
    push    rbp
    mov     rbp,rsp 
    push    rbx 
    call    .get_GOT 
.get_GOT: 
    pop     rbx 
    add     rbx,_GLOBAL_OFFSET_TABLE_+$$-.get_GOT wrt ..gotpc 
    ;two ways to get the external vaiable
    ;first if it's stored in the datasegment, but this is a long approach
    mov     rax,rdi
    mul     rdi
    mov     rbx,[rbp-8] 
    mov     rsp,rbp 
    pop     rbp 
    ret

Second, create a python module which will bind the library with python. I've called it nasmpyfunctions.py.

from ctypes import *
so_file = "./libpyfunctions.so"
functions = CDLL(so_file)

def square(x):
    return functions.square(x)

Next I've made a main.py script (call it whatever you want to) which imports the module and executes the square function which hopefully returns the square of the given integer and if not, something went wrong and I suggest to read this article again.

import nasmpyfunctions
def main():
    print(nasmpyfunctions(10))

main()

 

At last the makefile

.PHONY: all
.PHONY: clean
.PHONY: debug

LIBNAME=libpyfunctions
LIBS=-lc `pkg-config --libs gtk+-3.0`
VERSION=1.0.0
O = .o
SO = .so
LST = .lst
ASM = .asm
ASFLAGS= -felf64
LDFLAGS=-s -melf_x86_64
SONAME=$(LIBNAME)$(SO)

$(LIBNAME)$(SO) : $(LIBNAME)$(O) 
	ld $(LIBS) --dynamic-linker /lib64/ld-linux-x86-64.so.2 -shared -soname $(SONAME) -o $(LIBNAME)$(SO).$(VERSION) $(LIBNAME)$(O) -R .

$(LIBNAME)$(O) : $(LIBNAME)$(ASM)
	nasm $(ASFLAGS) -o $(LIBNAME)$(O) $(LIBNAME)$(ASM)
	
release:
	$(MAKE) $(LIBNAME)$(SO) ASFLAGS="-felf64" LDFLAGS="-melf_x86_64"
	ln -sf $(LIBNAME)$(SO).$(VERSION) $(LIBNAME)$(SO)
	ln -sf $(LIBNAME)$(SO).$(VERSION) $(LIBNAME)$(SO).1
	ln -sf $(LIBNAME)$(SO).$(VERSION) $(LIBNAME)$(SO).1.0

debug:
	$(MAKE) $(LIBNAME)$(SO) ASFLAGS="-felf64 -Fdwarf -g" LDFLAGS="-g -melf_x86_64"
	ln -sf $(LIBNAME)$(SO).$(VERSION) $(LIBNAME)$(SO)
	ln -sf $(LIBNAME)$(SO).$(VERSION) $(LIBNAME)$(SO).1
	ln -sf $(LIBNAME)$(SO).$(VERSION) $(LIBNAME)$(SO).1.0
	
clean:
	rm -f *$(O) $(LIBNAME).* *$(LST)