Skip to content

Instantly share code, notes, and snippets.

@manwithsteelnerves
Created September 24, 2025 10:08
Show Gist options
  • Save manwithsteelnerves/4acd7ada0013484375f762caaf46dd71 to your computer and use it in GitHub Desktop.
Save manwithsteelnerves/4acd7ada0013484375f762caaf46dd71 to your computer and use it in GitHub Desktop.
Script for checking 16KB page alignment (for Google Play Requirement)
#!/usr/bin/env bash
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# ============================= Helpers ======================================
get_arch_from_objdump() {
local so_file="$1"
local arch_line
arch_line=$(objdump -f "$so_file" 2>/dev/null | awk -F: '/architecture/ {print $2}' | tr '[:upper:]' '[:lower:]')
case "$arch_line" in
*aarch64*) echo "ARM64" ;;
*x86-64*) echo "x86_64" ;;
*i386*) echo "x86" ;;
*arm*) echo "ARM v7" ;;
*) echo "unknown" ;;
esac
}
check_so_alignment() {
local so_file="$1" relative_path="$2"
if [[ ! -f "$so_file" ]]; then
echo "ERROR|File not found|❌|0B|$relative_path|Unknown"
return 1
fi
local objdump_p
if ! objdump_p=$(objdump -p "$so_file" 2>/dev/null); then
echo "ERROR|objdump failed|❌|0B|$relative_path|Unknown"
return 1
fi
local arch
arch=$(get_arch_from_objdump "$so_file")
if [[ "$arch" == "ARM v7" ]]; then
echo "PASS|ARM v7 - 16KB not required|✅|N/A|$relative_path|$arch"
return 0
fi
local max_exp
max_exp=$(echo "$objdump_p" \
| grep -E "^ *LOAD" \
| grep -oE "align 2\*\*[0-9]+" \
| sed 's/align 2\*\*//' \
| sort -nr \
| head -1)
if [[ -z "$max_exp" ]]; then
echo "ERROR|Unknown alignment|❌|Unknown|$relative_path|$arch"
return 1
fi
local alignment_disp
if (( max_exp >= 10 )); then
local kb=$(( 1 << (max_exp - 10) ))
alignment_disp="${kb}KB"
else
local bytes=$(( 1 << max_exp ))
alignment_disp="${bytes}B"
fi
if (( max_exp >= 14 )); then
echo "PASS|>=16KB aligned|✅|${alignment_disp}|$relative_path|$arch"
return 0
else
echo "FAIL|Need 16KB alignment|❌|${alignment_disp}|$relative_path|$arch"
return 1
fi
}
# ============================= Table/UI ======================================
print_table_header() {
printf "${BLUE}%-8s %-50s %-12s %-12s %-s${NC}\n" "Status" "File Path" "Architecture" "Alignment" "Notes"
printf "%s\n" "$(printf '=%.0s' {1..120})"
}
print_table_row() {
local status="$1" filepath="$2" arch="$3" alignment="$4" notes="$5"
local color=""
case "$status" in
"✅") color="${GREEN}" ;;
"❌") color="${RED}" ;;
*) color="${NC}" ;;
esac
printf "${color}%-8s${NC} %-50s %-12s %-12s %-s\n" "$status" "$filepath" "$arch" "$alignment" "$notes"
}
process_results() {
local results=("$@")
local total_so=0 aligned_so=0 failed_so=0
local passes=() fails=()
for result in "${results[@]}"; do
[[ -z "$result" ]] && continue
total_so=$((total_so + 1))
IFS='|' read -r status_type notes symbol alignment filepath arch <<< "$result"
if [[ "$status_type" == "PASS" ]]; then
aligned_so=$((aligned_so + 1))
passes+=("$symbol|$filepath|$arch|$alignment|$notes")
else
failed_so=$((failed_so + 1))
fails+=("$symbol|$filepath|$arch|$alignment|$notes")
fi
done
# Print grouped table
if [[ ${#passes[@]} -gt 0 ]]; then
echo -e "\n${GREEN}✅ Compatible Libraries${NC}"
print_table_header
for row in "${passes[@]}"; do
IFS='|' read -r symbol filepath arch alignment notes <<< "$row"
print_table_row "$symbol" "$filepath" "$arch" "$alignment" "$notes"
done
fi
if [[ ${#fails[@]} -gt 0 ]]; then
echo -e "\n${RED}❌ Not Compatible Libraries${NC}"
print_table_header
for row in "${fails[@]}"; do
IFS='|' read -r symbol filepath arch alignment notes <<< "$row"
print_table_row "$symbol" "$filepath" "$arch" "$alignment" "$notes"
done
fi
echo ""
printf "%s\n" "$(printf '=%.0s' {1..120})"
echo -e "${BLUE}📊 SUMMARY${NC}"
echo "Total .so files: $total_so"
echo -e "16KB Aligned (>=): ${GREEN}$aligned_so${NC}"
echo -e "Not aligned: ${RED}$failed_so${NC}"
if [[ $failed_so -eq 0 ]]; then
echo -e "\n${GREEN}🎉 All .so files are properly aligned!${NC}"
else
echo -e "\n${RED}⚠️ Some .so files need alignment fixes${NC}"
fi
}
# ============================= Processors =====================================
process_apk() {
local apk_file="$1"
local temp_dir
echo -e "${BLUE}📱 Processing APK: $apk_file${NC}"
echo "================================================="
temp_dir=$(mktemp -d)
trap "rm -rf '$temp_dir'" RETURN
echo "🔓 Extracting APK..."
if ! unzip -q "$apk_file" -d "$temp_dir"; then
echo -e "${RED}❌ Failed to extract APK file${NC}"
return 1
fi
local results=()
while IFS= read -r -d '' so_file; do
local relative_path=${so_file#$temp_dir/}
results+=("$(check_so_alignment "$so_file" "$relative_path")")
done < <(find "$temp_dir" -type f -name "*.so" -print0)
if [[ ${#results[@]} -eq 0 ]]; then
echo -e "${YELLOW}⚠️ No .so files found in APK${NC}"
return 0
fi
process_results "${results[@]}"
}
process_single_so() {
local so_file="$1"
echo -e "${BLUE}🔍 Processing single .so file: $so_file${NC}"
echo "================================================="
local result
result=$(check_so_alignment "$so_file" "$(basename "$so_file")")
process_results "$result"
}
process_directory() {
local dir="$1"
echo -e "${BLUE}📁 Processing directory: $dir${NC}"
echo "================================================="
local results=()
while IFS= read -r -d '' so_file; do
local relative_path=${so_file#$dir/}
results+=("$(check_so_alignment "$so_file" "$relative_path")")
done < <(find "$dir" -type f -name "*.so" -print0)
if [[ ${#results[@]} -eq 0 ]]; then
echo -e "${YELLOW}⚠️ No .so files found in directory${NC}"
return 0
fi
process_results "${results[@]}"
}
show_usage() {
echo "Usage: $0 <file_or_directory>"
echo ""
echo "Examples:"
echo " $0 app.apk"
echo " $0 libfoo.so"
echo " $0 /path/to/lib/dir"
}
# ============================= Entry & Deps ===================================
check_dependencies() {
local missing=()
command -v objdump >/dev/null 2>&1 || missing+=("objdump (binutils)")
command -v unzip >/dev/null 2>&1 || missing+=("unzip")
if [[ ${#missing[@]} -gt 0 ]]; then
echo -e "${RED}❌ Missing dependencies:${NC}"
for dep in "${missing[@]}"; do echo " - $dep"; done
exit 1
fi
}
main() {
if [[ $# -eq 0 ]]; then
show_usage; exit 1
fi
local input="$1"
if [[ -f "$input" ]]; then
if [[ "$input" == *.apk ]]; then
process_apk "$input"
elif [[ "$input" == *.so ]]; then
process_single_so "$input"
else
echo -e "${RED}❌ Unsupported file type${NC}"; exit 1
fi
elif [[ -d "$input" ]]; then
process_directory "$input"
else
echo -e "${RED}❌ Invalid input${NC}"; exit 1
fi
}
check_dependencies
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment