Compiling with CGO against Apache's httpd.h

383 views
Skip to first unread message

philne...@gmail.com

unread,
Jan 9, 2015, 10:09:38 AM1/9/15
to golan...@googlegroups.com
I'm trying to construct a Go interface to the Apache module system. I.e. I want to be able to call Go functions from my Apache module. This is my Go file:

    package main

    // #cgo CFLAGS: -I/usr/include/apache2 -I/usr/include/apr-1.0
    // #include <httpd.h>
    import "C"

    //export handler
    func handler(r *C.request_rec) int {
        ap_set_content_type(r, "text/html");
        ap_rprintf(r, "Hello, world from Go")
        return OK;
    }

When I build it (`go build mod_frugal.go`) I get this error:

    # command-line-arguments
    ./mod_frugal.go:8:17: type conversion loop at volatile *struct apr_bucket

Line 8 is the line that function "handler" begins. Any suggestions?

Alternatively, when I build with cgo (`go tool cgo mod_frugal.go`) it doesn't see my include directives at all:

    /home/vagrant/mod_frugal/src/mod_frugal.go:4:20: fatal error: httpd.h: No such file or directory
    // #include <httpd.h>
                    ^
    compilation terminated.

I'm using "go version go1.2.1 linux/amd64".

safestyl...@gmail.com

unread,
Jan 9, 2015, 12:12:59 PM1/9/15
to golan...@googlegroups.com, philne...@gmail.com
" I want to be able to call Go functions from my Apache module"

Is that possible with CGO? I always though it was for Go to call C, not the other way round. Let me know if you do get this working! I'd love to link Go with Apache!

philne...@gmail.com

unread,
Jan 9, 2015, 12:39:20 PM1/9/15
to golan...@googlegroups.com, philne...@gmail.com
One other thing to note... when I prefix the two Apache calls with "C.":

package main

// #cgo CFLAGS: -I/usr/include/apache2 -I/usr/include/apr-1.0
// #include "mod_frugal.h"
import "C"

//export handler
func handler(r *C.struct_request_rec) int {
    C.ap_set_content_type(r, "text/html")
    C.ap_rprintf(r, "<h1>Hello, world from Go!</h1>")
    return OK
}

the original error goes away and I get this one:

# _/home/vagrant/mod_frugal/src
37: error: 'ap_set_content_type' undeclared (first use in this function)
37: error: 'ap_rprintf' undeclared (first use in this function)

philne...@gmail.com

unread,
Jan 9, 2015, 2:49:58 PM1/9/15
to golan...@googlegroups.com, philne...@gmail.com
Updated my Go file like this:

package main

// #cgo CFLAGS: -I/usr/include/apache2 -I/usr/include/apr-1.0 -Wl,--unresolved-symbols=ignore-all -Wl,--warn-unresolved-symbols
// #include "mod_frugal.h"
import "C"

import "unsafe"

//export handler
func handler(r unsafe.Pointer) int {
    C.go_ap_set_content_type(r, "text/html")
    C.go_ap_rprintf(r, "<h1>Hello, world from Go!</h1>")
    return OK
}

And created a header file to wrap the Apache calls:

#ifndef MOD_FRUGAL_H
#define MOD_FRUGAL_H

#include <httpd.h>
#include <http_protocol.h>

typedef void* Request;

static void go_ap_set_content_type(Request r, const char* t) {
    ap_set_content_type((request_rec*)r, t);
}

static void go_ap_rprintf(Request r, const char* t) {
    ap_rprintf((request_rec*)r, t);
}

#endif

So now I get this linker error:

# _/home/vagrant/mod_frugal/src/go_src
/tmp/go-build271197778/_/home/vagrant/mod_frugal/src/go_src/_obj/mod_frugal.cgo2.o: In function `go_ap_rprintf':
./mod_frugal.h:14: undefined reference to `ap_rprintf'
/tmp/go-build271197778/_/home/vagrant/mod_frugal/src/go_src/_obj/mod_frugal.cgo2.o: In function `go_ap_set_content_type':
./mod_frugal.h:10: undefined reference to `ap_set_content_type'
collect2: error: ld returned 1 exit status

Seems like I'm pretty close. I'm just not sure what to link this against.

On Friday, January 9, 2015 at 10:09:38 AM UTC-5, philne...@gmail.com wrote:

James Bardin

unread,
Jan 9, 2015, 3:08:45 PM1/9/15
to golan...@googlegroups.com, philne...@gmail.com


On Friday, January 9, 2015 at 2:49:58 PM UTC-5, philne...@gmail.com wrote:

Seems like I'm pretty close. I'm just not sure what to link this against.


You are not going to be able to call go from an apache module. The go program has to be the main process to setup the runtime. Go currently doesn't support creating shared libraries. 

philne...@gmail.com

unread,
Jan 9, 2015, 3:22:34 PM1/9/15
to golan...@googlegroups.com, philne...@gmail.com
Let me show you my mod_frugal.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ap_provider.h"
#include "httpd.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_request.h"

extern int handler(void*);

static int frugal_handler(request_rec* r) {
    int status = handler(r);
    return status;
}

static void register_hooks(apr_pool_t *pool) {
    ap_hook_handler(frugal_handler, NULL, NULL, APR_HOOK_LAST);
}

module AP_MODULE_DECLARE_DATA futile_module = {
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    register_hooks
};

This is the what apxs sees. I am just trying to hook into Go... or out of it. I'm having trouble explaining exactly what I'm trying to do but this C file should make it clear. I do believe this is possible if I can get the other aspects of compiling the Go into something accessible by C. Correct me if I'm still understanding. I definitely am new to Go.

philne...@gmail.com

unread,
Jan 9, 2015, 3:25:47 PM1/9/15
to golan...@googlegroups.com, philne...@gmail.com
Again, correct me if I'm wrong, but this Stack post seems to be doing exactly what I'm trying to do except without dependencies.

James Bardin

unread,
Jan 9, 2015, 10:09:17 PM1/9/15
to philne...@gmail.com, golan...@googlegroups.com
On Fri, Jan 9, 2015 at 3:25 PM, <philne...@gmail.com> wrote:
Again, correct me if I'm wrong, but this Stack post seems to be doing exactly what I'm trying to do except without dependencies.


That's a hack using gccgo, and I have no idea what you can actually make available to the calling program. There's only a small note about it in the faq:

> Gccgo is a GCC front-end that can, with care, be linked with GCC-compiled C or C++ programs. 


I do believe this is possible if I can get the other aspects of compiling the Go into something accessible by C. Correct me if I'm still understanding. I definitely am new to Go.


That is precisely what you can't do with the go toolchain. There's a proposal for other execution modes for Go, e.g. from a shared library, but that's not going to happen for a while. Until then, the Go runtime has to be the process entry point. 

minux

unread,
Jan 9, 2015, 11:31:21 PM1/9/15
to James Bardin, golang-nuts, philne...@gmail.com
On Fri, Jan 9, 2015 at 3:08 PM, James Bardin <j.ba...@gmail.com> wrote:
On Friday, January 9, 2015 at 2:49:58 PM UTC-5, philne...@gmail.com wrote:
Seems like I'm pretty close. I'm just not sure what to link this against.
You are not going to be able to call go from an apache module. The go program has to be the main process to setup the runtime.
Not impossible. Just hard.

It's possible to compile the Go code as a (static) object file, and link with the
other part of program. You need to start the Go runtime before calling back tp
cgo exported functions, but Go doesn't need to control the real main.

Hint: essentially the external linking support for Go compiles Go code into a
static object file. What if you rename the entry point in rt0_$GOOS_$GOARCH.s
to something other than "main", and call it to start the Go runtime? (This is how
the Android port works, except that it generates a position-dependent shared
library.

philne...@gmail.com

unread,
Jan 10, 2015, 9:34:06 AM1/10/15
to golan...@googlegroups.com, j.ba...@gmail.com, philne...@gmail.com
My most recent attempt came up right with what James was talking about. Here is mod_frugal.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ap_provider.h"
#include "httpd.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_request.h"

extern int handler(void*) __asm__ ("frugal2.main.handler");

static int frugal_handler(request_rec* r) {
    int status = handler(r);
    return status;
}

static void register_hooks(apr_pool_t *pool) {
    ap_hook_handler(frugal_handler, NULL, NULL, APR_HOOK_LAST);
}

module AP_MODULE_DECLARE_DATA frugal2_module = {
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    register_hooks
};

Here is frugal2.go:

package main

import "unsafe"

//extern ap_set_content_type
func c_ap_set_content_type(r unsafe.Pointer, t []byte)

//extern ap_rprintf
func c_ap_rprintf(r unsafe.Pointer, t []byte)

func handler(r unsafe.Pointer) int {
    c_ap_set_content_type(r, []byte("text/html"))
    c_ap_rprintf(r, []byte("<h1>Hello, world from Go!</h1>"))
    return 0
}

This is my compilation script:

libtool --tag=CC --mode=compile gccgo -c frugal2.go -o frugal2.o -fgo-prefix=frugal2 -static-libgo
apxs -c frugal2.lo mod_frugal2.c
sudo apxs -ai -n frugal2 mod_frugal2.la

And I get no error until I restart apache at which point this comes up:

Output of config test was:
apache2: Syntax error on line 140 of /etc/apache2/apache2.conf: Syntax error on line 1 of /etc/apache2/mods-enabled/frugal2.load: Cannot load /usr/lib/apache2/modules/mod_frugal2.so into server: /usr/lib/apache2/modules/mod_frugal2.so: undefined symbol: __go_new
Action 'configtest' failed.

So it looks like James was right, until you sent this message. I'll give this a shot! Thanks for the suggestion!
Reply all
Reply to author
Forward
0 new messages