From dadler at uni-goettingen.de  Mon Feb  6 14:27:59 2012
From: dadler at uni-goettingen.de (Daniel Adler)
Date: Mon, 6 Feb 2012 14:27:59 +0100
Subject: [Rd] Segfault on ".C" registration via R_CMethodDef according to
	'Writing R Extensions'.
Message-ID: <1FB48665-E0BE-4956-A3BE-9B02038006B7@uni-goettingen.de>

Dear R List,

I encountered a serious problem regarding the registration of ".C" when following the documentation "Writing R Extensions"
that leads to a segmentation fault (tested on windows and mac os x).

The registration mechanism for ".C" routines via R_registerRoutines and
the R_CMethodDef structure has been enhanced recently with the
addition of two fields, one for type specification and the other for
the style (in, out, inout or irrelevant). 

According to the manual 'Writing R Extensions' of version 2.14.1
an example is given that specifies to use the fourth field (type information)
for definitions of C routines that use the ".C" calling convention:

R_CMethodDef cMethods[] = {
        {"myC", (DL_FUNC) &myC, 4, {REALSXP, INTSXP, STRSXP, LGLSXP}}, /* segfault! */
        {NULL, NULL, 0}
};

If I follow this example I get compiler warnings or errors (whether I use C or C++, respectively) and 
a segmentation fault (in the case of C) when doing R CMD INSTALL, which seem to happen during testing.
See build log at the end of this e-mail.

When removing the last field in the initializer list in order to register .C routines in the old way the segfault goes away:

R_CMethodDef cMethods[] = {
        {"myC", (DL_FUNC) &myC, 4}, /* works */
        {NULL, NULL, 0}
};

There are still warnings/segfault or an error when initializing the undocumented fifth entry (parameter passing style), e.g.

R_CMethodDef cMethods[] = {
        {"myC", (DL_FUNC) &myC, 4, {REALSXP, INTSXP, STRSXP, LGLSXP}, {R_ARG_IN, R_ARG_IN, R_ARG_IN, R_ARG_IN},  /* segfault! */
        {NULL, NULL, 0}
};

Using a C source, the warnings are:

*** arch - i386
gcc -arch i386 -std=gnu99 -I/Library/Frameworks/R.framework/Resources/include -I/Library/Frameworks/R.framework/Resources/include/i386  -I/usr/local/include    -fPIC  -g -O2 -Wall -pedantic -c reg.c -o reg.o
reg.c:24: warning: braces around scalar initializer
reg.c:24: warning: (near initialization for ?cMethods[0].types?)
reg.c:24: warning: initialization makes pointer from integer without a cast
reg.c:24: warning: excess elements in scalar initializer
reg.c:24: warning: (near initialization for ?cMethods[0].types?)
reg.c:24: warning: excess elements in scalar initializer
reg.c:24: warning: (near initialization for ?cMethods[0].types?)
reg.c:24: warning: excess elements in scalar initializer
reg.c:24: warning: (near initialization for ?cMethods[0].types?)


Using C++, protecting the init/unload function prototypes and structure declarations via 'extern "C" { }', I get the following error:

*** arch - i386
g++ -arch i386 -I/Library/Frameworks/R.framework/Resources/include -I/Library/Frameworks/R.framework/Resources/include/i386  -I/usr/local/include    -fPIC  -g -O2 -c reg.cpp -o reg.o
reg.cpp:30: error: braces around scalar initializer for type ???R_NativePrimitiveArgType*???

(line 24 is the point on the entry, while line 30 is the end of the overall array initialization list in C++).

If I put the type and style (unsigned int and enum) arrays separately,
the build process works just fine. E.g.

R_NativePrimitiveArgType types[] = {REALSXP, INTSXP, STRSXP, LGLSXP};
R_NativeArgStyle styles[] = { R_ARG_IN, R_ARG_IN, R_ARG_IN, R_ARG_IN };

R_CMethodDef cMethods[] = {
        {"myC", (DL_FUNC) &myC, 4, types, NULL},    /* works */
        {"myC2", (DL_FUNC) &myC, 4, types, style},  /* works */
        {NULL, NULL, 0}
};

(Though I haven't tested the runtime behaviour yet.. but at least no segfault during R CMD INSTALL..)

I wonder what is wrong with the static initializer lists?!

I could imagine it has something to do with the standard compilance of the C/C++
compiler (due to the different behaviour warning or error during compilation).

Anyway, going with the manual right now, the ordinary user will get warnings and 
errors - at least on the systems that I have tested (recent version of Rtools/Windows 7
and Mac OS X 10.6 with gcc 4.2.1). 
On Windows instead of a trace output, a window pops up during install to tell 
about a process crash.

- Daniel

PS: If it helps, I could put up a test package online for further debugging.

--- build log: ----------------------------------------------------------------

** testing if installed package can be loaded
*** arch - i386

 *** caught bus error ***
address 0xe, cause 'non-existent physical address'

Traceback:
 1: dyn.load(file, DLLpath = DLLpath, ...)
 2: library.dynam(lib, package, package.lib)
 3: loadNamespace(package, c(which.lib.loc, lib.loc))
 4: doTryCatch(return(expr), name, parentenv, handler)
 5: tryCatchOne(expr, names, parentenv, handlers[[1L]])
 6: tryCatchList(expr, classes, parentenv, handlers)
 7: tryCatch(expr, error = function(e) {    call <- conditionCall(e)    if (!is.null(call)) {        if (identical(call[[1L]], quote(doTryCatch)))             call <- sys.call(-4L)        dcall <- deparse(call)[1L]        prefix <- paste("Error in", dcall, ": ")        LONG <- 75L        msg <- conditionMessage(e)        sm <- strsplit(msg, "\n")[[1L]]        w <- 14L + nchar(dcall, type = "w") + nchar(sm[1L], type = "w")        if (is.na(w))             w <- 14L + nchar(dcall, type = "b") + nchar(sm[1L],                 type = "b")        if (w > LONG)             prefix <- paste(prefix, "\n  ", sep = "")    }    else prefix <- "Error : "    msg <- paste(prefix, conditionMessage(e), "\n", sep = "")    .Internal(seterrmessage(msg[1L]))    if (!silent && identical(getOption("show.error.messages"),         TRUE)) {        cat(msg, file = stderr())        .Internal(printDeferredWarnings())    }    invisible(structure(msg, class = "try-error", condition = e))})
 8: try({    ns <- loadNamespace(package, c(which.lib.loc, lib.loc))    dataPath <- file.path(which.lib.loc, package, "data")    env <- attachNamespace(ns, pos = pos, dataPath = dataPath,         deps)})
 9: library(pkg_name, lib.loc = lib, character.only = TRUE, logical.return = TRUE)
10: withCallingHandlers(expr, packageStartupMessage = function(c) invokeRestart("muffleMessage"))
11: suppressPackageStartupMessages(library(pkg_name, lib.loc = lib,     character.only = TRUE, logical.return = TRUE))
12: doTryCatch(return(expr), name, parentenv, handler)
13: tryCatchOne(expr, names, parentenv, handlers[[1L]])
14: tryCatchList(expr, classes, parentenv, handlers)
15: tryCatch(expr, error = function(e) {    call <- conditionCall(e)    if (!is.null(call)) {        if (identical(call[[1L]], quote(doTryCatch)))             call <- sys.call(-4L)        dcall <- deparse(call)[1L]        prefix <- paste("Error in", dcall, ": ")        LONG <- 75L        msg <- conditionMessage(e)        sm <- strsplit(msg, "\n")[[1L]]        w <- 14L + nchar(dcall, type = "w") + nchar(sm[1L], type = "w")        if (is.na(w))             w <- 14L + nchar(dcall, type = "b") + nchar(sm[1L],                 type = "b")        if (w > LONG)             prefix <- paste(prefix, "\n  ", sep = "")    }    else prefix <- "Error : "    msg <- paste(prefix, conditionMessage(e), "\n", sep = "")    .Internal(seterrmessage(msg[1L]))    if (!silent && identical(getOption("show.error.messages"),         TRUE)) {        cat(msg, file = stderr())        .Internal(printDeferredWarnings())    }    invisible(structure(msg, class = "try-error", condition = e))})
16: try(suppressPackageStartupMessages(library(pkg_name, lib.loc = lib,     character.only = TRUE, logical.return = TRUE)))
17: tools:::.test_load_package("mylib", "/Users/dadler/Library/R/2.14/library")
aborting ...
sh: line 1: 75200 Bus error               '/Library/Frameworks/R.framework/Resources/bin/R' --arch=i386 --no-save --slave < /var/folders/Lr/Lrh7GWILEqCwHkyF1MdauE+++TI/-Tmp-//RtmpDqusSN/file1259d93ec2eb
*** arch - x86_64



[Rd] Segfault on ".C" registration via R_CMethodDef according to 'Writing R Extensions'.
Daniel Adler