Last active
September 4, 2025 14:34
-
-
Save alexxgarci/3c17aebc3b9a1e7f71aef8288ca63141 to your computer and use it in GitHub Desktop.
Build gradle script for realigning librealm.so library to 16 kb pagesize
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ========================================== | |
// 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 | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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