I am trying to use libpng1.6
to load a png from file, but the execution fails with a segmentation fault.
I load the library and libpng
function called png_image_begin_read_from_file
as shown:
final dylib = ffi.DynamicLibrary.open(
"/opt/homebrew/Cellar/libpng/1.6.37/lib/libpng.dylib");
final dart_function_t png_image_begin_read_from_file = dylib
.lookup<ffi.NativeFunction<native_function_t>>(
'png_image_begin_read_from_file')
.asFunction();
Here is the full snippet to reproduce the problem.
import 'dart:ffi' as ffi;
import 'package:ffi/ffi.dart';
class png_controlp extends ffi.Opaque {}
// Fields taken from "png.h"
class PngImage extends ffi.Struct {
@ffi.Uint32()
external int version,
width,
height,
flags,
format,
colormap_entries,
warning_or_error;
@ffi.Array(64)
external ffi.Array<ffi.Char> message;
external ffi.Pointer<png_controlp> opaque;
}
// native type
typedef native_function_t = ffi.Int32 Function(
ffi.Pointer<PngImage> image, ffi.Pointer<Utf8> file_name);
// dart type
typedef dart_function_t = int Function(
ffi.Pointer<PngImage> image, ffi.Pointer<Utf8> file_name);
void main() {
final dylib = ffi.DynamicLibrary.open(
"/opt/homebrew/Cellar/libpng/1.6.37/lib/libpng.dylib");
final dart_function_t png_image_begin_read_from_file = dylib
.lookup<ffi.NativeFunction<native_function_t>>(
'png_image_begin_read_from_file')
.asFunction();
final path = "my_image.png";
ffi.Pointer<PngImage> p_pngImage = calloc<PngImage>();
p_pngImage.ref.version = 1;
final res = png_image_begin_read_from_file(p_pngImage, path.toNativeUtf8());
}
===== CRASH =====
si_signo=Segmentation fault: 11(11), si_code=2, si_addr=0x11
version=2.17.0-266.7.beta (beta) (Mon Apr 25 14:54:53 2022 +0000) on "macos_arm64"
pid=42862, thread=20995, isolate_group=main(0x123810200), isolate=main(0x122034200)
isolate_instructions=10263d8c0, vm_instructions=10263d8c0
pc 0x00000001051b6604 fp 0x000000016e1be830 png_image_free+0x24
pc 0x00000001051b66c8 fp 0x000000016e1be850 png_image_error+0x38
It looks like the Dart struct is in the wrong order - check the C header:
2672 typedef struct
2673 {
2674 png_controlp opaque; /* Initialize to NULL, free with png_image_free */
2675 png_uint_32 version; /* Set to PNG_IMAGE_VERSION */
2676 png_uint_32 width; /* Image width in pixels (columns) */
2677 png_uint_32 height; /* Image height in pixels (rows) */
2678 png_uint_32 format; /* Image format as defined below */
2679 png_uint_32 flags; /* A bit mask containing informational flags */
2680 png_uint_32 colormap_entries;
2681 /* Number of entries in the color-map */
....
2704
2705 png_uint_32 warning_or_error;
2706
2707 char message[64];
2708 } png_image, *png_imagep;
So, the Dart struct should be:
class PngImage extends ffi.Struct {
external ffi.Pointer<png_controlp> opaque;
@ffi.Uint32()
external int version,
width,
height,
flags,
format,
colormap_entries,
warning_or_error;
@ffi.Array(64)
external ffi.Array<ffi.Char> message;
}
Also note the comment in the header:
2976 * The png_image passed to the read APIs must have been initialized by setting
2977 * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.)
Be sure to set the opaque
pointer to nullPtr
- though this is probably unnecessary as you are using calloc
to create it - which will zero it for you. (Also remember that you may need to make another C call to free stuff allocated by the read_from_file
method, and also free
anything that you calloc
.)