• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package com.ohos.hapsigntool.codesigning.sign;
17 
18 import com.ohos.hapsigntool.codesigning.datastructure.CodeSignBlock;
19 import com.ohos.hapsigntool.codesigning.datastructure.PageInfoExtension;
20 import com.ohos.hapsigntool.codesigning.elf.ElfFile;
21 import com.ohos.hapsigntool.codesigning.elf.ElfProgramHeader;
22 import com.ohos.hapsigntool.codesigning.exception.CodeSignErrMsg;
23 import com.ohos.hapsigntool.codesigning.exception.ElfFormatException;
24 import com.ohos.hapsigntool.codesigning.utils.NumberUtils;
25 import com.ohos.hapsigntool.error.HapFormatException;
26 import com.ohos.hapsigntool.utils.FileUtils;
27 import com.ohos.hapsigntool.utils.LogUtils;
28 import com.ohos.hapsigntool.zip.EntryType;
29 import com.ohos.hapsigntool.zip.Zip;
30 import com.ohos.hapsigntool.zip.ZipEntry;
31 import com.ohos.hapsigntool.zip.ZipEntryHeader;
32 
33 import java.io.File;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.nio.ByteBuffer;
37 import java.nio.ByteOrder;
38 import java.util.ArrayList;
39 import java.util.BitSet;
40 import java.util.LinkedHashMap;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.jar.JarEntry;
44 import java.util.jar.JarFile;
45 
46 /**
47  * pages info bitmap generator
48  *
49  * @since 2024/07/01
50  */
51 public class PageInfoGenerator {
52     private static final byte ABC_M_CODE = 2;
53 
54     private static final byte ELF_M_CODE = 1;
55 
56     private static final LogUtils LOGGER = new LogUtils(PageInfoGenerator.class);
57 
58     private long maxEntryDataOffset = 0L;
59 
60     private final List<ExcSegment> excSegmentList = new ArrayList<>();
61 
62     /**
63      * Constructor for PageInfoGenerator
64      *
65      * @param zip zip
66      * @throws IOException io error
67      * @throws HapFormatException hap file format error
68      * @throws ElfFormatException ElfFormatException
69      */
PageInfoGenerator(Zip zip)70     public PageInfoGenerator(Zip zip) throws IOException, HapFormatException, ElfFormatException {
71         Map<String, Long> runnableFileNames = new LinkedHashMap<>();
72         List<ZipEntry> zipEntries = zip.getZipEntries();
73         for (ZipEntry entry : zipEntries) {
74             ZipEntryHeader zipEntryHeader = entry.getZipEntryData().getZipEntryHeader();
75             long entryDataOffset = entry.getCentralDirectory().getOffset() + ZipEntryHeader.HEADER_LENGTH
76                 + zipEntryHeader.getFileNameLength() + zipEntryHeader.getExtraLength();
77             if (!NumberUtils.isMultiple4K(entryDataOffset)) {
78                 throw new HapFormatException(CodeSignErrMsg.FILE_4K_ALIGNMENT_ERROR.toString(entryDataOffset));
79             }
80             if (EntryType.RUNNABLE_FILE.equals(entry.getZipEntryData().getType())
81                     && Zip.FILE_UNCOMPRESS_METHOD_FLAG == entry.getZipEntryData().getZipEntryHeader().getMethod()) {
82                 runnableFileNames.put(zipEntryHeader.getFileName(), entryDataOffset);
83                 continue;
84             }
85             maxEntryDataOffset = entryDataOffset;
86             break;
87         }
88         File input = new File(zip.getFile());
89         try (JarFile hap = new JarFile(input, false)) {
90             for (Map.Entry<String, Long> en : runnableFileNames.entrySet()) {
91                 this.libExecSegment(hap, en.getKey(), en.getValue());
92             }
93         }
94     }
95 
libExecSegment(JarFile hap, String libFileName, long entryDataOffset)96     private void libExecSegment(JarFile hap, String libFileName, long entryDataOffset)
97         throws IOException, ElfFormatException {
98         JarEntry libEntry = hap.getJarEntry(libFileName);
99         if (libFileName.endsWith(FileUtils.ABC_FILE_SUFFIX)) {
100             long size = libEntry.getSize();
101             excSegmentList.add(new ExcSegment(ABC_M_CODE, libFileName, entryDataOffset, entryDataOffset + size));
102         } else {
103             try (InputStream stream = hap.getInputStream(libEntry)) {
104                 ElfFile elfFile = new ElfFile(stream);
105                 if (!elfFile.isElfFile()) {
106                     LOGGER.info("{} not ELF file", libFileName);
107                     return;
108                 }
109                 List<ElfProgramHeader> elfPHeaders = elfFile.filterExecPHeaders();
110                 for (ElfProgramHeader programHeader : elfPHeaders) {
111                     long pOffset = programHeader.getPOffset();
112                     long pFilesz = programHeader.getPFilesz();
113                     long off = entryDataOffset + pOffset;
114                     long endoff = off + pFilesz;
115                     excSegmentList.add(new ExcSegment(ELF_M_CODE, libFileName, off, endoff));
116                 }
117             }
118         }
119     }
120 
121     /**
122      * generate bitMap
123      *
124      * @return byte array of bitmap
125      * @throws HapFormatException hap format error
126      */
generateBitMap()127     public byte[] generateBitMap() throws HapFormatException {
128         if (excSegmentList.isEmpty()) {
129             return new byte[0];
130         }
131         if (!NumberUtils.isMultiple4K(maxEntryDataOffset)) {
132             throw new HapFormatException(CodeSignErrMsg.FILE_4K_ALIGNMENT_ERROR.toString(maxEntryDataOffset));
133         }
134         int len = (int) (maxEntryDataOffset / CodeSignBlock.PAGE_SIZE_4K * PageInfoExtension.DEFAULT_UNIT_SIZE);
135         BitSet bitmap = new BitSet(len);
136         for (ExcSegment es : excSegmentList) {
137             int begin = (int) (es.getStartOffset() >> 12) * PageInfoExtension.DEFAULT_UNIT_SIZE;
138             int end = (NumberUtils.isMultiple4K(es.getEndOffset()))
139                 ? (int) ((es.getEndOffset() >> 12)) * PageInfoExtension.DEFAULT_UNIT_SIZE
140                 : (int) ((es.getEndOffset() >> 12) + 1) * PageInfoExtension.DEFAULT_UNIT_SIZE;
141             for (int i = begin; i < end; i = i + PageInfoExtension.DEFAULT_UNIT_SIZE) {
142                 if ((ELF_M_CODE == es.getType())) {
143                     bitmap.set(i);
144                 } else {
145                     bitmap.set(i + 1);
146                 }
147             }
148         }
149         long[] longArray = bitmap.toLongArray();
150         int byteLen = bitmap.size() / Byte.SIZE;
151         ByteBuffer buffer = ByteBuffer.allocate(byteLen).order(ByteOrder.LITTLE_ENDIAN);
152         for (long l : longArray) {
153             buffer.putLong(l);
154         }
155         return buffer.array();
156     }
157 
158     static class ExcSegment {
159         /**
160          * abc or elf
161          */
162         private byte type;
163 
164         private String fileName;
165 
166         private long startOffset;
167 
168         private long endOffset;
169 
ExcSegment(byte type, String fileName, long startOffset, long endOffset)170         ExcSegment(byte type, String fileName, long startOffset, long endOffset) {
171             this.type = type;
172             this.fileName = fileName;
173             this.startOffset = startOffset;
174             this.endOffset = endOffset;
175         }
176 
getType()177         public byte getType() {
178             return type;
179         }
180 
getFileName()181         public String getFileName() {
182             return fileName;
183         }
184 
getStartOffset()185         public long getStartOffset() {
186             return startOffset;
187         }
188 
getEndOffset()189         public long getEndOffset() {
190             return endOffset;
191         }
192     }
193 }
194