xref: /aosp_15_r20/cts/tools/cfassembler/src/dxconvext/ClassFileAssembler.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1*b7c941bbSAndroid Build Coastguard Worker /*
2*b7c941bbSAndroid Build Coastguard Worker  * Copyright (C) 2008 The Android Open Source Project
3*b7c941bbSAndroid Build Coastguard Worker  *
4*b7c941bbSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*b7c941bbSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*b7c941bbSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*b7c941bbSAndroid Build Coastguard Worker  *
8*b7c941bbSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*b7c941bbSAndroid Build Coastguard Worker  *
10*b7c941bbSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*b7c941bbSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*b7c941bbSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*b7c941bbSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*b7c941bbSAndroid Build Coastguard Worker  * limitations under the License.
15*b7c941bbSAndroid Build Coastguard Worker  */
16*b7c941bbSAndroid Build Coastguard Worker 
17*b7c941bbSAndroid Build Coastguard Worker package dxconvext;
18*b7c941bbSAndroid Build Coastguard Worker 
19*b7c941bbSAndroid Build Coastguard Worker import dxconvext.util.FileUtils;
20*b7c941bbSAndroid Build Coastguard Worker 
21*b7c941bbSAndroid Build Coastguard Worker import java.io.BufferedOutputStream;
22*b7c941bbSAndroid Build Coastguard Worker import java.io.BufferedReader;
23*b7c941bbSAndroid Build Coastguard Worker import java.io.ByteArrayInputStream;
24*b7c941bbSAndroid Build Coastguard Worker import java.io.ByteArrayOutputStream;
25*b7c941bbSAndroid Build Coastguard Worker import java.io.File;
26*b7c941bbSAndroid Build Coastguard Worker import java.io.FileInputStream;
27*b7c941bbSAndroid Build Coastguard Worker import java.io.FileNotFoundException;
28*b7c941bbSAndroid Build Coastguard Worker import java.io.FileOutputStream;
29*b7c941bbSAndroid Build Coastguard Worker import java.io.IOException;
30*b7c941bbSAndroid Build Coastguard Worker import java.io.InputStreamReader;
31*b7c941bbSAndroid Build Coastguard Worker import java.io.OutputStream;
32*b7c941bbSAndroid Build Coastguard Worker import java.io.Reader;
33*b7c941bbSAndroid Build Coastguard Worker import java.io.UnsupportedEncodingException;
34*b7c941bbSAndroid Build Coastguard Worker import java.security.DigestException;
35*b7c941bbSAndroid Build Coastguard Worker import java.security.MessageDigest;
36*b7c941bbSAndroid Build Coastguard Worker import java.security.NoSuchAlgorithmException;
37*b7c941bbSAndroid Build Coastguard Worker import java.util.zip.Adler32;
38*b7c941bbSAndroid Build Coastguard Worker 
39*b7c941bbSAndroid Build Coastguard Worker public class ClassFileAssembler {
40*b7c941bbSAndroid Build Coastguard Worker 
41*b7c941bbSAndroid Build Coastguard Worker     /**
42*b7c941bbSAndroid Build Coastguard Worker      * @param args
43*b7c941bbSAndroid Build Coastguard Worker      */
main(String[] args)44*b7c941bbSAndroid Build Coastguard Worker     public static void main(String[] args) {
45*b7c941bbSAndroid Build Coastguard Worker         ClassFileAssembler cfa = new ClassFileAssembler();
46*b7c941bbSAndroid Build Coastguard Worker         cfa.run(args);
47*b7c941bbSAndroid Build Coastguard Worker     }
48*b7c941bbSAndroid Build Coastguard Worker 
run(String[] args)49*b7c941bbSAndroid Build Coastguard Worker     private void run(String[] args) {
50*b7c941bbSAndroid Build Coastguard Worker         // this class can be used to generate .class files that are somehow
51*b7c941bbSAndroid Build Coastguard Worker         // damaged in order to test the dalvik vm verifier.
52*b7c941bbSAndroid Build Coastguard Worker         // The input is a .cfh (class file hex) file.
53*b7c941bbSAndroid Build Coastguard Worker         // The output is a java vm .class file.
54*b7c941bbSAndroid Build Coastguard Worker         // The .cfh files can be generated as follows:
55*b7c941bbSAndroid Build Coastguard Worker         // 1. create the initial .cfh file from an existing .class files by using
56*b7c941bbSAndroid Build Coastguard Worker         //    the ClassFileParser
57*b7c941bbSAndroid Build Coastguard Worker         // 2. modify some bytes to damage the structure of the .class file in a
58*b7c941bbSAndroid Build Coastguard Worker         //    way that would not be possible with e.g. jasmin (otherwise you are
59*b7c941bbSAndroid Build Coastguard Worker         //    better off using jasmin).
60*b7c941bbSAndroid Build Coastguard Worker         //    Uncomment the original bytes, and write "MOD:" meaning a modified
61*b7c941bbSAndroid Build Coastguard Worker         // entry (with the original commented out)
62*b7c941bbSAndroid Build Coastguard Worker         //
63*b7c941bbSAndroid Build Coastguard Worker         // Use the ClassFileAssembler to generate the .class file.
64*b7c941bbSAndroid Build Coastguard Worker         // this class here simply takes all non-comment lines from the .cfh
65*b7c941bbSAndroid Build Coastguard Worker         // file, parses them as hex values and writes the bytes to the class file
66*b7c941bbSAndroid Build Coastguard Worker         File cfhF = new File(args[0]);
67*b7c941bbSAndroid Build Coastguard Worker         if (!cfhF.getName().endsWith(".cfh") &&
68*b7c941bbSAndroid Build Coastguard Worker             !cfhF.getName().endsWith(".dfh")) {
69*b7c941bbSAndroid Build Coastguard Worker             System.out.println("file must be a .cfh or .dfh file, and its filename end with .cfh or .dfh");
70*b7c941bbSAndroid Build Coastguard Worker             return;
71*b7c941bbSAndroid Build Coastguard Worker         }
72*b7c941bbSAndroid Build Coastguard Worker 
73*b7c941bbSAndroid Build Coastguard Worker         String outBase = args[1];
74*b7c941bbSAndroid Build Coastguard Worker 
75*b7c941bbSAndroid Build Coastguard Worker         boolean isDex = cfhF.getName().endsWith(".dfh");
76*b7c941bbSAndroid Build Coastguard Worker 
77*b7c941bbSAndroid Build Coastguard Worker         byte[] cfhbytes = FileUtils.readFile(cfhF);
78*b7c941bbSAndroid Build Coastguard Worker         ByteArrayInputStream bais = new ByteArrayInputStream(cfhbytes);
79*b7c941bbSAndroid Build Coastguard Worker         // encoding should not matter, since we are skipping comment lines and parsing
80*b7c941bbSAndroid Build Coastguard Worker         try {
81*b7c941bbSAndroid Build Coastguard Worker             // get the package name
82*b7c941bbSAndroid Build Coastguard Worker             BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(cfhF)));
83*b7c941bbSAndroid Build Coastguard Worker             String firstLine = br.readLine();
84*b7c941bbSAndroid Build Coastguard Worker             br.close();
85*b7c941bbSAndroid Build Coastguard Worker             String classHdr = "//@class:";
86*b7c941bbSAndroid Build Coastguard Worker             String dexHdr = "// Processing '";
87*b7c941bbSAndroid Build Coastguard Worker             String hdr;
88*b7c941bbSAndroid Build Coastguard Worker             if(isDex)
89*b7c941bbSAndroid Build Coastguard Worker                 hdr = dexHdr;
90*b7c941bbSAndroid Build Coastguard Worker             else
91*b7c941bbSAndroid Build Coastguard Worker                 hdr = classHdr;
92*b7c941bbSAndroid Build Coastguard Worker 
93*b7c941bbSAndroid Build Coastguard Worker             if (!firstLine.startsWith(hdr)) throw new RuntimeException("wrong format:"+firstLine +" isDex=" + isDex);
94*b7c941bbSAndroid Build Coastguard Worker             String tFile;
95*b7c941bbSAndroid Build Coastguard Worker             if(isDex) {
96*b7c941bbSAndroid Build Coastguard Worker                 tFile = outBase + "/classes.dex";
97*b7c941bbSAndroid Build Coastguard Worker             } else {
98*b7c941bbSAndroid Build Coastguard Worker                 String classO = firstLine.substring(hdr.length()).trim();
99*b7c941bbSAndroid Build Coastguard Worker                 tFile = outBase +"/"+classO+".class";
100*b7c941bbSAndroid Build Coastguard Worker             }
101*b7c941bbSAndroid Build Coastguard Worker             File outFile = new File(tFile);
102*b7c941bbSAndroid Build Coastguard Worker             System.out.println("outfile:" + outFile);
103*b7c941bbSAndroid Build Coastguard Worker             String mkdir = tFile.substring(0, tFile.lastIndexOf("/"));
104*b7c941bbSAndroid Build Coastguard Worker             new File(mkdir).mkdirs();
105*b7c941bbSAndroid Build Coastguard Worker 
106*b7c941bbSAndroid Build Coastguard Worker             Reader r = new InputStreamReader(bais,"utf-8");
107*b7c941bbSAndroid Build Coastguard Worker             OutputStream os = new FileOutputStream(outFile);
108*b7c941bbSAndroid Build Coastguard Worker             BufferedOutputStream bos = new BufferedOutputStream(os);
109*b7c941bbSAndroid Build Coastguard Worker             writeClassFile(r, bos, isDex);
110*b7c941bbSAndroid Build Coastguard Worker             bos.close();
111*b7c941bbSAndroid Build Coastguard Worker         } catch (UnsupportedEncodingException e) {
112*b7c941bbSAndroid Build Coastguard Worker             throw new RuntimeException("problem while parsing .dfh or .cfh file: "+cfhF.getAbsolutePath(), e);
113*b7c941bbSAndroid Build Coastguard Worker         } catch (FileNotFoundException e) {
114*b7c941bbSAndroid Build Coastguard Worker             throw new RuntimeException("problem while parsing .dfh or .cfh file: "+cfhF.getAbsolutePath(), e);
115*b7c941bbSAndroid Build Coastguard Worker         } catch (IOException e) {
116*b7c941bbSAndroid Build Coastguard Worker             throw new RuntimeException("problem while parsing .dfh or .cfh file: "+cfhF.getAbsolutePath(), e);
117*b7c941bbSAndroid Build Coastguard Worker         }
118*b7c941bbSAndroid Build Coastguard Worker     }
119*b7c941bbSAndroid Build Coastguard Worker 
120*b7c941bbSAndroid Build Coastguard Worker     /**
121*b7c941bbSAndroid Build Coastguard Worker      * Calculates the signature for the <code>.dex</code> file in the
122*b7c941bbSAndroid Build Coastguard Worker      * given array, and modify the array to contain it.
123*b7c941bbSAndroid Build Coastguard Worker      *
124*b7c941bbSAndroid Build Coastguard Worker      * Originally from com.android.dx.dex.file.DexFile.
125*b7c941bbSAndroid Build Coastguard Worker      *
126*b7c941bbSAndroid Build Coastguard Worker      * @param bytes non-null; the bytes of the file
127*b7c941bbSAndroid Build Coastguard Worker      */
calcSignature(byte[] bytes)128*b7c941bbSAndroid Build Coastguard Worker     private void calcSignature(byte[] bytes) {
129*b7c941bbSAndroid Build Coastguard Worker         MessageDigest md;
130*b7c941bbSAndroid Build Coastguard Worker 
131*b7c941bbSAndroid Build Coastguard Worker         try {
132*b7c941bbSAndroid Build Coastguard Worker             md = MessageDigest.getInstance("SHA-1");
133*b7c941bbSAndroid Build Coastguard Worker         } catch (NoSuchAlgorithmException ex) {
134*b7c941bbSAndroid Build Coastguard Worker             throw new RuntimeException(ex);
135*b7c941bbSAndroid Build Coastguard Worker         }
136*b7c941bbSAndroid Build Coastguard Worker 
137*b7c941bbSAndroid Build Coastguard Worker         md.update(bytes, 32, bytes.length - 32);
138*b7c941bbSAndroid Build Coastguard Worker 
139*b7c941bbSAndroid Build Coastguard Worker         try {
140*b7c941bbSAndroid Build Coastguard Worker             int amt = md.digest(bytes, 12, 20);
141*b7c941bbSAndroid Build Coastguard Worker             if (amt != 20) {
142*b7c941bbSAndroid Build Coastguard Worker                 throw new RuntimeException("unexpected digest write: " + amt +
143*b7c941bbSAndroid Build Coastguard Worker                                            " bytes");
144*b7c941bbSAndroid Build Coastguard Worker             }
145*b7c941bbSAndroid Build Coastguard Worker         } catch (DigestException ex) {
146*b7c941bbSAndroid Build Coastguard Worker             throw new RuntimeException(ex);
147*b7c941bbSAndroid Build Coastguard Worker         }
148*b7c941bbSAndroid Build Coastguard Worker     }
149*b7c941bbSAndroid Build Coastguard Worker 
150*b7c941bbSAndroid Build Coastguard Worker     /**
151*b7c941bbSAndroid Build Coastguard Worker      * Calculates the checksum for the <code>.dex</code> file in the
152*b7c941bbSAndroid Build Coastguard Worker      * given array, and modify the array to contain it.
153*b7c941bbSAndroid Build Coastguard Worker      *
154*b7c941bbSAndroid Build Coastguard Worker      * Originally from com.android.dx.dex.file.DexFile.
155*b7c941bbSAndroid Build Coastguard Worker      *
156*b7c941bbSAndroid Build Coastguard Worker      * @param bytes non-null; the bytes of the file
157*b7c941bbSAndroid Build Coastguard Worker      */
calcChecksum(byte[] bytes)158*b7c941bbSAndroid Build Coastguard Worker     private void calcChecksum(byte[] bytes) {
159*b7c941bbSAndroid Build Coastguard Worker         Adler32 a32 = new Adler32();
160*b7c941bbSAndroid Build Coastguard Worker 
161*b7c941bbSAndroid Build Coastguard Worker         a32.update(bytes, 12, bytes.length - 12);
162*b7c941bbSAndroid Build Coastguard Worker 
163*b7c941bbSAndroid Build Coastguard Worker         int sum = (int) a32.getValue();
164*b7c941bbSAndroid Build Coastguard Worker 
165*b7c941bbSAndroid Build Coastguard Worker         bytes[8]  = (byte) sum;
166*b7c941bbSAndroid Build Coastguard Worker         bytes[9]  = (byte) (sum >> 8);
167*b7c941bbSAndroid Build Coastguard Worker         bytes[10] = (byte) (sum >> 16);
168*b7c941bbSAndroid Build Coastguard Worker         bytes[11] = (byte) (sum >> 24);
169*b7c941bbSAndroid Build Coastguard Worker     }
170*b7c941bbSAndroid Build Coastguard Worker 
writeClassFile(Reader r, OutputStream rOs, boolean isDex)171*b7c941bbSAndroid Build Coastguard Worker     public void writeClassFile(Reader r, OutputStream rOs, boolean isDex) {
172*b7c941bbSAndroid Build Coastguard Worker         ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);
173*b7c941bbSAndroid Build Coastguard Worker         BufferedReader br = new BufferedReader(r);
174*b7c941bbSAndroid Build Coastguard Worker         String line;
175*b7c941bbSAndroid Build Coastguard Worker         String secondLine = null;
176*b7c941bbSAndroid Build Coastguard Worker         int lineCnt = 0;
177*b7c941bbSAndroid Build Coastguard Worker         try {
178*b7c941bbSAndroid Build Coastguard Worker             while ((line = br.readLine()) != null) {
179*b7c941bbSAndroid Build Coastguard Worker                 if (isDex && lineCnt++ == 1) {
180*b7c941bbSAndroid Build Coastguard Worker                     secondLine = line;
181*b7c941bbSAndroid Build Coastguard Worker                 }
182*b7c941bbSAndroid Build Coastguard Worker                 // skip it if it is a comment
183*b7c941bbSAndroid Build Coastguard Worker                 if (!line.trim().startsWith("//")) {
184*b7c941bbSAndroid Build Coastguard Worker                     // we have a row like "    ae 08 21 ff" etc.
185*b7c941bbSAndroid Build Coastguard Worker                     String[] parts = line.split("\\s+");
186*b7c941bbSAndroid Build Coastguard Worker                     for (int i = 0; i < parts.length; i++) {
187*b7c941bbSAndroid Build Coastguard Worker                         String part = parts[i].trim();
188*b7c941bbSAndroid Build Coastguard Worker                         if (!part.equals("")) {
189*b7c941bbSAndroid Build Coastguard Worker                             int res = Integer.parseInt(part, 16);
190*b7c941bbSAndroid Build Coastguard Worker                             baos.write(res);
191*b7c941bbSAndroid Build Coastguard Worker                         }
192*b7c941bbSAndroid Build Coastguard Worker                     }
193*b7c941bbSAndroid Build Coastguard Worker                 }
194*b7c941bbSAndroid Build Coastguard Worker             }
195*b7c941bbSAndroid Build Coastguard Worker 
196*b7c941bbSAndroid Build Coastguard Worker             // now for dex, update the checksum and the signature.
197*b7c941bbSAndroid Build Coastguard Worker             // special case:
198*b7c941bbSAndroid Build Coastguard Worker             // for two tests (currently T_f1_9.dfh and T_f1_10.dfh), we need
199*b7c941bbSAndroid Build Coastguard Worker             // to keep the checksum or the signature, respectively.
200*b7c941bbSAndroid Build Coastguard Worker             byte[] outBytes = baos.toByteArray();
201*b7c941bbSAndroid Build Coastguard Worker             if (isDex) {
202*b7c941bbSAndroid Build Coastguard Worker                 boolean leaveChecksum = secondLine.contains("//@leaveChecksum");
203*b7c941bbSAndroid Build Coastguard Worker                 boolean leaveSignature= secondLine.contains("//@leaveSignature");
204*b7c941bbSAndroid Build Coastguard Worker                 // update checksum and signature for dex file
205*b7c941bbSAndroid Build Coastguard Worker                 if(!leaveSignature)
206*b7c941bbSAndroid Build Coastguard Worker                     calcSignature(outBytes);
207*b7c941bbSAndroid Build Coastguard Worker                 if(!leaveChecksum)
208*b7c941bbSAndroid Build Coastguard Worker                     calcChecksum(outBytes);
209*b7c941bbSAndroid Build Coastguard Worker             }
210*b7c941bbSAndroid Build Coastguard Worker             rOs.write(outBytes);
211*b7c941bbSAndroid Build Coastguard Worker             rOs.close();
212*b7c941bbSAndroid Build Coastguard Worker         } catch (IOException e) {
213*b7c941bbSAndroid Build Coastguard Worker             throw new RuntimeException("problem while writing file",e);
214*b7c941bbSAndroid Build Coastguard Worker         }
215*b7c941bbSAndroid Build Coastguard Worker     }
216*b7c941bbSAndroid Build Coastguard Worker 
217*b7c941bbSAndroid Build Coastguard Worker }
218