07-24-2023, 12:14 PM
In a bizarre turn of events, I've ended up in the following predicament where I'm using the following Python code to write the assembly generated by Numba to a file:
```python
@jit(nopython=True, nogil=True)
def six():
return 6
with open("six.asm", "w") as f:
for k, v in six.inspect_asm().items():
f.write(v)
```
The assembly code is successfully written to the file but I can't figure out how to execute it. I've tried the following:
```bash
$ as -o six.o six.asm
$ ld six.o -o six.bin
$ chmod +x six.bin
$ ./six.bin
```
However, the linking step fails with the following:
```
ld: warning: cannot find entry symbol _start; defaulting to 00000000004000f0
six.o: In function `cpython::__main__::six$241':
<string>:(.text+0x20): undefined reference to `PyArg_UnpackTuple'
<string>:(.text+0x47): undefined reference to `PyEval_SaveThread'
<string>:(.text+0x53): undefined reference to `PyEval_RestoreThread'
<string>:(.text+0x62): undefined reference to `PyLong_FromLongLong'
<string>:(.text+0x74): undefined reference to `PyExc_RuntimeError'
<string>:(.text+0x88): undefined reference to `PyErr_SetString'
```
I'm suspecting that the Numba and/or the Python standard library need to be dynamically linked against the generated object file for this to run successfully but I'm not sure how it can be done (if it can even be done in the first place).
I've also tried the following where I write the intermediate LLVM code to the file instead of the assembly:
```python
with open("six.ll", "w") as f:
for k, v in six.inspect_llvm().items():
f.write(v)
```
And then
```bash
$ lli six.ll
```
But this fails as well with the following error:
```
'main' function not found in module.
```
**UPDATE:**
It turns out that there exists a utility to find the relevant flags to pass to the ```ld``` command to dynamically link the Python standard library.
```bash
$ python3-config --ldflags
```
Returns
```
-L/Users/rayan/anaconda3/lib/python3.7/config-3.7m-darwin -lpython3.7m -ldl -framework CoreFoundation
```
Running the following again, this time with the correct flags:
```bash
$ as -o six.o six.asm
$ ld six.o -o six.bin -L/Users/rayan/anaconda3/lib/python3.7/config-3.7m-darwin -lpython3.7m -ldl -framework CoreFoundation
$ chmod +x six.bin
$ ./six.bin
```
I am now getting
```
ld: warning: No version-min specified on command line
ld: entry point (_main) undefined. for inferred architecture x86_64
```
I have tried adding a ```_main``` label in the assembly file but that doesn't seem to do anything. Any ideas on how to define the entry point?
**UPDATE 2:**
Here's the assembly code in case that's useful, it seems like the target function is the one with label ```_ZN8__main__7six$241E```:
```
.text
.file "<string>"
.globl _ZN8__main__7six$241E
.p2align 4, 0x90
.type _ZN8__main__7six$241E,@function
_ZN8__main__7six$241E:
movq $6, (%rdi)
xorl %eax, %eax
retq
.Lfunc_end0:
.size _ZN8__main__7six$241E, .Lfunc_end0-_ZN8__main__7six$241E
.globl _ZN7cpython8__main__7six$241E
.p2align 4, 0x90
.type _ZN7cpython8__main__7six$241E,@function
_ZN7cpython8__main__7six$241E:
.cfi_startproc
pushq %rax
.cfi_def_cfa_offset 16
movq %rsi, %rdi
movabsq $.const.six, %rsi
movabsq $PyArg_UnpackTuple, %r8
xorl %edx, %edx
xorl %ecx, %ecx
xorl %eax, %eax
callq *%r8
testl %eax, %eax
je .LBB1_3
movabsq $_ZN08NumbaEnv8__main__7six$241E, %rax
cmpq $0, (%rax)
je .LBB1_2
movabsq $PyEval_SaveThread, %rax
callq *%rax
movabsq $PyEval_RestoreThread, %rcx
movq %rax, %rdi
callq *%rcx
movabsq $PyLong_FromLongLong, %rax
movl $6, %edi
popq %rcx
.cfi_def_cfa_offset 8
jmpq *%rax
.LBB1_2:
.cfi_def_cfa_offset 16
movabsq $PyExc_RuntimeError, %rdi
movabsq $".const.missing Environment", %rsi
movabsq $PyErr_SetString, %rax
callq *%rax
.LBB1_3:
xorl %eax, %eax
popq %rcx
.cfi_def_cfa_offset 8
retq
.Lfunc_end1:
.size _ZN7cpython8__main__7six$241E, .Lfunc_end1-_ZN7cpython8__main__7six$241E
.cfi_endproc
.globl cfunc._ZN8__main__7six$241E
.p2align 4, 0x90
.type cfunc._ZN8__main__7six$241E,@function
cfunc._ZN8__main__7six$241E:
movl $6, %eax
retq
.Lfunc_end2:
.size cfunc._ZN8__main__7six$241E, .Lfunc_end2-cfunc._ZN8__main__7six$241E
.type _ZN08NumbaEnv8__main__7six$241E,@object
.comm _ZN08NumbaEnv8__main__7six$241E,8,8
.type .const.six,@object
.section .rodata,"a",@progbits
.const.six:
.asciz "six"
.size .const.six, 4
.type ".const.missing Environment",@object
.p2align 4
.const.missing Environment:
.asciz "missing Environment"
.size ".const.missing Environment", 20
.section ".note.GNU-stack","",@progbits
```
```python
@jit(nopython=True, nogil=True)
def six():
return 6
with open("six.asm", "w") as f:
for k, v in six.inspect_asm().items():
f.write(v)
```
The assembly code is successfully written to the file but I can't figure out how to execute it. I've tried the following:
```bash
$ as -o six.o six.asm
$ ld six.o -o six.bin
$ chmod +x six.bin
$ ./six.bin
```
However, the linking step fails with the following:
```
ld: warning: cannot find entry symbol _start; defaulting to 00000000004000f0
six.o: In function `cpython::__main__::six$241':
<string>:(.text+0x20): undefined reference to `PyArg_UnpackTuple'
<string>:(.text+0x47): undefined reference to `PyEval_SaveThread'
<string>:(.text+0x53): undefined reference to `PyEval_RestoreThread'
<string>:(.text+0x62): undefined reference to `PyLong_FromLongLong'
<string>:(.text+0x74): undefined reference to `PyExc_RuntimeError'
<string>:(.text+0x88): undefined reference to `PyErr_SetString'
```
I'm suspecting that the Numba and/or the Python standard library need to be dynamically linked against the generated object file for this to run successfully but I'm not sure how it can be done (if it can even be done in the first place).
I've also tried the following where I write the intermediate LLVM code to the file instead of the assembly:
```python
with open("six.ll", "w") as f:
for k, v in six.inspect_llvm().items():
f.write(v)
```
And then
```bash
$ lli six.ll
```
But this fails as well with the following error:
```
'main' function not found in module.
```
**UPDATE:**
It turns out that there exists a utility to find the relevant flags to pass to the ```ld``` command to dynamically link the Python standard library.
```bash
$ python3-config --ldflags
```
Returns
```
-L/Users/rayan/anaconda3/lib/python3.7/config-3.7m-darwin -lpython3.7m -ldl -framework CoreFoundation
```
Running the following again, this time with the correct flags:
```bash
$ as -o six.o six.asm
$ ld six.o -o six.bin -L/Users/rayan/anaconda3/lib/python3.7/config-3.7m-darwin -lpython3.7m -ldl -framework CoreFoundation
$ chmod +x six.bin
$ ./six.bin
```
I am now getting
```
ld: warning: No version-min specified on command line
ld: entry point (_main) undefined. for inferred architecture x86_64
```
I have tried adding a ```_main``` label in the assembly file but that doesn't seem to do anything. Any ideas on how to define the entry point?
**UPDATE 2:**
Here's the assembly code in case that's useful, it seems like the target function is the one with label ```_ZN8__main__7six$241E```:
```
.text
.file "<string>"
.globl _ZN8__main__7six$241E
.p2align 4, 0x90
.type _ZN8__main__7six$241E,@function
_ZN8__main__7six$241E:
movq $6, (%rdi)
xorl %eax, %eax
retq
.Lfunc_end0:
.size _ZN8__main__7six$241E, .Lfunc_end0-_ZN8__main__7six$241E
.globl _ZN7cpython8__main__7six$241E
.p2align 4, 0x90
.type _ZN7cpython8__main__7six$241E,@function
_ZN7cpython8__main__7six$241E:
.cfi_startproc
pushq %rax
.cfi_def_cfa_offset 16
movq %rsi, %rdi
movabsq $.const.six, %rsi
movabsq $PyArg_UnpackTuple, %r8
xorl %edx, %edx
xorl %ecx, %ecx
xorl %eax, %eax
callq *%r8
testl %eax, %eax
je .LBB1_3
movabsq $_ZN08NumbaEnv8__main__7six$241E, %rax
cmpq $0, (%rax)
je .LBB1_2
movabsq $PyEval_SaveThread, %rax
callq *%rax
movabsq $PyEval_RestoreThread, %rcx
movq %rax, %rdi
callq *%rcx
movabsq $PyLong_FromLongLong, %rax
movl $6, %edi
popq %rcx
.cfi_def_cfa_offset 8
jmpq *%rax
.LBB1_2:
.cfi_def_cfa_offset 16
movabsq $PyExc_RuntimeError, %rdi
movabsq $".const.missing Environment", %rsi
movabsq $PyErr_SetString, %rax
callq *%rax
.LBB1_3:
xorl %eax, %eax
popq %rcx
.cfi_def_cfa_offset 8
retq
.Lfunc_end1:
.size _ZN7cpython8__main__7six$241E, .Lfunc_end1-_ZN7cpython8__main__7six$241E
.cfi_endproc
.globl cfunc._ZN8__main__7six$241E
.p2align 4, 0x90
.type cfunc._ZN8__main__7six$241E,@function
cfunc._ZN8__main__7six$241E:
movl $6, %eax
retq
.Lfunc_end2:
.size cfunc._ZN8__main__7six$241E, .Lfunc_end2-cfunc._ZN8__main__7six$241E
.type _ZN08NumbaEnv8__main__7six$241E,@object
.comm _ZN08NumbaEnv8__main__7six$241E,8,8
.type .const.six,@object
.section .rodata,"a",@progbits
.const.six:
.asciz "six"
.size .const.six, 4
.type ".const.missing Environment",@object
.p2align 4
.const.missing Environment:
.asciz "missing Environment"
.size ".const.missing Environment", 20
.section ".note.GNU-stack","",@progbits
```