How to Build a Go Barcode Reader on Apple Silicon Macs (macOS dylib Linking)
The goBarcodeQrSDK Go module reads barcodes and QR codes on macOS by using cgo to link a C bridge library against the Dynamsoft Capture Vision SDK (Barcode Reader 11.6.10) universal *.dylib files. The Dynamsoft macOS libraries now ship as universal binaries — one libDynamsoftBarcodeReader.dylib works on both Apple Silicon (arm64) and Intel (x86_64) Macs — so you download the SDK once and link the same files on every Mac.
What you’ll build: a Go module plus command-line reader that decodes QR codes, Data Matrix, PDF417, and other 1D/2D symbols from images and PDFs on an Apple Silicon Mac, with the correct @rpath/DYLD_LIBRARY_PATH handling so the dylibs actually load.
Key Takeaways
- The Dynamsoft DCV SDK ships universal macOS dylibs (
arm64+x86_64); the goBarcodeQrSDK repo stores them underdcv/lib/mac/. - cgo cannot call C++ directly, so the module links a C bridge (
libbridge.dylib) that wrapsCCaptureVisionRouterwithextern "C". - On macOS, add the dylib directory with
-Wl,-rpath,${SRCDIR}/dcv/lib/macin the cgoLDFLAGSsodyldresolves@rpathentries at runtime. - When rpath is not embedded,
install_name_tool -add_rpathon the compiled binary is a reliable fallback that works on macOS Sonoma and later. - Build the bridge as a universal binary (
lipo-merged arm64 + x86_64) so onelibbridge.dylibserves all Macs.
Common Developer Questions
How do I load a macOS dylib from a Go program?
Add the library path to the cgo #cgo darwin LDFLAGS line with -Wl,-rpath,<path>, then link the library by name using the #cgo darwin LDFLAGS: -l<name> directive. The goBarcodeQrSDK module links -lbridge and points rpath at dcv/lib/mac, so dyld finds libbridge.dylib and its @rpath-referenced Dynamsoft dylibs automatically.
Does Dynamsoft Barcode Reader support Apple Silicon Macs?
Yes. Dynamsoft Barcode Reader 11.6.10 provides universal macOS binaries containing both arm64 (Apple Silicon) and x86_64 (Intel) code in a single libDynamsoftBarcodeReader.dylib. You can confirm with lipo -info — the file reports Architectures in the fat file with both arm64 and x86_64.
Why does my Go app crash with “dyld: Library not loaded”?
The macOS dynamic linker cannot find @rpath/libDynamsoftBarcodeReader.dylib when the compiled binary has no rpath entry pointing to dcv/lib/mac. Fix it by embedding the path at link time (-Wl,-rpath) or by running install_name_tool -add_rpath <path> <binary> on the built executable.
What is the universal bridge library on macOS?
The goBarcodeQrSDK module wraps the C++ CCaptureVisionRouter API in src/bridge.cpp and compiles it to libbridge.dylib. The scripts/build_macos_universal.sh script compiles the bridge once for arm64 and once for x86_64, then merges both with lipo -create so one dylib works on every Mac.
This article is Part 1 in a 3-Part Series.
Development Environment
- Mac mini (M2, 2023)
- macOS Sonoma 14.2
- Go 1.24
- CMake

Prerequisites
-
Xcode Command Line Tools
xcode-select --install - Go Environment
- Get a 30-day free trial license for Dynamsoft Barcode Reader.
- CMake (
brew install cmake) - The universal
*.dylibfiles of Dynamsoft Barcode Reader (DCV) for macOS — the goBarcodeQrSDK repository already includes them underdcv/lib/mac/.
Unlike the older v9 SDK, the modern Capture Vision C++ SDK download ships the macOS libraries directly, including universal builds for Apple Silicon and Intel. No more extracting dylibs from the Python wheel — copy the contents of the dcv/lib/mac folder from the SDK (or the repo) and you are ready.
Step 1: Adding *.dylib Files to the Go Module
In the dcv/lib/mac directory of the module, keep the universal dynamic libraries from the SDK:

The key files are libDynamsoftBarcodeReader.dylib, libDynamsoftCaptureVisionRouter.dylib, libDynamsoftCore.dylib, libDynamsoftLicense.dylib and libDynamsoftUtility.dylib. All of them are fat binaries (arm64 + x86_64), so a single set works on every Mac.
In reader.go, the macOS-specific cgo directives link the custom libbridge library and record the rpath:
package goBarcodeQrSDK
import (
"unsafe"
/*
#cgo CFLAGS: -I${SRCDIR}/dcv/include
#cgo darwin LDFLAGS: -L${SRCDIR}/dcv/lib/mac -lbridge -Wl,-rpath,${SRCDIR}/dcv/lib/mac
#cgo linux LDFLAGS: -L${SRCDIR}/dcv/lib/linux -lbridge -Wl,-rpath,${SRCDIR}/dcv/lib/linux
#cgo windows LDFLAGS: -L${SRCDIR}/dcv/lib/win -lbridge
#include <stdlib.h>
#include "bridge.h"
*/
"C"
)
With these directives, a Go binary built for darwin embeds ${SRCDIR}/dcv/lib/mac as an LC_RPATH entry, and dyld resolves every @rpath/lib*.dylib dependency against it — no environment variables needed for normal builds.
Step 2: Building the C Bridge as a Universal dylib
The bridge translates the C++ CCaptureVisionRouter calls into the pure-C DBR_* functions declared in dcv/include/bridge.h. Build it with CMake:
# Single architecture (current Mac only)
./scripts/build_macos.sh
# Universal binary (arm64 + x86_64) — recommended for distribution
./scripts/build_macos_universal.sh
The universal script compiles the bridge in build_arm64 and build_x86_64 and merges the results:
lipo -create libbridge_arm64.dylib libbridge_x86_64.dylib -output dcv/lib/mac/libbridge.dylib
Verify the output:
lipo -info dcv/lib/mac/libbridge.dylib
# Architectures in the fat file: dcv/lib/mac/libbridge.dylib are: x86_64 arm64
Step 3: Ensuring Go Tests Pass on macOS
Execute go test in the terminal to initiate the tests.

If you see import errors such as:
permission denieddyld: Library not loaded: libDynamsoftBarcodeReader.dylib
Resolving the Issues
-
Permission Denied: This issue can often be resolved by running the test with
sudo:sudo go test -
Dynamic Library Not Loaded: This error indicates that the dynamic linker cannot find
libDynamsoftBarcodeReader.dylib. There are two reliable solutions:-
Ensure the cgo
LDFLAGSinclude-Wl,-rpath,${SRCDIR}/dcv/lib/mac(recommended — automated).✔ rpath is embedded at build time with no manual steps.
-
Use the
install_name_toolcommand to append the library search path to the test binary.✔ A viable option when the rpath was not embedded.
-
Setting only
DYLD_LIBRARY_PATHmay be ignored on macOS Sonoma 14+ because of stricter runtime protections, so prefer rpath.
-
What Does the go test Command Do?
Go compiles the test files (*_test.go) together with the package into a temporary executable dedicated to tests. That binary must be able to resolve the dylibs, which is why the rpath matters:
go test -c -o testapp
./testapp
Adding Rpath to the Go Test Binary
-
Inspect the library search path of the Go test binary.
otool -L testapp testapp: @rpath/libDynamsoftBarcodeReader.dylib ...
@rpathis a placeholder thatdyldresolves at runtime based on theLC_RPATHentries embedded in the executable. -
Append the library search path to the Go test binary.
install_name_tool -add_rpath ./dcv/lib/mac testappVerify the rpath was added:
otool -l testapp | grep -A2 LC_RPATH
Writing a Script to Build and Run a Go Test
The repository’s run_mac_test.sh automates the compile-check-run cycle:
#!/bin/bash
RPATH="./dcv/lib/mac"
TARGET="testapp"
go test -c -o $TARGET
if ! otool -l $TARGET | grep -q $RPATH; then
echo "Adding rpath $RPATH to $TARGET"
install_name_tool -add_rpath $RPATH $TARGET
else
echo "RPATH $RPATH already exists in $TARGET"
fi
./$TARGET
rm ./$TARGET
Run it:
chmod +x run_mac_test.sh
sudo ./run_mac_test.sh

Step 4: Running the Go Barcode Reader Application on macOS
The command-line example lives in example/command-line. Its run_mac.sh script builds the app and adds the rpath for the module source directory:
#!/bin/bash
PACKAGE_PATH=$(find "$GOPATH/pkg/mod/github.com/yushulx" -mindepth 1 -maxdepth 1 -type d | sort -r | head -n 1)
RPATH="$PACKAGE_PATH/dcv/lib/mac"
TARGET="testapp"
go build -o $TARGET
if ! otool -l $TARGET | grep -q $RPATH; then
echo "Adding rpath $RPATH to $TARGET"
install_name_tool -add_rpath $RPATH $TARGET
else
echo "RPATH $RPATH already exists in $TARGET"
fi
./$TARGET test.png
rm ./$TARGET
go build compiles the reader, install_name_tool appends the dylib search path, and the app decodes test.png on the Apple Silicon Mac.
Common Issues & Edge Cases
dyld: Library not loaded— the binary is missing the rpath; embed it at link time or add it withinstall_name_tool.permission denied— rerun the script withsudo(the Dynamsoft SDK initializes some resources under privileged paths on macOS).responds to different architecturesfromlipo— build the bridge for arm64 and x86_64 separately before merging; a single -arch build produces a thin binary that only runs on one Mac type.DYLD_LIBRARY_PATHignored on Sonoma — SIP hardened runtime behavior; prefer embedded rpath over environment variables.
Source Code
Get the complete sample project source code on GitHub
Disclaimer:
The wrappers and sample code on Dynamsoft Codepool are community editions, shared as-is and not fully tested. Dynamsoft is happy to provide technical support for users exploring these solutions but makes no guarantees.