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