Search code examples
phpcsegmentation-faultphp-extensionphp-internals

SEG Fault in PHP extension


I wrote a PHP extension to access functions in a static lib, I built PHP as a CGI, and everything seemed to work (after days of working on it..)

Thrilled once everything worked, I re-compiled PHP without debugging messages I had in it. (php_printf("here111"); .... php_printf("sending arguments...");)

Then, it just stopped working. The function I'm calling in the static lib works, I've tested it by calling it directly from another executable.

I built PHP with debugging symbols (--enable-debug) and can debug it to a certain degree in gdb.

I'm still struggling to figure out what's wrong. It seems that the function in the lib (diffFst) cannot seem to read the input arguments.

268     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssssssd",
269        &filA, &filA_len,
270        &nomvarA, &nomvarA_len,
271        &filB, &filB_len,
272        &nomvarB, &nomvarB_len,
273        &filO, &filO_len,
274        &newnomvar, &newnomvar_len,
275        &mult
276        ) == FAILURE) {
277         RETURN_LONG(-100);
278     }
279 
280     php_printf("Read arguments:\nfilA: %s, nomvara: %s\nfilB: %s, nomvarB: %s\nfilO: %s, nomvarO: %s\nMult: %0.3f\n",
281        filA,nomvarA, filB,nomvarB, filO,newnomvar, mult);
282 
285     ier = difffst_(filA,nomvarA, filB,nomvarB, filO,newnomvar, mult);

When I call this function, the php_printf() statement works and prints out the right values. When I let it call the difffst_ function however, I get a segfault when it tries to read the input variables.

The diffFst function is written in fortran:

  5 function diffFst(filA, nomvara, filB, nomvarb, filO, newnomvar, change, write_tictac, in_verbose) result(ier)
 10     implicit none
 11 

 12     character (len=*), intent(IN) :: filA, filB, filO
 13     character (len=*), intent(IN) :: nomvara, nomvarb, newnomvar
 14 
 16     real, intent(IN) :: change
 17     logical, intent(IN) :: write_tictac
 18 
 19     logical, intent(IN), optional :: in_verbose
 21     logical :: verbose = .false.
 27     integer :: ier
...
117     ier = fstouv(iuna, 'RND')
118     IF (ier < 0) THEN
119         if (verbose) write(stderr,'(2A)') "Could not fstouv FST file ", trim(filA)
120     ELSE
121         nmax = fstnbr(iuna);
122         if (verbose) write(stdout,'(3A,I6,A)') "Succesfully opened ", trim(filA), ' with ', nmax, ' records'
123         allocate(liste(nmax))
124     END IF

Specifically, it fails at line 122 (according to the debugger) when it tries to read filA.

I have no idea why, I've tried:

  • Making the function a subroutine
  • Making the function a fortran function
  • Making the function a 'pure' function
  • Having return values (that's what is there now, the ier = ..)
  • Having return statements in the code, removing the return statements
  • Tried printing things out to stdout, and to log files

It just seems like the data isn't being passed properly. Even in the debugger, I can't read the arguments.

The frustrating thing is, at one point this just worked.. I've checked file permissions, checked paths, etc.. And I can run the function from a fortran wrapper executable just fine.

Is there a trick I'm missing?

Thanks


Solution

  • Took a while, and needed additional help (issues like this)

    Basically two things had to change:

    • Pass integers by reference
    • Accept strings properly

    The first is easy, simply ier=func(..., &integer_var, ...)

    The second involved passing the length of the string. There may have been an easier way to do this (sensing string length by looking for the \0) but they didn't work out. So, now I pass

    ier = func(str,strlen,...)
    

    Then in Fortran, I accept the string as

    character(kind=c_char,len=strlen), intent(IN) :: str
    

    The specific changes to the fortran code above are

    11     use, intrinsic :: iso_c_binding
    12     use interfaces_rmnutils
    13     implicit none
    16     integer(kind=c_int), intent(IN) :: len_filA, len_filB, len_filO
    17     character (kind=c_char,len=len_filA), intent(IN) :: filA
    18     character (kind=c_char,len=len_filB), intent(IN) :: filB
    19     character (kind=c_char,len=len_filO), intent(IN) :: filO
    

    When it was working, it must have been before I attempted to read in the strings as (len=*), and integers were being passed in as references, so they'd have had random values essentially.

    Thanks for everyone's else!