FAQ
I'm currently in the process of porting go to a new OS for 386/amd64
and, in doing so, find myself writing go code that needs to be called
from c code under various circumstances. In doing so, I've come
across an error that may or may not be a compiler bug depending on
whether or not this type of operation is supported.

To be clear, I'm using gc, not gccgo as my go compiler, and the c code
I'm referring to is NOT of the type that should be preprocessed by cgo
and compiled by gcc, but rather "go-compatible" c code compiled by
8c/6c (e.g. pkg/runtime/os_linux.c).

Glancing through the code base, I've found a few places where go
already gest called directly from c (most notably main·main() in
pkg/runtime/proc.c), but these calls appear to be far and few between.
  Since this sort of operation doesn't seem to be documented anywhere
and its use is not very ubiquitous throughout the code base, it's
actually not clear to me whether this practice is encouraged or even
supported at all.

That said, from what I can tell by looking at the x86 assembly
generated for go functions vs. 8c/6c functions, it seems that it
should be OK to just directly call go functions from c, so long as the
go function only returns a single value (or void), and I get the
function declaration right when I extern it in, e.g..
go: func go_func(n int32) int32 { return n }
c: extern int32 go_func(int32 n);

So long as I adhere to these guidelines, this has worked well for me
in almost every case I've encountered -- with one notable exception.
If I call a go function from within a c function and the c function
has the following properties, I get a page fault in my executing
program:

1) The c function does not take any arguments
2) The c function does not have any local variables
3) The c function makes a tail call to return the value of a go
function, rather than explicitly consuming the go functions return
value and then returning that,
i.e.
return pkg·func();
// rather than ...
n = pkg.func();
return n;

Unlike in C, go functions appear to pass their return values both on
the stack (I assume to accommodate support for multiple return values)
as well as in eax in the case of having a single return value (as is
the common practice in standard c). So long as some stack space has
been reserved for ANY purpose within the body of the c function, a
tail call to a go function will benignly overwrite some (now unusable)
local variable, and the return value will be propagated to its caller
through eax (just as in standard c). Assembly output for such a
scenario can be seen below:
<c_func>:
// Check if we need to grow our linked stack
mov %gs:0xffffffdc,%ecx
cmp (%ecx),%esp
ja <c_func+0x14>
xor %edi,%edi
xor %eax,%eax
call <runtime.morestack>
// Reserve some stack space for local operations
sub $0xc,%esp
// Do some local operations
// Make a tail call to return the value of go_func()
call <go_func>
// Restore esp to the value it had upon entering
add $0xc,%esp
ret

However, if the conditions listed above are satisfied, then assembly
such as the following ends up being generated (on 32-bit x86):
<c_func>:
mov %gs:0xffffffdc,%ecx
cmp (%ecx),%esp
ja <c_func+0x14>
xor %edi,%edi
xor %eax,%eax
call <runtime.morestack>
// No stack reservation
call <global_func_1>
call <global_func_2>
call <go_func>
// No stack restore
ret

Since this c function takes no arguments and has no local variables,
then no stack space is reserved that is able to consume the return
value from the tail call to the go function, and instead, the return
value of the c function residing at 0(%esp) gets overwritten by the go
function, and the c function returns to some garbage location.

A full example can be constructed as follows:
extern int32 go_pkg·go_func();
static int32 global_data = 10;
static int32 change_global() { global_data += 1; }
static int32 buggy_func() {
   change_global();
   change_global();
   change_global();
   return go_pkg·go_func();
}

Which generates the following assembly:
<buggy_func>:
mov %gs:0xffffffdc,%ecx
cmp (%ecx),%esp
ja <buggy_func+0x14>
xor %edi,%edi
xor %eax,%eax
call <runtime.morestack>
call <change_global>
call <change_global>
call <change_global>
call <go_pkg.go_func>
ret

............................................................................

As a side note, is there any particular reason that the preamble to
each function is of the form:
mov %gs:0xffffffdc,%ecx
cmp (%ecx),%esp
ja <func+0x14>
xor %edi,%edi
xor %eax,%eax
call <runtime.morestack>
// Actual function body at <func+0x14>
ret

Rather than:
mov %gs:0xffffffdc,%ecx
cmp (%ecx),%esp
jnbe <stack_stuff>
// Actual function body
ret
stack_stuff:
xor %edi,%edi
xor %eax,%eax
call <runtime.morestack>

The second one is slightly more efficient since (in the common case)
the linked stacks will not need to grow and a jump will be avoided
(saving 12 cycles per function call).

--
~Kevin

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Search Discussions

  • Kevin Klues at Jul 14, 2013 at 5:52 pm
    In my description, I obviously meant that the "the return ADDRESS of the c
    function residing at 0(%esp) gets overwritten by the go
    function, and the c function returns to some garbage location."

    Kevin
    On Sunday, July 14, 2013 9:36:28 AM UTC-7, Kevin Klues wrote:

    I'm currently in the process of porting go to a new OS for 386/amd64
    and, in doing so, find myself writing go code that needs to be called
    from c code under various circumstances. In doing so, I've come
    across an error that may or may not be a compiler bug depending on
    whether or not this type of operation is supported.

    To be clear, I'm using gc, not gccgo as my go compiler, and the c code
    I'm referring to is NOT of the type that should be preprocessed by cgo
    and compiled by gcc, but rather "go-compatible" c code compiled by
    8c/6c (e.g. pkg/runtime/os_linux.c).

    Glancing through the code base, I've found a few places where go
    already gest called directly from c (most notably main·main() in
    pkg/runtime/proc.c), but these calls appear to be far and few between.
    Since this sort of operation doesn't seem to be documented anywhere
    and its use is not very ubiquitous throughout the code base, it's
    actually not clear to me whether this practice is encouraged or even
    supported at all.

    That said, from what I can tell by looking at the x86 assembly
    generated for go functions vs. 8c/6c functions, it seems that it
    should be OK to just directly call go functions from c, so long as the
    go function only returns a single value (or void), and I get the
    function declaration right when I extern it in, e.g..
    go: func go_func(n int32) int32 { return n }
    c: extern int32 go_func(int32 n);

    So long as I adhere to these guidelines, this has worked well for me
    in almost every case I've encountered -- with one notable exception.
    If I call a go function from within a c function and the c function
    has the following properties, I get a page fault in my executing
    program:

    1) The c function does not take any arguments
    2) The c function does not have any local variables
    3) The c function makes a tail call to return the value of a go
    function, rather than explicitly consuming the go functions return
    value and then returning that,
    i.e.
    return pkg·func();
    // rather than ...
    n = pkg.func();
    return n;

    Unlike in C, go functions appear to pass their return values both on
    the stack (I assume to accommodate support for multiple return values)
    as well as in eax in the case of having a single return value (as is
    the common practice in standard c). So long as some stack space has
    been reserved for ANY purpose within the body of the c function, a
    tail call to a go function will benignly overwrite some (now unusable)
    local variable, and the return value will be propagated to its caller
    through eax (just as in standard c). Assembly output for such a
    scenario can be seen below:
    <c_func>:
    // Check if we need to grow our linked stack
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    ja <c_func+0x14>
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>
    // Reserve some stack space for local operations
    sub $0xc,%esp
    // Do some local operations
    // Make a tail call to return the value of go_func()
    call <go_func>
    // Restore esp to the value it had upon entering
    add $0xc,%esp
    ret

    However, if the conditions listed above are satisfied, then assembly
    such as the following ends up being generated (on 32-bit x86):
    <c_func>:
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    ja <c_func+0x14>
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>
    // No stack reservation
    call <global_func_1>
    call <global_func_2>
    call <go_func>
    // No stack restore
    ret

    Since this c function takes no arguments and has no local variables,
    then no stack space is reserved that is able to consume the return
    value from the tail call to the go function, and instead, the return
    value of the c function residing at 0(%esp) gets overwritten by the go
    function, and the c function returns to some garbage location.

    A full example can be constructed as follows:
    extern int32 go_pkg·go_func();
    static int32 global_data = 10;
    static int32 change_global() { global_data += 1; }
    static int32 buggy_func() {
    change_global();
    change_global();
    change_global();
    return go_pkg·go_func();
    }

    Which generates the following assembly:
    <buggy_func>:
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    ja <buggy_func+0x14>
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>
    call <change_global>
    call <change_global>
    call <change_global>
    call <go_pkg.go_func>
    ret

    ............................................................................


    As a side note, is there any particular reason that the preamble to
    each function is of the form:
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    ja <func+0x14>
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>
    // Actual function body at <func+0x14>
    ret

    Rather than:
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    jnbe <stack_stuff>
    // Actual function body
    ret
    stack_stuff:
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>

    The second one is slightly more efficient since (in the common case)
    the linked stacks will not need to grow and a jump will be avoided
    (saving 12 cycles per function call).

    --
    ~Kevin
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Minux at Jul 14, 2013 at 6:08 pm

    On Mon, Jul 15, 2013 at 12:36 AM, Kevin Klues wrote:

    I'm currently in the process of porting go to a new OS for 386/amd64
    and, in doing so, find myself writing go code that needs to be called
    just curious, what's the target OS?
    from c code under various circumstances. In doing so, I've come
    across an error that may or may not be a compiler bug depending on
    whether or not this type of operation is supported.

    Glancing through the code base, I've found a few places where go
    already gest called directly from c (most notably main·main() in
    pkg/runtime/proc.c), but these calls appear to be far and few between.
    Since this sort of operation doesn't seem to be documented anywhere
    and its use is not very ubiquitous throughout the code base, it's
    actually not clear to me whether this practice is encouraged or even
    supported at all.
    it's supported (in the sense that it's supported by the toolchain), 6c/8c
    code
    just can't get regular Go function return values, so you must define your
    C-callable Go function like this:
    func CCallableFunction(args uintptr /* or others, prefers pointer for
    anything larger
    than uintptr */, retval *uintptr) { /* blah blah */ }
    That said, from what I can tell by looking at the x86 assembly
    generated for go functions vs. 8c/6c functions, it seems that it
    should be OK to just directly call go functions from c, so long as the
    go function only returns a single value (or void), and I get the
    function declaration right when I extern it in, e.g..
    go: func go_func(n int32) int32 { return n }
    c: extern int32 go_func(int32 n);
    it won't always work, as C expects the return value in some register (AX
    for 386/amd64,
    R0 for ARM), Go always set return value on stack, so actually what you got
    for the
    n here is the last value that is stored into that register in the Go
    function, and it might
    not be the actual return value.
    So long as I adhere to these guidelines, this has worked well for me
    in almost every case I've encountered -- with one notable exception.
    If I call a go function from within a c function and the c function
    has the following properties, I get a page fault in my executing
    page fault? you mean SIGSEGV/SIGBUS?
    program:

    1) The c function does not take any arguments
    2) The c function does not have any local variables
    3) The c function makes a tail call to return the value of a go remember
    function, rather than explicitly consuming the go functions return
    value and then returning that,
    i.e.
    return pkg·func();
    // rather than ...
    n = pkg.func();
    return n;

    Unlike in C, go functions appear to pass their return values both on
    the stack (I assume to accommodate support for multiple return values)
    as well as in eax in the case of having a single return value (as is
    i think this is not guaranteed, it might just that the register allocator
    chooses
    to use AX to save the return value to stack.
    so to be safe, you should treat the return value as an argument in the C
    world,
    for example:
    // in Go
    func GoFunc(int) int
    // in C
    extern void GoFunc(intgo, intgo);

    as i said above, in C you can't treat the GoFunc as returning one intgo
    value.
    the common practice in standard c). So long as some stack space has
    been reserved for ANY purpose within the body of the c function, a
    tail call to a go function will benignly overwrite some (now unusable)
    local variable, and the return value will be propagated to its caller
    through eax (just as in standard c). Assembly output for such a
    scenario can be seen below:
    <c_func>:
    // Check if we need to grow our linked stack
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    ja <c_func+0x14>
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>
    // Reserve some stack space for local operations
    sub $0xc,%esp
    // Do some local operations
    // Make a tail call to return the value of go_func()
    call <go_func>
    // Restore esp to the value it had upon entering
    add $0xc,%esp
    ret

    However, if the conditions listed above are satisfied, then assembly
    such as the following ends up being generated (on 32-bit x86):
    <c_func>:
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    ja <c_func+0x14>
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>
    // No stack reservation
    call <global_func_1>
    call <global_func_2>
    call <go_func>
    // No stack restore
    ret

    Since this c function takes no arguments and has no local variables,
    then no stack space is reserved that is able to consume the return
    value from the tail call to the go function, and instead, the return
    value of the c function residing at 0(%esp) gets overwritten by the go
    function, and the c function returns to some garbage location.

    A full example can be constructed as follows:
    extern int32 go_pkg·go_func();
    static int32 global_data = 10;
    static int32 change_global() { global_data += 1; }
    static int32 buggy_func() {
    change_global();
    change_global();
    change_global();
    return go_pkg·go_func();
    }

    Which generates the following assembly:
    <buggy_func>:
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    ja <buggy_func+0x14>
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>
    call <change_global>
    call <change_global>
    call <change_global>
    call <go_pkg.go_func>
    ret


    ............................................................................

    As a side note, is there any particular reason that the preamble to
    each function is of the form:
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    ja <func+0x14>
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>
    // Actual function body at <func+0x14>
    ret

    Rather than:
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    jnbe <stack_stuff>
    // Actual function body
    ret
    stack_stuff:
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>

    The second one is slightly more efficient since (in the common case)
    the linked stacks will not need to grow and a jump will be avoided
    (saving 12 cycles per function call).
    have you measured the difference? i think it might not be that big since
    the pc difference is pretty small, and also that for the actual function
    body
    to execute, we must wait for the result for last cmp instruction, so i don't
    think there could be that big difference for inverting the comparison
    condition.

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Kevin Klues at Jul 15, 2013 at 2:11 am

    On Sun, Jul 14, 2013 at 11:08 AM, minux wrote:
    On Mon, Jul 15, 2013 at 12:36 AM, Kevin Klues wrote:

    I'm currently in the process of porting go to a new OS for 386/amd64
    and, in doing so, find myself writing go code that needs to be called
    just curious, what's the target OS?
    The OS is called Akaros. It's part of my Ph.D. work at Berkeley.
    http://akaros.cs.berkeley.edu
    from c code under various circumstances. In doing so, I've come
    across an error that may or may not be a compiler bug depending on
    whether or not this type of operation is supported.

    Glancing through the code base, I've found a few places where go
    already gest called directly from c (most notably main·main() in
    pkg/runtime/proc.c), but these calls appear to be far and few between.
    Since this sort of operation doesn't seem to be documented anywhere
    and its use is not very ubiquitous throughout the code base, it's
    actually not clear to me whether this practice is encouraged or even
    supported at all.
    it's supported (in the sense that it's supported by the toolchain), 6c/8c
    code
    just can't get regular Go function return values, so you must define your
    C-callable Go function like this:
    func CCallableFunction(args uintptr /* or others, prefers pointer for
    anything larger
    than uintptr */, retval *uintptr) { /* blah blah */ }
    I was originally doing things this way, but was hoping there was a way
    to get at the return values of existing go functions without modifying
    them. I guess I can always wrap them in a go function of a slightly
    different name and get at them that way.

    That said, from what I can tell by looking at the x86 assembly
    generated for go functions vs. 8c/6c functions, it seems that it
    should be OK to just directly call go functions from c, so long as the
    go function only returns a single value (or void), and I get the
    function declaration right when I extern it in, e.g..
    go: func go_func(n int32) int32 { return n }
    c: extern int32 go_func(int32 n);
    it won't always work, as C expects the return value in some register (AX for
    386/amd64,
    R0 for ARM), Go always set return value on stack, so actually what you got
    for the
    n here is the last value that is stored into that register in the Go
    function, and it might
    not be the actual return value.
    Yeah, I wasn't sure if it was just dumb luck that the return value was
    also in EAX or if that was done on purpose by the compiler so that c
    code could call it as if it was calling a regular c function.
    Probably safer to always pass it as a parameter.

    So long as I adhere to these guidelines, this has worked well for me
    in almost every case I've encountered -- with one notable exception.
    If I call a go function from within a c function and the c function
    has the following properties, I get a page fault in my executing
    page fault? you mean SIGSEGV/SIGBUS?
    In Akaros, we report it differently, but yeah, faulting on trying to
    access an unmapped address, e.g. NULL.
    program:

    1) The c function does not take any arguments
    2) The c function does not have any local variables
    3) The c function makes a tail call to return the value of a go remember
    function, rather than explicitly consuming the go functions return
    value and then returning that,
    i.e.
    return pkg·func();
    // rather than ...
    n = pkg.func();
    return n;

    Unlike in C, go functions appear to pass their return values both on
    the stack (I assume to accommodate support for multiple return values)
    as well as in eax in the case of having a single return value (as is
    i think this is not guaranteed, it might just that the register allocator
    chooses
    to use AX to save the return value to stack.
    so to be safe, you should treat the return value as an argument in the C
    world,
    for example:
    // in Go
    func GoFunc(int) int
    // in C
    extern void GoFunc(intgo, intgo);

    as i said above, in C you can't treat the GoFunc as returning one intgo
    value.
    the common practice in standard c). So long as some stack space has
    been reserved for ANY purpose within the body of the c function, a
    tail call to a go function will benignly overwrite some (now unusable)
    local variable, and the return value will be propagated to its caller
    through eax (just as in standard c). Assembly output for such a
    scenario can be seen below:
    <c_func>:
    // Check if we need to grow our linked stack
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    ja <c_func+0x14>
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>
    // Reserve some stack space for local operations
    sub $0xc,%esp
    // Do some local operations
    // Make a tail call to return the value of go_func()
    call <go_func>
    // Restore esp to the value it had upon entering
    add $0xc,%esp
    ret

    However, if the conditions listed above are satisfied, then assembly
    such as the following ends up being generated (on 32-bit x86):
    <c_func>:
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    ja <c_func+0x14>
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>
    // No stack reservation
    call <global_func_1>
    call <global_func_2>
    call <go_func>
    // No stack restore
    ret

    Since this c function takes no arguments and has no local variables,
    then no stack space is reserved that is able to consume the return
    value from the tail call to the go function, and instead, the return
    value of the c function residing at 0(%esp) gets overwritten by the go
    function, and the c function returns to some garbage location.

    A full example can be constructed as follows:
    extern int32 go_pkg·go_func();
    static int32 global_data = 10;
    static int32 change_global() { global_data += 1; }
    static int32 buggy_func() {
    change_global();
    change_global();
    change_global();
    return go_pkg·go_func();
    }

    Which generates the following assembly:
    <buggy_func>:
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    ja <buggy_func+0x14>
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>
    call <change_global>
    call <change_global>
    call <change_global>
    call <go_pkg.go_func>
    ret


    ............................................................................

    As a side note, is there any particular reason that the preamble to
    each function is of the form:
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    ja <func+0x14>
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>
    // Actual function body at <func+0x14>
    ret

    Rather than:
    mov %gs:0xffffffdc,%ecx
    cmp (%ecx),%esp
    jnbe <stack_stuff>
    // Actual function body
    ret
    stack_stuff:
    xor %edi,%edi
    xor %eax,%eax
    call <runtime.morestack>

    The second one is slightly more efficient since (in the common case)
    the linked stacks will not need to grow and a jump will be avoided
    (saving 12 cycles per function call).
    have you measured the difference? i think it might not be that big since
    the pc difference is pretty small, and also that for the actual function
    body
    to execute, we must wait for the result for last cmp instruction, so i don't
    think there could be that big difference for inverting the comparison
    condition.
    I haven't measured the difference, though intuitively it seems like it
    would be slower since a call to ja/jnbe take 16 cycles if a jump
    occurs and only 4 cycles if it does not. That said, I'm not an expert
    on the intricacies of branch prediction, out of order processing, etc.
    so it very well could be a negligible difference when these sort of
    cpu optimizations are taken into account.

    --
    ~Kevin

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedJul 14, '13 at 5:33p
activeJul 15, '13 at 2:11a
posts4
users2
websitegolang.org

2 users in discussion

Kevin Klues: 3 posts Minux: 1 post

People

Translate

site design / logo © 2022 Grokbase