I am trying to make some experiments on distributed computing in Nim. As a first step, I would like to be able to invoke procedures by address. The idea should be that if the same executable is distributed on various machines, the address of functions in memory should be the same, and one could pass a message containing this address and the necessary arguments to ask other machines to execute a specific procedure.
I have followed this example to load and call functions dynamically from a DLL and it works. I have tried to change it in order to call a function in the current executable, using the no-argument version of loadLib like this
import dynlib
proc sayHi*() {.exportc.} =
echo "hi"
let
lib = loadLib()
address = lib.symAddr("sayHi")
echo "The address of sayHi is ", cast[int](address)
Unfortunately it does not seem to be able to locate sayHi and echoes 0.
Is there a way to call a function from the current executable given its name?
In addition, is it possible to get its address given the proc itself? Calling addr on a proc is not valid
I don't think it will work at all: https://en.wikipedia.org/wiki/Address_space_layout_randomization
What you need to do is to pass the name of the function as a string and create a name->proc mapping at program startup on your own (mapping["theFunc"] = theFunc). You can use a macro so that you just have to annotate the proc with e.g. .remoteAccess. This is a much cleaner solution anyway, it clearly communicates that something unusual is going on.
Thank you. I am aware of ASLR, but I think that it can be disabled programmatically anyway (at least on Linux) by a father process launching the actual executable. In any case, I was also investigating the approach via strings, and it should be easy to generate them with a macro.
The problem that I have is that I am not able to make either approach work. For the address approach, I am not sure how to get the address of a proc, while for the string approach I am not able to get the address of a symbol in the current executable - the example I give above fails to resolve sayHi.
Any ideas how to go on?
This works for me (Windows). Dunno if it works on Linux, I think it does.
import dynlib
proc sayHi*() {.exportc, dynlib.} =
echo "hi"
let
lib = loadLib()
address = lib.symAddr("sayHi")
echo "The address of sayHi is ", cast[int](address)
You are right, that my be the simplest approach, considering that I will have to use a macro anyway to store a function id into a hash table.
Anyway, I tried the example by Araq (I was missing a dinlyb pragma) on Ubuntu and it does not work for me - still reports address 0. Should I compile the executable with some particular flag? Just curious, I think I will go with the hashtable approach anyway
static:
var
count = 0
procMap = newTable[string, proc()]()
proc onNodeSend(n, procId: string) =
# send the actual request to node n
macro onNode(n: string, f: proc): untyped =
template inner(n, id) =
onNodeSend(n, id)
let fId = "func_" & $(count)
# procMap[fId] = f
count += 1
result = getAst(inner(n, fId))
to be used like this
onNode "2", proc() =
echo "Hello from node 2"
but I run into two issues.
Turns out the simplest solution here is to just call closures and rely on the address returned by f.rawProc (which is consistent across process, regardless of having ASLR activated - I think ASLR ony causes issues for DLLs).
The problem that I have now is to properly send a captured environment because I don't know how to get its size