Understanding SyscallN for windows when doing windows api call

97 views
Skip to first unread message

rudeus greyrat

unread,
Oct 25, 2024, 11:34:28 AM10/25/24
to golang-nuts
Hello,

In cpp (and other LLVM based languages), the windows API function address is taken from the DLL after it has been loaded in the Virtual Memory space (or at least something like that).

In go, everything is wrapped around that SyscallN function, which sadly I am not understanding how it works.

  1. Why is it called syscall ? I know Syscall in windows have a syscall number ...
  2. I feel (just a feeling) it is bypassing hook put in place by security solution (Bitdefender and other EDR) that hook Syscall after ntdll has been loaded in process memory
I found the definition here:
```
func syscall_SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) {
if len(args) > maxArgs {
panic("runtime: SyscallN has too many arguments")
}

// The cgocall parameters are stored in m instead of in
// the stack because the stack can move during fn if it
// calls back into Go.
c := &getg().m.winsyscall
c.fn = fn
c.n = uintptr(len(args))
if c.n != 0 {
c.args = uintptr(noescape(unsafe.Pointer(&args[0])))
}
cgocall(asmstdcallAddr, unsafe.Pointer(c))
// cgocall may reschedule us on to a different M,
// but it copies the return values into the new M's
// so we can read them from there.
c = &getg().m.winsyscall
return c.r1, c.r2, c.err
}
```

If I get the doc of getg I read:
```
 getg returns the pointer to the current g. The compiler rewrites calls to this function into instructions that fetch the g directly (from TLS or from the dedicated register). 
```

Does that mean all the address are written in the TLS allocated at the beginning of the program ?

Any more doc about what "g" and "m" stand for ?

I would be very thankfull if someone helps clarify this as I find it fascinating.
Thanks

Ian Lance Taylor

unread,
Oct 25, 2024, 2:43:42 PM10/25/24
to rudeus greyrat, golang-nuts
On Fri, Oct 25, 2024 at 8:34 AM rudeus greyrat
<rudeusqu...@gmail.com> wrote:
>
> In cpp (and other LLVM based languages), the windows API function address is taken from the DLL after it has been loaded in the Virtual Memory space (or at least something like that).
>
> In go, everything is wrapped around that SyscallN function, which sadly I am not understanding how it works.
>
> Why is it called syscall ? I know Syscall in windows have a syscall number ...

The name is just due to the fact that Go was originally implemented
for Unix systems. On Unix (and other) systems all calls into the
operating systems are implemented as system calls, or syscalls.
Windows works differently, but the Windows port just uses the same
name "syscall".


> I feel (just a feeling) it is bypassing hook put in place by security solution (Bitdefender and other EDR) that hook Syscall after ntdll has been loaded in process memory

It doesn't have anything to do with security solutions. It's because
we need to convert from the Go calling convention to the Windows
calling convention, and it's convenient to do that in one place.


> ```
> getg returns the pointer to the current g. The compiler rewrites calls to this function into instructions that fetch the g directly (from TLS or from the dedicated register).
> ```
>
> Does that mean all the address are written in the TLS allocated at the beginning of the program ?

No, just the g pointer itself. The g points to data stored in
ordinary heap memory.


> Any more doc about what "g" and "m" stand for ?

"g" stands for goroutine. "m" more or less stands for "machine
thread". Each m is associated with an operating system thread, which
on Windows is created using CreateThread. The goroutines are
multiplexed onto threads.

Ian
Reply all
Reply to author
Forward
0 new messages