1#!/bin/bash 2# Copyright (C) 2023 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16set -e 17 18 19help() { 20 cat <<'EOF' 21 22 dump-jar: Dump java classes in jar files 23 24 Usage: 25 dump-jar [-v] CLASS-FILE [...] 26 27 Dump a *.class file 28 29 dump-jar [-v] [-s] [-o OUTPUT-FILENAME] JAR-FILE[: class internal name regex] [...] 30 31 Dump a jar file. 32 33 If a filename contains a ':', then the following part 34 will be used to filter files in the jar file that matches against class internal names. 35 36 For example, "file.jar:/MyClass$" will only dump "MyClass" in file.jar. 37 38 Options: 39 -v: Enable verbose output. 40 41 -s: Simple output mode, used to check HostStubGen output jars. 42 43 -o: Write the output to a specified file. 44EOF 45} 46 47# Parse the options. 48 49verbose=0 50simple=0 51output="" 52while getopts "hvso:" opt; do 53case "$opt" in 54 h) 55 help 56 exit 0 57 ;; 58 v) 59 verbose=1 60 ;; 61 s) 62 simple=1 63 ;; 64 o) 65 output="$OPTARG" 66 ;; 67 '?') 68 help 69 exit 1 70 ;; 71esac 72done 73shift $(($OPTIND - 1)) 74 75JAVAP_OPTS="${JAVAP_OPTS:--v -p -s -sysinfo -constants}" 76 77if (( $simple )) ; then 78 JAVAP_OPTS="-p -c -v" 79fi 80 81# Convert the output for `-s` as needed. 82filter_output() { 83 if (( $simple )) ; then 84 # For "simple output" mode, 85 # - Normalize the constant numbers (replace with "#x") 86 # - Normalize byte code offsets and other similar numbers. (e.g. "0:" -> "x:") 87 # - Remove the constant pool 88 # - Remove the line number table 89 # - Some other transient lines 90 # - Sometimes the javap shows mysterious warnings, so remove them too. 91 # 92 # Most conversion are simple per-line deletion or simple inline replacement with a regex. 93 # 94 # But removing the constant pool is a bit tricky. It looks like this in the output: 95 #--------------------------- 96 #Constant pool: 97 # #1 = Methodref #31.#88 // java/lang/Object."<init>":()V 98 # #2 = Class #89 // java/lang/UnsupportedOperationException 99 # : 100 #{ // Or something, I'm not sure if it always ends with a "{". 101 #--------------------------- 102 # i.e. we want to delete all lines from "Constant pool:" as long as the first character 103 # is a space. 104 # 105 # If we simply use '/^Constant pool:/,/^[^ ]/d', then it'll delete the "Constant pool:" 106 # line and "{" line too, but again the last line might be important, so we don't want to 107 # delete it. 108 # 109 # So we instead, use '/^Constant pool:/,/^[^ ]/{/^ /d}', which mean: 110 # between lines matching '/^Constant pool:/' and '/^[^ ]/', delete lines that start with 111 # a space. (=='/^ /d'). 112 # 113 sed -e 's/#[0-9][0-9]*/#x/g' \ 114 -e 's/^\( *\)[0-9][0-9]*:/\1x:/' \ 115 -e '/^Constant pool:/,/^[^ ]/{/^ /d}' \ 116 -e '/^ *line *[0-9][0-9]*: *[0-9][0-9]*$/d' \ 117 -e '/^ *SHA-256 checksum/d' \ 118 -e '/^ *Last modified/d' \ 119 -e '/^Classfile jar/d' \ 120 -e '/\[warning\]/d' 121 else 122 cat # Print as-is. 123 fi 124} 125 126# Write to the output file (specified with -o) as needed. 127write_to_out() { 128 if [[ -n "$output" ]] ; then 129 cat >"$output" 130 echo "Wrote output to $output" 1>&2 131 else 132 cat # print to stdout 133 fi 134} 135 136# Read jar file names and remove the .class suffix. 137# Also remove non-class files. 138to_internal_names() { 139 sed -ne 's/\.class$//p' 140} 141 142for file in "${@}"; do 143 144 # *.class? 145 if echo "$file" | grep -qE '\.class$' ; then 146 echo "# Class: $file" 1>&2 147 javap $dump_code_opt $JAVAP_OPTS $file 148 149 # *.jar? 150 elif echo "$file" | grep -qE '\.jar(:.*)?$' ; then 151 # Take the regex. Remove everything up to : in $file 152 regex="" 153 if [[ "$file" =~ : ]] ; then 154 regex="${file##*:}" 155 fi 156 157 # Remove everything after ':', inclusively, in $file. 158 file="${file%:*}" 159 160 # Print the filename and the regex. 161 if ! (( $simple )) ; then 162 echo -n "# Jar: $file" 163 if [[ "$regex" != "" ]] ;then 164 echo -n " (regex: $regex)" 165 fi 166 echo 167 fi 168 169 jar tf "$file" | sort | to_internal_names | grep -- "$regex" | while read -r class ; do 170 echo "## Class: $class.class" 171 javap $dump_code_opt $JAVAP_OPTS -cp "$file" "${class}" 172 done 173 174 else 175 echo "Unknown file type: $file" 1>&2 176 exit 1 177 fi 178done | filter_output | write_to_out 179