Skip to content

Instantly share code, notes, and snippets.

@alexxgarci
Last active September 4, 2025 14:34
Show Gist options
  • Save alexxgarci/3c17aebc3b9a1e7f71aef8288ca63141 to your computer and use it in GitHub Desktop.
Save alexxgarci/3c17aebc3b9a1e7f71aef8288ca63141 to your computer and use it in GitHub Desktop.
Build gradle script for realigning librealm.so library to 16 kb pagesize
// ==========================================
// 16KB PAGE SIZE REALM LIBRARY FIX
// ==========================================
task realignRealmLibraries {
description 'Realign Realm native libraries to 16KB page size'
group 'build'
doLast {
// Process all build variants
def buildDir = new File(project.buildDir, "intermediates/merged_native_libs")
if (!buildDir.exists()) {
println "πŸ“ No native libs found yet, Realm realignment will run during assembly"
return
}
// Path to our Python ELF fixer script
def projectRoot = project.rootProject.projectDir.parent
def pythonScript = new File(projectRoot, "fix_elf_segments.py")
if (!pythonScript.exists()) {
println "⚠️ Python ELF fixer script not found: ${pythonScript.absolutePath}"
return
}
buildDir.eachDir { variantDir ->
variantDir.eachDir { abiDir ->
def libDir = new File(abiDir, "out/lib")
if (libDir.exists()) {
libDir.eachDir { archDir ->
def realmLib = new File(archDir, "librealm_dart.so")
if (realmLib.exists()) {
realignLibraryWithPython(realmLib, pythonScript, archDir.name)
}
}
}
}
}
}
}
def realignLibraryWithPython(File libraryFile, File pythonScript, String arch) {
try {
println "πŸ”§ Realigning ${libraryFile.name} for ${arch} to 16KB page size..."
// Create backup
def backupFile = new File(libraryFile.parent, "${libraryFile.name}.backup")
if (!backupFile.exists()) {
def backupData = libraryFile.bytes
backupFile.bytes = backupData
}
// Run Python script
def cmd = ["python3", pythonScript.absolutePath, libraryFile.absolutePath]
def process = cmd.execute()
process.waitFor()
if (process.exitValue() == 0) {
println "βœ… Successfully realigned ${libraryFile.name} for ${arch}"
verifyLibraryAlignment(libraryFile, arch)
} else {
println "❌ Failed to realign ${libraryFile.name}, restoring backup"
println "Error output: ${process.errorStream.text}"
libraryFile.bytes = backupFile.bytes
}
} catch (Exception e) {
println "❌ Error realigning ${libraryFile.name}: ${e.message}"
}
}
def verifyLibraryAlignment(File libraryFile, String arch) {
try {
def ndkDir = android.ndkDirectory
if (ndkDir == null) {
def sdkDir = android.sdkDirectory
ndkDir = new File(sdkDir, "ndk/27.0.12077973")
}
def objdump = new File(ndkDir, "toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objdump")
if (!objdump.exists()) return
def cmd = [objdump.absolutePath, "-p", libraryFile.absolutePath]
def process = cmd.execute()
process.waitFor()
def output = process.inputStream.text
def loadLines = output.split('\n').findAll { it.contains('LOAD') }
def has16KBAlignment = loadLines.any {
it.contains('align 2**14') || it.contains('align 2**15') || it.contains('align 2**16')
}
if (has16KBAlignment) {
println "βœ… Verification: ${libraryFile.name} (${arch}) now has 16KB alignment"
} else {
println "⚠️ Verification: ${libraryFile.name} (${arch}) may still need manual alignment"
}
} catch (Exception e) {
println "⚠️ Could not verify alignment for ${libraryFile.name}: ${e.message}"
}
}
// Hook the realignment task into the build process
afterEvaluate {
tasks.matching { it.name.startsWith('merge') && it.name.endsWith('NativeLibs') }.all { task ->
task.finalizedBy realignRealmLibraries
}
}
#!/bin/bash
# Check 16KB Page Size Compliance Script
# Supports both APK and Android App Bundle (AAB) files
# Based on https://developer.android.com/guide/practices/page-sizes
set -e
# Check for help flag
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
echo "16KB Page Size Compliance Checker"
echo "================================="
echo ""
echo "Usage: $0 [file_path]"
echo ""
echo "Arguments:"
echo " file_path Path to APK or AAB file (optional)"
echo " If not provided, will auto-detect:"
echo " - build/app/outputs/bundle/productionRelease/app-production-release.aab"
echo " - build/app/outputs/flutter-apk/app-production-release.apk"
echo ""
echo "Examples:"
echo " $0 # Auto-detect file"
echo " $0 my-app.apk # Check specific APK"
echo " $0 my-app.aab # Check specific AAB"
echo ""
echo "Requirements:"
echo " - Android SDK with build-tools/35.0.0"
echo " - Android NDK 27.0.12077973"
echo " - bundletool (for AAB support): brew install bundletool"
exit 0
fi
echo "πŸ” Checking 16KB Page Size Compliance for Focus Flutter App"
echo "============================================================="
# Configuration
if [ -n "$1" ]; then
INPUT_FILE="$1"
else
# Try to find AAB first (preferred for Play Store), then fallback to APK
if [ -f "build/app/outputs/bundle/productionΓ§/app-production-release.aab" ]; then
INPUT_FILE="build/app/outputs/bundle/productionRelease/app-production-release.aab"
else
INPUT_FILE="build/app/outputs/flutter-apk/app-production-release.apk"
fi
fi
TEMP_DIR="/tmp/apk_analysis_16kb"
ANDROID_HOME="${ANDROID_HOME:-$HOME/Library/Android/sdk}"
NDK_VERSION="27.0.12077973"
# Detect file type and set paths
if [[ "$INPUT_FILE" == *.aab ]]; then
FILE_TYPE="AAB"
AAB_PATH="$INPUT_FILE"
APK_PATH="$TEMP_DIR/extracted.apk"
else
FILE_TYPE="APK"
APK_PATH="$INPUT_FILE"
fi
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Check if input file exists
if [ ! -f "$INPUT_FILE" ]; then
echo -e "${RED}❌ File not found: $INPUT_FILE${NC}"
echo "Please build the production release first:"
if [[ "$INPUT_FILE" == *.aab ]]; then
echo " fvm flutter build appbundle --release --flavor=production --target=lib/main_production.dart"
else
echo " fvm flutter build apk --release --flavor=production --target=lib/main_production.dart"
fi
exit 1
fi
echo -e "${BLUE}πŸ“± Input File: $INPUT_FILE ($FILE_TYPE)${NC}"
# Handle AAB files by extracting APK first
if [ "$FILE_TYPE" = "AAB" ]; then
echo -e "\n${BLUE}πŸ”§ Converting AAB to APK for analysis...${NC}"
# Check if bundletool is available
if ! command -v bundletool &> /dev/null; then
echo -e "${RED}❌ bundletool not found${NC}"
echo "Please install bundletool: brew install bundletool"
exit 1
fi
# Create temp directory
rm -rf "$TEMP_DIR"
mkdir -p "$TEMP_DIR"
# Extract universal APK from AAB
APKS_PATH="$TEMP_DIR/app.apks"
echo -e " πŸ“¦ Extracting universal APK from AAB..."
bundletool build-apks --bundle="$AAB_PATH" --output="$APKS_PATH" --mode=universal > /dev/null 2>&1
# Extract the APK from the APKS file
echo -e " πŸ“€ Extracting APK from APKS..."
echo "" | unzip -o -q "$APKS_PATH" -d "$TEMP_DIR" >/dev/null 2>&1
# Find the universal APK
UNIVERSAL_APK=$(find "$TEMP_DIR" -name "universal.apk" | head -1)
if [ ! -f "$UNIVERSAL_APK" ]; then
echo -e "${RED}❌ Failed to extract universal APK from AAB${NC}"
exit 1
fi
# Copy to our expected location
cp "$UNIVERSAL_APK" "$APK_PATH"
echo -e "${GREEN}βœ… Successfully extracted APK from AAB${NC}"
fi
# Step 1: Check APK zip alignment for 16KB
echo -e "\n${BLUE}πŸ”§ Step 1: Checking APK Zip Alignment...${NC}"
if "$ANDROID_HOME/build-tools/35.0.0/zipalign" -c -P 16 -v 4 "$APK_PATH" > /dev/null 2>&1; then
echo -e "${GREEN}βœ… APK is properly aligned for 16KB page size${NC}"
else
echo -e "${RED}❌ APK failed 16KB alignment check${NC}"
echo "Run this to see details:"
echo " \$ANDROID_HOME/build-tools/35.0.0/zipalign -c -P 16 -v 4 $APK_PATH"
exit 1
fi
# Step 2: Extract and analyze native libraries
echo -e "\n${BLUE}πŸ”§ Step 2: Extracting APK for native library analysis...${NC}"
# Only create temp dir if not already created (for AAB processing)
if [ ! -d "$TEMP_DIR" ]; then
rm -rf "$TEMP_DIR"
mkdir -p "$TEMP_DIR"
fi
# Extract APK contents
APK_EXTRACT_DIR="$TEMP_DIR/apk_contents"
mkdir -p "$APK_EXTRACT_DIR"
echo "" | unzip -o -q "$APK_PATH" -d "$APK_EXTRACT_DIR" >/dev/null 2>&1
# Step 3: Check ELF alignment for arm64-v8a and x86_64 libraries
echo -e "\n${BLUE}πŸ”§ Step 3: Checking ELF Segment Alignment...${NC}"
OBJDUMP="$ANDROID_HOME/ndk/$NDK_VERSION/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objdump"
if [ ! -f "$OBJDUMP" ]; then
echo -e "${RED}❌ NDK objdump not found: $OBJDUMP${NC}"
echo "Please ensure Android NDK $NDK_VERSION is installed"
exit 1
fi
check_elf_alignment() {
local lib_path="$1"
local arch="$2"
if [ ! -f "$lib_path" ]; then
echo -e "${YELLOW}⚠️ Library not found: $lib_path${NC}"
return 1
fi
local lib_name=$(basename "$lib_path")
echo -e "\n πŸ“š Checking: ${BLUE}$lib_name${NC} (${arch})"
# Get LOAD segments and check alignment
local load_output
load_output=$("$OBJDUMP" -p "$lib_path" | grep LOAD || true)
if [ -z "$load_output" ]; then
echo -e " ${RED}❌ No LOAD segments found${NC}"
return 1
fi
local all_aligned=true
while IFS= read -r line; do
if echo "$line" | grep -q "align 2\*\*1[6-9]"; then
# 2**16 (16KB) or higher alignment
local align=$(echo "$line" | grep -o "align 2\*\*[0-9]*")
echo -e " ${GREEN}βœ… $align${NC}"
elif echo "$line" | grep -q "align 2\*\*1[4-5]"; then
# 2**14 (16KB) or 2**15 (32KB) - acceptable for 16KB
local align=$(echo "$line" | grep -o "align 2\*\*[0-9]*")
echo -e " ${GREEN}βœ… $align${NC}"
else
# Less than 16KB alignment
local align=$(echo "$line" | grep -o "align 2\*\*[0-9]*" || echo "unknown")
echo -e " ${RED}❌ $align (needs 2**14 or higher for 16KB support)${NC}"
all_aligned=false
fi
done <<< "$load_output"
if [ "$all_aligned" = true ]; then
echo -e " ${GREEN}βœ… Library is 16KB compliant${NC}"
return 0
else
echo -e " ${RED}❌ Library needs recompilation for 16KB support${NC}"
return 1
fi
}
# Check critical architectures and libraries
COMPLIANCE_ISSUES=0
# arm64-v8a (most important for modern devices)
if [ -d "$APK_EXTRACT_DIR/lib/arm64-v8a" ]; then
echo -e "\n${BLUE}πŸ—οΈ Checking arm64-v8a libraries:${NC}"
for lib in "$APK_EXTRACT_DIR/lib/arm64-v8a"/*.so; do
if ! check_elf_alignment "$lib" "arm64-v8a"; then
((COMPLIANCE_ISSUES++))
fi
done
else
echo -e "${YELLOW}⚠️ No arm64-v8a libraries found${NC}"
fi
# x86_64 (for emulators and some devices)
if [ -d "$APK_EXTRACT_DIR/lib/x86_64" ]; then
echo -e "\n${BLUE}πŸ—οΈ Checking x86_64 libraries:${NC}"
for lib in "$APK_EXTRACT_DIR/lib/x86_64"/*.so; do
if ! check_elf_alignment "$lib" "x86_64"; then
((COMPLIANCE_ISSUES++))
fi
done
else
echo -e "${YELLOW}⚠️ No x86_64 libraries found${NC}"
fi
# Step 4: Generate report
echo -e "\n${BLUE}πŸ“Š 16KB Page Size Compliance Report${NC}"
echo "=========================================="
if [ $COMPLIANCE_ISSUES -eq 0 ]; then
echo -e "${GREEN}πŸŽ‰ CONGRATULATIONS! Your app is 16KB page size compliant!${NC}"
echo -e "${GREEN}βœ… All native libraries have proper 16KB ELF alignment${NC}"
echo -e "${GREEN}βœ… APK zip alignment is correct for 16KB pages${NC}"
echo ""
echo -e "${GREEN}Your app will work on Android 15+ devices with 16KB page size!${NC}"
else
echo -e "${RED}⚠️ COMPLIANCE ISSUES FOUND: $COMPLIANCE_ISSUES${NC}"
echo ""
echo -e "${YELLOW}πŸ”§ Recommended fixes:${NC}"
echo "1. Update problematic libraries to versions with 16KB support"
echo "2. Recompile native libraries with proper flags:"
echo " - For NDK r27+: Enable APP_SUPPORT_FLEXIBLE_PAGE_SIZES=true"
echo " - For older NDK: Add -Wl,-z,max-page-size=16384 flags"
echo "3. Check library documentation for 16KB page size support"
echo ""
echo -e "${BLUE}πŸ“š Reference: https://developer.android.com/guide/practices/page-sizes${NC}"
fi
# Cleanup
rm -rf "$TEMP_DIR"
echo -e "\n${BLUE}✨ Analysis complete!${NC}"
exit $COMPLIANCE_ISSUES
#!/usr/bin/env python3
"""
ELF Segment Alignment Tool for 16KB Page Size Compliance
Fixes ELF LOAD segment alignment to 16KB boundaries
"""
import sys
import struct
import os
def fix_elf_alignment(file_path):
"""Fix ELF segment alignment to 16KB boundaries"""
try:
with open(file_path, 'r+b') as f:
data = f.read()
# Check ELF magic
if data[:4] != b'\x7fELF':
print(f"Not an ELF file: {file_path}")
return False
# Get architecture (32 or 64 bit)
is_64bit = data[4] == 2
if is_64bit:
# 64-bit ELF
e_phoff = struct.unpack('<Q', data[32:40])[0]
e_phentsize = struct.unpack('<H', data[54:56])[0]
e_phnum = struct.unpack('<H', data[56:58])[0]
else:
# 32-bit ELF
e_phoff = struct.unpack('<I', data[28:32])[0]
e_phentsize = struct.unpack('<H', data[42:44])[0]
e_phnum = struct.unpack('<H', data[44:46])[0]
modified = False
# Process program headers
for i in range(e_phnum):
ph_offset = e_phoff + i * e_phentsize
if is_64bit:
p_type = struct.unpack('<I', data[ph_offset:ph_offset+4])[0]
if p_type == 1: # PT_LOAD
p_align_offset = ph_offset + 48
current_align = struct.unpack('<Q', data[p_align_offset:p_align_offset+8])[0]
if current_align < 16384:
data = data[:p_align_offset] + struct.pack('<Q', 16384) + data[p_align_offset+8:]
modified = True
else:
p_type = struct.unpack('<I', data[ph_offset:ph_offset+4])[0]
if p_type == 1: # PT_LOAD
p_align_offset = ph_offset + 28
current_align = struct.unpack('<I', data[p_align_offset:p_align_offset+4])[0]
if current_align < 16384:
data = data[:p_align_offset] + struct.pack('<I', 16384) + data[p_align_offset+4:]
modified = True
if modified:
f.seek(0)
f.write(data)
f.truncate()
print(f"βœ… Successfully aligned {file_path} to 16KB")
return True
else:
print(f"ℹ️ {file_path} already aligned")
return True
except Exception as e:
print(f"❌ Error processing {file_path}: {e}")
return False
if __name__ == '__main__':
if len(sys.argv) != 2:
print("Usage: python3 fix_elf_segments.py <library_file>")
sys.exit(1)
library_path = sys.argv[1]
if not os.path.exists(library_path):
print(f"File not found: {library_path}")
sys.exit(1)
success = fix_elf_alignment(library_path)
sys.exit(0 if success else 1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment