diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/PEArchive.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/PEArchive.java new file mode 100644 index 00000000000..37b1e21bb99 --- /dev/null +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/PEArchive.java @@ -0,0 +1,315 @@ +package org.eclipse.cdt.utils.coff; + +/* + * (c) Copyright QNX Software Systems Ltd. 2002. + * All Rights Reserved. + */ + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Vector; + +/** + * The AR class is used for parsing standard ELF archive (ar) files. + * + * Each object within the archive is represented by an ARHeader class. Each of + * of these objects can then be turned into an PE object for performing PE + * class operations. + * @see ARHeader + */ +public class PEArchive { + + protected String filename; + protected RandomAccessFile rfile; + protected long strtbl_pos = -1; + private ARHeader[] headers; + + public void dispose() { + try { + if (rfile != null) { + rfile.close(); + rfile = null; + } + } catch (IOException e) { + } + } + + /** + * Do not leak fds. + */ + protected void finalize() throws Throwable { + try { + dispose(); + } finally { + super.finalize(); + } + } + + /** + * The ARHeader class is used to store the per-object file + * archive headers. It can also create an PE object for inspecting + * the object file data. + */ + public class ARHeader { + + private String object_name; + private String modification_time; + private String uid; + private String gid; + private String mode; + private long size; + private long elf_offset; + + /** + * Remove the padding from the archive header strings. + */ + private String removeBlanks(String str) { + while (str.charAt(str.length() - 1) == ' ') + str = str.substring(0, str.length() - 1); + return str; + } + + /** + * Look up the name stored in the archive's string table based + * on the offset given. + * + * Maintains rfile file location. + * + * @param offset + * Offset into the string table for first character of the name. + * @throws IOException + * offset not in string table bounds. + */ + private String nameFromStringTable(long offset) throws IOException { + StringBuffer name = new StringBuffer(0); + long pos = rfile.getFilePointer(); + + try { + if (strtbl_pos != -1) { + byte temp; + rfile.seek(strtbl_pos + offset); + while ((temp = rfile.readByte()) != '\n') + name.append((char) temp); + } + } finally { + rfile.seek(pos); + } + + return name.toString(); + } + + /** + * Creates a new archive header object. + * + * Assumes that rfile is already at the correct location in the file. + * + * @throws IOException + * There was an error processing the header data from the file. + */ + public ARHeader() throws IOException { + byte[] object_name = new byte[16]; + byte[] modification_time = new byte[12]; + byte[] uid = new byte[6]; + byte[] gid = new byte[6]; + byte[] mode = new byte[8]; + byte[] size = new byte[10]; + byte[] trailer = new byte[2]; + + // + // Read in the archive header data. Fixed sizes. + // + rfile.read(object_name); + rfile.read(modification_time); + rfile.read(uid); + rfile.read(gid); + rfile.read(mode); + rfile.read(size); + rfile.read(trailer); + + // + // Save this location so we can create the PE object later. + // + elf_offset = rfile.getFilePointer(); + + // + // Convert the raw bytes into strings and numbers. + // + this.object_name = removeBlanks(new String(object_name)); + this.modification_time = new String(modification_time); + this.uid = new String(uid); + this.gid = new String(gid); + this.mode = new String(mode); + this.size = Long.parseLong(removeBlanks(new String(size))); + + // + // If the name is of the format "/", get name from the + // string table. + // + if (strtbl_pos != -1 + && this.object_name.length() > 1 + && this.object_name.charAt(0) == '/') { + try { + long offset = Long.parseLong(this.object_name.substring(1)); + this.object_name = nameFromStringTable(offset); + } catch (java.lang.Exception e) { + } + } + + // + // Strip the trailing / from the object name. + // + int len = this.object_name.length(); + if (len > 2 && this.object_name.charAt(len - 1) == '/') { + this.object_name = this.object_name.substring(0, len - 1); + } + + } + + /** Get the name of the object file */ + public String getObjectName() { + return object_name; + } + + /** Get the size of the object file . */ + public long getSize() { + return size; + } + + /** + * Create an new PE object for the object file. + * + * @throws IOException + * Not a valid PE object file. + * @return A new PE object. + * @see PE#PE( String, long ) + */ + public PE getPE() throws IOException { + return new PE(filename, elf_offset); + } + + public PE getPE(boolean filter_on) throws IOException { + return new PE(filename, elf_offset, filter_on); + } + + public byte[] getObjectData() throws IOException { + byte[] temp = new byte[(int) size]; + rfile.seek(elf_offset); + rfile.read(temp); + return temp; + } + } + + /** + * Creates a new AR object from the contents of + * the given file. + * + * @param filename The file to process. + * @throws IOException The file is not a valid archive. + */ + public PEArchive(String filename) throws IOException { + this.filename = filename; + rfile = new RandomAccessFile(filename, "r"); + String hdr = rfile.readLine(); + if (hdr == null || hdr.compareTo("!") != 0) { + rfile.close(); + throw new IOException("Not a valid archive file."); + } + } + + /** Load the headers from the file (if required). */ + private void loadHeaders() throws IOException { + if (headers != null) + return; + + Vector v = new Vector(); + try { + // + // Check for EOF condition + // + while (rfile.getFilePointer() < rfile.length()) { + ARHeader header = new ARHeader(); + String name = header.getObjectName(); + + long pos = rfile.getFilePointer(); + + // + // If the name starts with a / it is specical. + // + if (name.charAt(0) != '/') + v.add(header); + + // + // If the name is "//" then this is the string table section. + // + if (name.compareTo("//") == 0) + strtbl_pos = pos; + + // + // Compute the location of the next header in the archive. + // + pos += header.getSize(); + if ((pos % 2) != 0) + pos++; + + rfile.seek(pos); + } + } catch (IOException e) { + } + headers = (ARHeader[]) v.toArray(new ARHeader[0]); + } + + /** + * Get an array of all the object file headers for this archive. + * + * @throws IOException + * Unable to process the archive file. + * @return An array of headers, one for each object within the archive. + * @see ARHeader + */ + public ARHeader[] getHeaders() throws IOException { + loadHeaders(); + return headers; + } + + private boolean stringInStrings(String str, String[] set) { + for (int i = 0; i < set.length; i++) + if (str.compareTo(set[i]) == 0) + return true; + return false; + } + + public String[] extractFiles(String outdir, String[] names) + throws IOException { + Vector names_used = new Vector(); + String object_name; + int count; + + loadHeaders(); + + count = 0; + for (int i = 0; i < headers.length; i++) { + object_name = headers[i].getObjectName(); + if (names != null && !stringInStrings(object_name, names)) + continue; + + object_name = "" + count + "_" + object_name; + count++; + + byte[] data = headers[i].getObjectData(); + File output = new File(outdir, object_name); + names_used.add(object_name); + + RandomAccessFile rfile = new RandomAccessFile(output, "rw"); + rfile.write(data); + rfile.close(); + } + + return (String[]) names_used.toArray(new String[0]); + } + + public String[] extractFiles(String outdir) throws IOException { + return extractFiles(outdir, null); + } + +}