FAQ
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.

Search Discussions

Discussion Posts

Previous

Follow ups

Related Discussions

Discussion Navigation
viewthread | post
posts ‹ prev | 2 of 4 | next ›
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