/******************************************************************************* * Copyright (c) 2004, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.utils.xcoff; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.Vector; import org.eclipse.cdt.core.CCorePlugin; /** * The <code>AR</code> class is used for parsing standard XCOFF32 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 XCOFF32 object for performing XCOFF32 * class operations. * @see MemberHeader * * @author vhirsl */ public class AR { protected String filename; private RandomAccessFile file; private ARHeader header; private MemberHeader[] memberHeaders; /** * Content of an archive in AIX XCOFF32 format * * @author vhirsl */ public class ARHeader { private final static int SAIAMAG = 8; private byte[] fl_magic = new byte[SAIAMAG];// Archive magic string private byte[] fl_memoff = new byte[20]; // Offset to member table private byte[] fl_gstoff = new byte[20]; // Offset to global symbol table private byte[] fl_gst64off = new byte[20]; // Offset to global symbol table for 64-bit objects private byte[] fl_fstmoff = new byte[20]; // Offset to first archive member private byte[] fl_lstmoff = new byte[20]; // Offset to last archive member private byte[] fl_freeoff = new byte[20]; // Offset to first member on free list private long fstmoff = 0; private long lstmoff = 0; // private long memoff = 0; public ARHeader() throws IOException { try { RandomAccessFile file = getRandomAccessFile(); file.seek(0); file.read(fl_magic); if (isARHeader(fl_magic)) { file.read(fl_memoff); file.read(fl_gstoff); file.read(fl_gst64off); file.read(fl_fstmoff); file.read(fl_lstmoff); file.read(fl_freeoff); fstmoff = Long.parseLong(removeBlanks(new String(fl_fstmoff))); lstmoff = Long.parseLong(removeBlanks(new String(fl_lstmoff))); // memoff = Long.parseLong(removeBlanks(new String(fl_memoff))); } } catch (IOException e) { dispose(); CCorePlugin.log(e); } } /** * @return boolean */ public boolean isXcoffARHeader() { return (fstmoff != 0); } } /** * Creates a new <code>AR</code> object from the contents of * the given file. * * @param filename The file to process. * @throws IOException The file is not a valid archive. */ public AR(String filename) throws IOException { this.filename = filename; file = new RandomAccessFile(filename, "r"); //$NON-NLS-1$ header = new ARHeader(); if (!header.isXcoffARHeader()) { file.close(); throw new IOException(CCorePlugin.getResourceString("Util.exception.invalidArchive")); //$NON-NLS-1$ } } public void dispose() { try { if (file != null) { file.close(); file = null; } } catch (IOException e) { } } @Override protected void finalize() throws Throwable { try { dispose(); } finally { super.finalize(); } } public static boolean isARHeader(byte[] ident) { if (ident == null || ident.length < 8 || ident[0] != '<' || ident[1] != 'b' || ident[2] != 'i' || ident[3] != 'g' || ident[4] != 'a' || ident[5] != 'f' || ident[6] != '>' || ident[7] != '\n') return false; return true; } /** * The <code>ARHeader</code> class is used to store the per-object file * archive headers. It can also create an XCOFF32 object for inspecting * the object file data. */ public class MemberHeader { byte[] ar_size = new byte[20]; // File member size - decimal byte[] ar_nxtmem = new byte[20]; // Next member offset - decimal byte[] ar_prvmem = new byte[20]; // Previous member offset - decimal byte[] ar_date = new byte[12]; // File member date - decimal byte[] ar_uid = new byte[12]; // File member userid - decimal byte[] ar_gid = new byte[12]; // File member group id - decimal byte[] ar_mode = new byte[12]; // File member mode - octal byte[] ar_namlen = new byte[4]; // File member name length -decimal byte[] ar_name; // Start of member name byte[] ar_fmag = new byte[2]; // AIAFMAG - string to end "`\n" // converted values long size; long nxtmem; long prvmem; int namlen; String name; long file_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 <code>file</code> file location. * * @param offset * Offset into the string table for first character of the name. * @throws IOException * <code>offset</code> not in string table bounds. */ // private String nameFromStringTable(long offset) throws IOException { // StringBuffer name = new StringBuffer(0); // long pos = file.getFilePointer(); // // try { // if (strtbl_pos != -1) { // byte temp; // file.seek(strtbl_pos + offset); // while ((temp = file.readByte()) != '\n') // name.append((char) temp); // } // } finally { // file.seek(pos); // } // // return name.toString(); // } /** * Creates a new archive header object. * * Assumes that file is already at the correct location in the file. * * @throws IOException * There was an error processing the header data from the file. */ public MemberHeader() throws IOException { // // Read in the archive header data. Fixed sizes. // RandomAccessFile file = getRandomAccessFile(); file.read(ar_size); file.read(ar_nxtmem); file.read(ar_prvmem); file.read(ar_date); file.read(ar_uid); file.read(ar_gid); file.read(ar_mode); file.read(ar_namlen); namlen = Integer.parseInt(removeBlanks(new String(ar_namlen))); ar_name = new byte[namlen]; file.read(ar_name); file.read(ar_fmag); size = Long.parseLong(removeBlanks(new String(ar_size))); nxtmem = Long.parseLong(removeBlanks(new String(ar_nxtmem))); prvmem = Long.parseLong(removeBlanks(new String(ar_prvmem))); name = new String(ar_name, 0, namlen); // // Save this location so we can create the XCOFF32 object later. // file_offset = file.getFilePointer(); if ((file_offset % 2) == 1) { ++file_offset; } } /** Get the name of the object file */ public String getObjectName() { return name; } /** Get the size of the object file . */ public long getSize() { return size; } public String getArchiveName() { return filename; } public long getObjectDataOffset() { return file_offset; } public byte[] getObjectData() throws IOException { byte[] temp = new byte[(int) size]; RandomAccessFile file = getRandomAccessFile(); file.seek(file_offset); file.read(temp); dispose(); return temp; } } /** Load the headers from the file (if required). */ private void loadHeaders() throws IOException { if (memberHeaders != null) return; Vector<MemberHeader> v = new Vector<MemberHeader>(); try { // // Check for EOF condition // MemberHeader aHeader; for (long pos = header.fstmoff; pos < file.length(); pos = aHeader.nxtmem) { file.seek(pos); aHeader = new MemberHeader(); v.add(aHeader); if (pos == 0 || pos == header.lstmoff) { // end of double linked list break; } } } catch (IOException e) { } memberHeaders = v.toArray(new MemberHeader[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 MemberHeader[] getHeaders() throws IOException { loadHeaders(); return memberHeaders; } public String[] extractFiles(String outdir, String[] names) throws IOException { Vector<String> names_used = new Vector<String>(); String object_name; int count; loadHeaders(); count = 0; for (MemberHeader memberHeader : memberHeaders) { object_name = memberHeader.getObjectName(); if (names != null && !stringInStrings(object_name, names)) continue; object_name = "" + count + "_" + object_name; //$NON-NLS-1$ //$NON-NLS-2$ count++; byte[] data = memberHeader.getObjectData(); File output = new File(outdir, object_name); names_used.add(object_name); RandomAccessFile rfile = new RandomAccessFile(output, "rw"); //$NON-NLS-1$ rfile.write(data); rfile.close(); } return names_used.toArray(new String[0]); } private boolean stringInStrings(String str, String[] set) { for (String element : set) if (str.compareTo(element) == 0) return true; return false; } /** * Remove the padding from the archive header strings. */ protected String removeBlanks(String str) { while (str.charAt(str.length() - 1) == ' ') str = str.substring(0, str.length() - 1); return str; } public String[] extractFiles(String outdir) throws IOException { return extractFiles(outdir, null); } protected RandomAccessFile getRandomAccessFile () throws IOException { if (file == null) { file = new RandomAccessFile(filename, "r"); //$NON-NLS-1$ } return file; } public static void main(String[] args) { try { AR ar = new AR(args[0]); ar.getHeaders(); ar.extractFiles(args[1]); System.out.println(ar); } catch (IOException e) { e.printStackTrace(); } } }