Skip to content

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

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions