Daniel Adler
2012-Feb-06 13:27 UTC
[Rd] Segfault on ".C" registration via R_CMethodDef according to 'Writing R Extensions'.
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
Simon Urbanek
2012-Feb-06 14:47 UTC
[Rd] Segfault on ".C" registration via R_CMethodDef according to 'Writing R Extensions'.
Daniel, the code you are using is probably not what you intended - since there is no length information the compiler assumes you are filling the structure sequentially and thus the expected value is that for R_NativePrimitiveArgType* which has no length, so it can only be initialized with a scalar value, so you are setting R_NativePrimitiveArgType* types = REALSXP which is wrong as you are casing an integer into a pointer. Also that's why you get the warnings which are all valid: ra.c:9: warning: braces around scalar initializer -- because it is interpeted as {REALSXP} hence superfluous braces ra.c:9: warning: initialization makes pointer from integer without a cast -- because you're initializing R_NativePrimitiveArgType* with an integer (REALSXP) ra.c:9: warning: excess elements in scalar initializer -- because INTSXP, ... must be ignored since "types" can only be initialized with a scalar (pointer) For what you intended, you're using variable-length array so you have to specify its length: {"myC", (DL_FUNC) &myC, 4, (R_NativePrimitiveArgType[4]) {REALSXP, INTSXP, STRSXP, LGLSXP}} which will allocate extra static object (it has to because "types" is a pointer, not a fixed array) - so the effect is the same as using a static object. Cheers, Simon On Feb 6, 2012, at 8:27 AM, Daniel Adler wrote:> 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 > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel > >