/*
 * SectionHeader.java - This file is part of the Jakstab project.
 * 
 * Copyright 2007-2015 Johannes Kinder <jk@jakstab.org>
 * Copyright (C) 2003 The University of Arizona
 *
 * The original code for this class was taken from "MBEL: The Microsoft 
 * Bytecode Engineering Library" and modified for use with Jakstab.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
 */

package org.jakstab.loader.pe;

import org.jakstab.util.BinaryInputBuffer;
import org.jakstab.util.Logger;

/** 
 * This is the structure used to parse a PE/COFF Section Header.
 * Sections must appear in ascending order of virtual address,
 * and must be consecutive in the virtual address space.
 * Their virtual addresses must be multiples of SectionAlignment
 * in the PE header.

 * Also, the PointerToRawData and SizeOfRawData fields must be multiples of 
 * FileAlignment in the PE header.
 * If (SectionAlignment < PageSize) then
 *    PointerToRawData == VirtualAddress
 * @author Michael Stepp
 */
class SectionHeader {
	private final static Logger logger = Logger.getLogger(SectionHeader.class);
	public static final int STRUCT_SIZE = 40;

	///// Flags for 'Characteristics' field //////////////////////////////
	public static final int IMAGE_SCN_SCALE_INDEX            = 0x00000001;
	public static final int IMAGE_SCN_CNT_CODE               = 0x00000020;
	public static final int IMAGE_SCN_CNT_INITIALIZED_DATA   = 0x00000040;
	public static final int IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080;
	public static final int IMAGE_SCN_LNK_INFO               = 0x00000200;
	public static final int IMAGE_SCN_LNK_REMOVE             = 0x00000800;
	public static final int IMAGE_SCN_LNK_COMDAT             = 0x00001000;
	public static final int IMAGE_SCN_GPREL                  = 0x00008000;
	public static final int IMAGE_SCN_NO_DEFER_SPEC_EXC      = 0x00004000;
	public static final int IMAGE_SCN_LNK_NRELOC_OVFL        = 0x01000000;
	public static final int IMAGE_SCN_MEM_DISCARDABLE        = 0x02000000;
	public static final int IMAGE_SCN_MEM_NOT_CACHED         = 0x04000000;
	public static final int IMAGE_SCN_MEM_NOT_PAGED          = 0x08000000;
	public static final int IMAGE_SCN_MEM_SHARED             = 0x10000000;
	public static final int IMAGE_SCN_MEM_EXECUTE            = 0x20000000;
	public static final int IMAGE_SCN_MEM_READ               = 0x40000000;
	public static final int IMAGE_SCN_MEM_WRITE              = 0x80000000;
	//////////////////////////////////////////////////////////////////////

	private final byte NameArray[];                 // 8bytes
	private final String name;
	// Virtual size and address are not final to allow relocation of sections in objs
	public long VirtualSize;            // 4bytes
	public long VirtualAddress;         // 4bytes (really an RVA)
	public final long SizeOfRawData;          // 4bytes
	public final long PointerToRawData;       // 4bytes
	public final long PointerToRelocations;   // 4bytes (file pointer) (object only)
	public final long PointerToLinenumbers;   // 4bytes (file pointer)
	public final int NumberOfRelocations;     // 2bytes
	public final int NumberOfLinenumbers;     // 2bytes
	public final long Characteristics;        // 4bytes
	public final long startFP;

	/** 
	 * Parses a Section Header from an input stream
	 */
	public SectionHeader(BinaryInputBuffer in) throws java.io.IOException{
		startFP = in.getCurrent();

		NameArray = new byte[8];
		in.read(NameArray);
		
		StringBuilder nBuilder = new StringBuilder();
		for (int i=0;i<8;i++){
			if ((NameArray[i]&0xFF)>32 && (NameArray[i]&0xFF)<128)
				nBuilder.append((char)(NameArray[i]&0xFF));
		}
		name = nBuilder.toString();

		VirtualSize                   = in.readDWORD();
		VirtualAddress                = in.readDWORD();
		SizeOfRawData                 = in.readDWORD();
		PointerToRawData              = in.readDWORD();
		PointerToRelocations          = in.readDWORD();
		PointerToLinenumbers          = in.readDWORD();
		NumberOfRelocations           = in.readWORD();
		NumberOfLinenumbers           = in.readWORD();
		Characteristics               = in.readDWORD();
		//output();
	}

	public boolean isCodeSection() {
		return (Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE)) > 0;
	}
	
	public boolean isReadOnlySection() {
		return (Characteristics & IMAGE_SCN_MEM_READ) > 0 && (Characteristics & IMAGE_SCN_MEM_WRITE) == 0; 
	}
	
	public boolean hasComdat() {
		return (Characteristics & IMAGE_SCN_LNK_COMDAT) > 0;
	}
	
	public boolean isRemovedByLinker() {
		return (Characteristics & IMAGE_SCN_LNK_REMOVE) > 0;
	}
	
	public boolean isDiscardable() {
		return (Characteristics & IMAGE_SCN_MEM_DISCARDABLE) > 0;
	}
	
	public String getName() {
		return name;
	}

	public void output(){
		logger.debug("SectionHeader: {");
		logger.debug("  Name = '" + getName() + "'");
		logger.debug("  VirtualSize = " + "0x" + Long.toHexString(VirtualSize));
		logger.debug("  VirtualAddress = 0x" + Long.toHexString(VirtualAddress));
		logger.debug("  SizeOfRawData = 0x" + Long.toHexString(SizeOfRawData));
		logger.debug("  PointerToRawData = " + "0x" + Long.toHexString(PointerToRawData));
		logger.debug("  PointerToRelocations = " + "0x" + Long.toHexString(PointerToRelocations));
		logger.debug("  PointerToLinenumbers = " + "0x" + Long.toHexString(PointerToLinenumbers));
		logger.debug("  NumberOfRelocations = " + NumberOfRelocations);
		logger.debug("  NumberOfLinenumbers = " + NumberOfLinenumbers);
		logger.debug("  Characteristics = 0x" + Long.toHexString(Characteristics));
		logger.debug("}");
	}
}
