Lookup operates differently from the nominally tail-calling msgSend in that it cannot support transparent forwarding (where the receiver is switched to another object before dispatch.) Since objc_msgSend is given control over the final call, the destination can be swapped if necessary.
It's not possible[1] to implement objc_msgSend in plain (portable) C, as it needs to pass on an unknowable number of arguments without damaging the ones passed in registers, the stack frame, or the optional hidden return value pointer. Any methods attempting to do so using, say, naked functions and the method's type encoding will be more fragile than a perfect argument forwarding stub written in assembly.
[1]: __builtin_apply_args, __builtin_apply and __builtin_return (https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Constructing-Ca...) notwithstanding.