# Passing complex struct to C function results in segfault

**URL:** https://discourse.julialang.org/t/passing-complex-struct-to-c-function-results-in-segfault/58100
**Category:** General Usage
**Tags:** question, c
**Created:** [March 28, 2021, 12:25am UTC](https://discourse.julialang.org/t/passing-complex-struct-to-c-function-results-in-segfault/58100 "2021-03-28T00:25:14Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![oekenta](https://avatars.discourse-cdn.com/v4/letter/o/90ced4/32.png) [@oekenta](https://discourse.julialang.org/u/oekenta)
#### Post date: [March 28, 2021, 12:25am UTC](https://discourse.julialang.org/t/passing-complex-struct-to-c-function-results-in-segfault/58100/1 "2021-03-28T00:25:14Z")

</div>

I’m trying to interface with a C API for solving linear systems but I keep running into segfault errors. The algorithm uses a somewhat complex C structure, called LUSOLrec, to keep track of all the relevant data.

**\_LUSOLrec in C**

```julia
typedef struct _LUSOLrec {

  /* Parameter storage arrays */
  int luparm[LUSOL_IP_LASTITEM + 1];
  REAL parmlu[LUSOL_RP_LASTITEM + 1];

  /* Arrays of length lena+1 */
  int lena, nelem;
  int *indc, *indr;
  REAL *a;

  /* Arrays of length m+1 (row storage) */
  int maxm, m;
  int *lenr, *ip, *iqloc, *ipinv, *locr;

  /* Arrays of length n+1 (column storage) */
  int maxn, n;
  int *lenc, *iq, *iploc, *iqinv, *locc;
  REAL *w, *vLU6L;

  /* Extra arrays of length n for TCP and keepLU == FALSE */
  REAL *Ha, *diagU;
  int *Hj, *Hk; 

  /* Extra arrays of length m for TRP*/
  REAL *amaxr;

  /* Extra array for L0 stored by row for faster btran */
  LUSOLmat *L0;
  
  /* Miscellaneous data */
  int expanded_a;
  int replaced_c;
  int replaced_r;

} LUSOLrec;

/* Sparse matrix data */
typedef struct _LUSOLmat {
  REAL *a;
  int *lenx, *indr, *indc, *indx;
} LUSOLmat;

```

I converted this to a Julia struct so I could access the fields both in Julia and in C.

**LUSOLrec in Julia**

```julia
struct LUSOLrec 
    luparm::NTuple{33,Cint}
    parmlu::NTuple{21,Cdouble}

    # Arrays of length lena+1
    lena::Cint
    nelm::Cint
    
    indc::Array{Cint,1}
    indr::Array{Cint,1}
    a::Array{Cdouble,1}

    # Arrays of length m+1 (row storage)
    
    maxm::Cint
    m::Cint
    
    lenr::Array{Cint,1}
    ip::Array{Cint,1}
    iqloc::Array{Cint,1}
    ipinv::Array{Cint,1}
    locr::Array{Cint,1}

    # Arrays of length n+1 (column storage)
    
    maxn::Cint
    n::Cint
    lenc::Array{Cint,1}
    iq::Array{Cint,1}
    iploc::Array{Cint,1}
    iqinv::Array{Cint,1}
    locc::Array{Cint,1}
    
    w::Array{Cdouble,1}
    vLU6L::Array{Cdouble}

    # Extra arrays of length n for TCP and keepLU == False

    Ha::Array{Cdouble,1}
    diagU::Array{Cdouble,1}
    Hj::Array{Cint,1}
    Hk::Array{Cint,1}

    # Extra arrays of length m for TRP
    amaxr::Array{Cdouble,1}

    # Extra array for L0 stored by row for faster btran
    L0::Ptr{LUSOLmat}

    # Miscellaneous data
    expanded_a::Cint
    replaced_c::Cint
    replaced_r::Cint
end
struct LUSOLmat
    a::Array{Cdouble,1} 
    lenx::Array{Cint,1}
    indr::Array{Cint,1}
    indc::Array{Cint,1}
    indx::Array{Cint,1}
end

```

The API includes this function for assigning some of the fields of the LUSOLrec struct

**LUSOL\_assign function**

```julia
MYBOOL LUSOL_assign(LUSOLrec *LUSOL, int iA[], int jA[], REAL Aij[], int nzcount, MYBOOL istriplet)
{
  int k, m, n, ij, kol;

  m = 0;
  n = 0;
  kol = 1;
  for(k = 0; k < nzcount; k++) {
    /* First the row indicator */
    ij = iA[k];
    LUSOL->indc[k] = ij;

    /* Then the column indicator;
       Handle both triplet and column count formats */
    if(istriplet)
      ij = jA[k];
    else {
      if(k >= jA[kol])
        kol++;
      ij = kol;
    }

    LUSOL->indr[k] = ij;

    /* Lastly the matrix value itself */
    LUSOL->a[k] = Aij[k];
  }
  LUSOL->m = m;
  LUSOL->n = n;
  LUSOL->nelem = nzcount;
  return( TRUE );

```

But when I try to call the above function in Julia I get a segfault. I’ve stepped through the function with gdb and it looks like everything is working as it should from within the function.

The code I’m using to run the function is

```julia
function lusol_assign(LUSOL::LUSOLrec, iA::Array{Cint,1}
                      ,jA::Array{Cint,1},Aij::Array{Cdouble,1},
                      nzcount::Integer,istriplet::Integer)

    output = ccall(plusol_assign,
                   Cuchar,
                   (Ref{LUSOLrec},Ptr{Cint},Ptr{Cint},Ptr{Cdouble},
                    Cint,Cuchar),
                   LUSOL, iA, jA, Aij, nzcount, istriplet)
    return output
end
                   

using SparseArrays
LUSOL = lusol_rec(5,5,1000)

A = sprand(5,5,.5)
(i,j,v) = findnz(A)
i = convert(Array{Cint,1},i)
j = convert(Array{Cint,1},j)
lusol_assign(LUSOL,i,j,v,length(i),1)

```

Where lusol\_rec constructs the initial LUSOL object.

**lusol\_rec function**

```julia
function lusol_rec(m,n,lena)
    luparm = tuple(zeros(Cint,33)...)
    parmlu = tuple(zeros(Cdouble, 21)...)

   
    # Arrays of length lena + 1
    lena = Cint(lena)
    nelm = 0

    indc = zeros(Cint,lena+1)
    indr = zeros(Cint,lena+1)
    a = zeros(Cdouble,lena+1)

   
    # Arrays of length m+1
    maxm = m

    lenr = zeros(Cint,m+1)
    ip = zeros(Cint,m+1)
    iqloc = zeros(Cint,m+1)
    ipinv = zeros(Cint,m+1)
    locr = zeros(Cint,m+1)

    #Arrays of length n+1
    maxn = n

    lenc = zeros(Cint,n+1)
    iq = zeros(Cint,n+1)
    iploc = zeros(Cint,n+1)
    iqinv = zeros(Cint,n+1)
    locc = zeros(Cint,n+1)

    w = zeros(Cdouble, n+1)
    vLU6L = zeros(Cdouble, n+1)

    #Extra arrays of length n for TCP and keepLU = False
    Ha = zeros(Cdouble,n)
    diagU = zeros(Cdouble, n)
    Hj = zeros(Cint, n)
    Hk = zeros(Cint, n)

    # Extra arrays of length m for TRP
    amaxr = zeros(Cdouble, m)

    # Extra array for L) stored for faster btrain
    L0 = Ptr{LUSOLmat}(C_NULL)

    # Miscellaneous data
    expanded_a = Cint(0)
    expanded_c = Cint(0)
    replaced_r = Cint(0)
    return LUSOLrec(luparm,parmlu,lena,nelm,indc,indr,a,maxm,
                    m,lenr,ip,iqloc,ipinv,locr,maxn,n,lenc,
                    iq,iploc,iqinv,locc,w,vLU6L,Ha,diagU,Hj,
                    Hk,amaxr,L0,expanded_a,expanded_c,
                    replaced_r)
    
end

```

Does anyone have any idea why this code is causing a segfault? I’m pretty sure I converted the LUSOLrec structure to a Julia structure correctly and called the LUSOL\_assign function correctly as well. I can even see that it looks like it’s working fine using gdb to step through the LUSOL\_assign function. But at the very end when it completes the function I get a segfault anyways. What am I doing wrong?

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [March 28, 2021, 1:50am UTC](https://discourse.julialang.org/t/passing-complex-struct-to-c-function-results-in-segfault/58100/2 "2021-03-28T01:50:12Z")

</div>

> [@oekenta](#):
>
> `lenc::Array{Cint,1}`

This is not equivalent to a C `int *` field.

---

<div class="post-metadata">

### Author: ![cjdoris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjdoris/32/213133_2.png) [@cjdoris](https://discourse.julialang.org/u/cjdoris)
#### Post date: [March 28, 2021, 12:38pm UTC](https://discourse.julialang.org/t/passing-complex-struct-to-c-function-results-in-segfault/58100/3 "2021-03-28T12:38:23Z")

</div>

To expand on stevengj’s comment, in order to pass structs to/from C you need to ensure they have exactly the same layout in Julia as in C. In your case, any fields like `int*` must be translated as `Ptr{Cint}` in Julia.

To populate this struct from Julia, you need to create arrays as you have and use `pointer` to extract a pointer suitable to insert into the struct. You need to ensure that the array itself is not garbage collected for as long as the pointer might be accessed, e.g. using `GC.@preserve` or storing it somewhere.

To access the data behind these pointers from Julia, you can use `unsafe_load` to get individual elements, or `unsafe_wrap` to interpret a block of memory as an array.

This is well documented here: [Calling C and Fortran Code · The Julia Language](https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/).

Small plug: my package [UnsafePointers.jl](https://github.com/cjdoris/UnsafePointers.jl) can make accessing complex nested structs like this much simpler.

---

<div class="post-metadata">

### Author: ![oekenta](https://avatars.discourse-cdn.com/v4/letter/o/90ced4/32.png) [@oekenta](https://discourse.julialang.org/u/oekenta)
#### Post date: [March 28, 2021, 5:50pm UTC](https://discourse.julialang.org/t/passing-complex-struct-to-c-function-results-in-segfault/58100/4 "2021-03-28T17:50:32Z")

</div>

Oh, I see. Thanks for the help!
