Skip to content

ARM64 (Apple Silicon macOS) build support - critical gsbytes.c fix #233

@Steve-Albers

Description

@Steve-Albers

We have successfully built NCAR Graphics 6.6.2 on ARM64 macOS (Apple Silicon M1/M2/M3) and identified several source code changes required. The most critical is a bug in gsbytes.c that causes SIGSEGV crashes on ARM64.

Environment

  • macOS on Apple Silicon (ARM64)
  • Homebrew GCC 15 / gfortran
  • XQuartz for X11

Critical Bug Fix: gsbytes.c

Problem

On ARM64 architectures, long is 64-bit (8 bytes), but the code in common/src/libncarg_c/gsbytes.c assumes 32-bit words (#define SWORD 32). This mismatch causes:

  • Incorrect bit shifting operations
  • Memory corruption and buffer overflows
  • SIGSEGV crashes when using NCGM workstation (type 1)

The crash occurs in the SBYTES/GBYTES bit-packing routines called during metafile generation.

Fix

Change all long declarations to int in gsbytes.c:

// Original (crashes on ARM64):
extern void NGCALLF(gbyte,GBYTE)(long *p, long *u, long *q, long *b);
extern void NGCALLF(sbyte,SBYTE)(long *p, long *u, long *q, long *b);

void NGCALLF(gbytes,GBYTES)(p, u, q, b, s, n)
    long           *p, *u, *q, *b, *s, *n;
{
    register long   i = 0, jp = 0;
    long            jq = *q;

// Fixed (works on ARM64):
extern void NGCALLF(gbyte,GBYTE)(int *p, int *u, int *q, int *b);
extern void NGCALLF(sbyte,SBYTE)(int *p, int *u, int *q, int *b);

void NGCALLF(gbytes,GBYTES)(p, u, q, b, s, n)
    int           *p, *u, *q, *b, *s, *n;
{
    register int   i = 0, jp = 0;
    int            jq = *q;

Functions requiring this change:

  • gbytes() - unpack multiple bytes
  • sbytes() - pack multiple bytes
  • gbyte() - unpack single byte
  • sbyte() - pack single byte
  • g8bits() - get 8-bit bytes
  • s8bits() - store 8-bit bytes

New Configuration File: config/Darwin_ARM64

A new configuration file is needed for ARM64 macOS builds:

/*
 * Description: Configuration for gfortran/gcc build on ARM64 (Apple Silicon) Mac.
 *              Created for macOS on M1/M2/M3 chips.
 */
#define HdfDefines  -DDARWIN
#define StdDefines  -DSYSV -D_POSIX_SOURCE -D_XOPEN_SOURCE -DByteSwapped -D__UNIXOS2__ -D_DARWIN_C_SOURCE
#define ByteSwapped
#define Cstatic
#define Cdynamic
#define CppCommand '/usr/bin/cpp -traditional'
#define CCompiler   /opt/homebrew/bin/gcc-15
#define CxxCompiler /opt/homebrew/bin/g++-15
#define FCompiler   /opt/homebrew/bin/gfortran
#define CcOptions      -fPIC -fopenmp -std=gnu89 -Wno-implicit-int -Wno-implicit-function-declaration
#define FcOptions      -fPIC -fno-range-check -fopenmp -fallow-argument-mismatch -fallow-invalid-boz
#define CtoFLibraries      -L/opt/homebrew/lib -lgfortran -lquadmath
#define CtoFLibrariesUser  -lgfortran -lquadmath
#define XToolLibrary    -lXt -lSM -lICE
#define BuildShared NO
#define XLibrary -lXpm -lX11 -lXext

#define LibSearchUser    -L/opt/X11/lib -L/opt/homebrew/lib
#define IncSearchUser    -I/opt/X11/include -I/opt/homebrew/include -I/opt/homebrew/include/freetype2

#define ArchRecLibSearch    -L/opt/X11/lib -L/opt/homebrew/lib
#define ArchRecIncSearch    -I/opt/X11/include -I/opt/homebrew/include

FC = $(F77)

Fortran BOZ Literal Constants (~97 files)

Modern gfortran rejects BOZ literal constants (e.g., Z'40000000') in certain contexts. These need to be converted to decimal integers.

Example change in ncarg2d/src/libncarg_gks/bwi/argb2ci.f:

! Original (fails with modern gfortran):
parameter (ARGBMASK = Z'40000000')
parameter (RMASK     = Z'00FF0000')
r = (iand(index, RMASK) / Z'0000FFFF') / 255.

! Fixed (decimal equivalents):
parameter (ARGBMASK = 1073741824)
parameter (RMASK     = 16711680)
parameter (RSHIFT    = 65535)
r = (iand(index, RMASK) / RSHIFT) / 255.

Fortran compiler flags that help:

  • -fallow-invalid-boz - Allows some BOZ usage (but not all)
  • -fno-range-check - Relaxes range checking

Summary of Required Changes

  1. Critical: Fix common/src/libncarg_c/gsbytes.c - change long to int
  2. New file: Add config/Darwin_ARM64 configuration
  3. ~97 Fortran files: Convert BOZ literals to decimal integers
  4. Site.local: Configure with BuildShared NO for static libraries

Testing

After these changes:

  • GKS workstation type 1 (NCGM) works correctly
  • gmeta files are generated successfully
  • ctrans converts gmeta to PostScript and other formats
  • All basic NCAR Graphics functionality tested and working

Notes

  • The gsbytes.c fix may also apply to other 64-bit platforms where sizeof(long) != 4
  • Consider using int32_t from <stdint.h> for maximum portability
  • The Fortran BOZ changes could potentially be automated with a script

Tested on macOS Sequoia (ARM64) with Homebrew GCC 15, XQuartz, December 2025

ncl-arm64-patches.tar.gz

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions