How function calls work depends on the calling convention. Most of the time, there will be a call stack in memory which can be used to pass function arguments etc. around. A calling convention will typically work like this:
- save dirty registers to the stack
- push function arguments to the stack
- call the function
- push the return address (next program counter) to the stack
- jump to the function address
Calling conventions may allow some arguments to be passed in registers, and may provide for a different order of the items on the stack.
The called function can then access the arguments on the stack, and use the stack to manage its own temporary data. When returning, the function will:
- clean up its own stack usage
- push the return value onto the stack
- jump to the return address that the caller wrote to the stack
So a function call only needs to manipulate memory of the stack, not of the function itself.
Many CPUs have security features that prevent the memory from being used freely. For example, the stack area might be writeable and readable but not executable: you can't call a function that is located on the stack. And the memory pages that contain executable code are marked as non-writeable. Here, the manual tells you that the memory of this function is also unreadable: you cannot load data from these pages, and the CPU only lets you jump to addresses in this protected region.
A compiler can create the necessary machine code for a function call without knowing its target. It just needs to know the function signature (number and types of arguments) and the calling convention. That is why functions must be declared first in C. The typedef describes this information.
The machine code can later be rewritten on the fly to point to the correct address of the function, this is called “linking”. Here, the target of the function call is known in advance, so the macro can provide the address of the function body's machine code.