/* sections.c (was subsegs.c in original GAS version)
   Copyright (C) 1987 Free Software Foundation, Inc.

This file is part of GAS, the GNU Assembler.

GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.

GAS 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 for more details.

You should have received a copy of the GNU General Public License
along with GAS; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* FROM line 25 */
#include "as.h"

/*
 * Mach-O sections are chains of fragments.
 */
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include "sections.h"
#include "obstack.h"
#include "xmalloc.h"
#include "frags.h"
#include "messages.h"
#include "symbols.h"

/*
 * All sections' chains hang off here.  NULL means no frchains yet.
 */
frchainS *frchain_root = NULL;

/*
 * The frchain we are assembling into now.  That is, the current section's
 * frag chain, even if it contains no (complete) frags.
 */
frchainS *frchain_now = NULL;

/*
 * The variables now_seg and now_subseg are defined here and used slightly
 * differently than in GAS so it works with Mach-O files and the code generating
 * dwarf debug info.
 *
 * The variable now_seg contains the current section number that is what is
 * stored in the struct frchain's field frch_nsect.  And is set by section_new()
 * in here and also used in layout.c.  The variable now_subseg always remains
 * zero.  And is defined to minimize changes to dwarf2dbg.c that uses it.
 * Note, now_seg is never set to a section number of a section with a type of
 * S_ZEROFILL or S_THREAD_LOCAL_ZEROFILL.
 */
int now_seg = 0;
int now_subseg = 0;

/*
 * sections_begin() sets up to allow sections to be created.
 */
void
sections_begin(
void)
{
	/* This may be needed if a section_new() for .text is not assumed */
	/* obstack_begin(&frags, 5000); */
	/* frag_now = (fragS *)obstack_alloc(&frags, SIZEOF_STRUCT_FRAG); */
}

/*
 * section_new() (for non-zerofill sections) switches to a new section, creating
 * it if needed, and creates a fresh fragment.  If it is the current section
 * nothing happens except checks to make sure the type, attributes and
 * sizeof_stub are the same.  The segment and section names will be trimed to
 * fit in the section structure and is the responsiblity of the caller to
 * report errors if they don't.  For zerofill sections only the struct frchain
 * for the section is returned after possibly being created (these section are
 * never made the current section and no frags are ever touched).
 *
 * Globals on input:
 *    frchain_now points to the (possibly none) struct frchain for the current
 *    section.
 *    frag_now points at an incomplete frag for current section.
 *    If frag_now == NULL, then there is no old, incomplete frag, so the old
 *    frag is not closed off.
 *
 * Globals on output:
 *    frchain_now points to the (possibly new) struct frchain for this section.
 *    frchain_root updated if needed (for the first section created).
 *    frag_now is set to the last (possibly new) frag in the section.
 *    now_seg is set to the Mach-O section number (frch_nsect field) except
 *     it is not set for section types of S_ZEROFILL or S_THREAD_LOCAL_ZEROFILL.
 */
frchainS *
section_new(
char *segname,
char *sectname,
uint32_t type,
uint32_t attributes,
uint32_t sizeof_stub)
{
    frchainS *frcP;
    frchainS **lastPP;
    uint32_t last_nsect;

	if(frags.chunk_size == 0)
	    /*
	     * This line is use instead of:
	     * 	 obstack_begin(&frags, 5000);
	     * which the only difference is that frags are allocated on 4 byte
	     * boundaries instead of the default.  The problem with the default
	     * is that on some RISC machines the obstack uses 8 (the alignment
	     * of a double after a char in a struct) and then the common use of:
	     * 	frag_now->fr_fix = obstack_next_free(&frags) -
	     *			   frag_now->fr_literal;
	     * can get an extra 4 bytes that are not in the frag because of the
	     * 8 byte alignment where only 4 byte alignment for frags are
	     * needed.
	     */
	    _obstack_begin(&frags, 5000, 4,
			   obstack_chunk_alloc, obstack_chunk_free);

	/*
	 * Determine if this section has been seen.
	 */
	last_nsect = 0;
	for(frcP = *(lastPP = &frchain_root);
	    frcP != NULL;
	    frcP = *(lastPP = &frcP->frch_next)){
	    if(strncmp(frcP->frch_section.segname, segname,
		       sizeof(frcP->frch_section.segname)) == 0 &&
	       strncmp(frcP->frch_section.sectname, sectname,
		       sizeof(frcP->frch_section.sectname)) == 0)
		break;
	    last_nsect = frcP->frch_nsect;
	}

	/*
	 * If this section has been seen make sure it's type and attributes
	 * for this call are the same as when the section was created.
	 */
	if(frcP != NULL){
	    if((frcP->frch_section.flags & SECTION_TYPE) != type){
		as_warn("section type does not match previous section type");
	    }
	    if(type == S_SYMBOL_STUBS &&
	       frcP->frch_section.reserved2 != sizeof_stub){
		as_warn("section stub size does not match previous section "
			"stub size");
	    }
	    frcP->frch_section.flags |= attributes;
	}

	/*
	 * If the current section is the same as for this call there is nothing
	 * more to do.
	 */
	if(frcP != NULL && (frchain_now == frcP || type == S_ZEROFILL ||
			    type == S_THREAD_LOCAL_ZEROFILL)){
	    if(type != S_ZEROFILL && type != S_THREAD_LOCAL_ZEROFILL)
		now_seg = frcP->frch_nsect;
	    return(frcP);
	}

	/*
	 * For non-zerofill sections it will be made the current section so deal
	 * with the current frag.
	 */
	if(type != S_ZEROFILL && type != S_THREAD_LOCAL_ZEROFILL){
	    /*
	     * If there is any current frag in the old section close it off.
	     */
	    if(frag_now != NULL){
		frag_now->fr_fix = obstack_next_free(&frags) -
				   frag_now->fr_literal;
		frag_wane(frag_now);
	    }

	    /*
	     * We must do the obstack_finish(), so the next object we put on
	     * obstack frags will not appear to start at the fr_literal of the
	     * current frag.  Also, it ensures that the next object will begin
	     * on a address that is aligned correctly for the engine that runs
	     * the assembler.
	     */
	    (void)obstack_finish(&frags);
	}

	/*
	 * If this section exists since it is not the current section switch to
	 * it by making it the current chain and create a new frag in it.
	 */
	if(frcP != NULL){
	    if(type != S_ZEROFILL && type != S_THREAD_LOCAL_ZEROFILL)
		now_seg = frcP->frch_nsect;
	    /*
	     * For a zerofill section no frags are created here and since it
	     * exists just return a pointer to the section.
	     */
	    if((frcP->frch_section.flags & SECTION_TYPE) == S_ZEROFILL ||
	       (frcP->frch_section.flags & SECTION_TYPE) ==
						       S_THREAD_LOCAL_ZEROFILL){
		return(frcP);
	    }
	    else{
		/*
		 * Make this section the current section.
		 */
		frchain_now = frcP;
		if(type != S_ZEROFILL && type != S_THREAD_LOCAL_ZEROFILL)
		    now_seg = frchain_now->frch_nsect;

		/*
		 * Make a fresh frag for the section.
		 */
		frag_now = (fragS *)obstack_alloc(&frags, SIZEOF_STRUCT_FRAG);
		memset(frag_now, '\0', SIZEOF_STRUCT_FRAG);
		frag_now->fr_next = NULL;

		/*
		 * Append the new frag to the existing frchain.
		 */
		frchain_now->frch_last->fr_next = frag_now;
		frchain_now->frch_last = frag_now;
	    }
	}
	else{
	    /*
	     * This section does not exist so create a new frchainS struct fill
	     * it in, link it to the chain
	     */
	    frcP = (frchainS *)xmalloc(sizeof(frchainS));
	    memset(frcP, '\0', sizeof(frchainS));
	    strncpy(frcP->frch_section.segname, segname,
		    sizeof(frcP->frch_section.segname));
	    strncpy(frcP->frch_section.sectname, sectname,
		    sizeof(frcP->frch_section.sectname));
	    frcP->frch_section.flags = attributes | type;
	    frcP->frch_section.reserved2 = sizeof_stub;

	    if(last_nsect + 1 > MAX_SECT)
		as_fatal("too many sections (maximum %d)\n", MAX_SECT);
	    frcP->frch_nsect = last_nsect + 1;
	    if(type != S_ZEROFILL && type != S_THREAD_LOCAL_ZEROFILL)
		now_seg = frcP->frch_nsect;

	    *lastPP = frcP;

	    /*
	     * For zerofill sections no frag is created here so just return.
	     * For non-zerofill section create the sections new frag and
	     * make the section the current chain.
	     */
	    if(type == S_ZEROFILL || type == S_THREAD_LOCAL_ZEROFILL){
		return(frcP);
	    }
	    else{
		/*
		 * Make a fresh frag for the new section.
		 */
		frag_now = (fragS *)obstack_alloc(&frags, SIZEOF_STRUCT_FRAG);
		memset(frag_now, '\0', SIZEOF_STRUCT_FRAG);
		frag_now->fr_next = NULL;

		/*
		 * Append the new frag to new frchain.
		 */
		frcP->frch_root = frag_now;
		frcP->frch_last = frag_now;

		/*
		 * Make this section the current section.
		 */
		frchain_now = frcP;
	    }
	}
	return(frchain_now);
}

/*
 * section_set() sets the current section to passed section pointer struct.
 * This is used by dwarf2dbg.c before emiting the debug sections.
 */
void
section_set(
frchainS *frcP)
{
	section_new(frcP->frch_section.segname, frcP->frch_section.sectname,
		    frcP->frch_section.flags & SECTION_TYPE,
		    frcP->frch_section.flags & SECTION_ATTRIBUTES,
		    frcP->frch_section.reserved2);
}

/*
 * section_symbol() creates and stores (if needed) a symbol for the start of
 * the section. This is used by code in dwarf2dbg.c .
 */
symbolS *
section_symbol(
frchainS *frcP)
{
	if(frcP->section_symbol == NULL){
	    frcP->section_symbol = symbol_temp_new(frcP->frch_nsect,
						   0,
						   frcP->frch_root);
	}
	return(frcP->section_symbol);
}

/* Return non zero if the section has at least one byte of data.  It is
   possible that we'll return zero even on a non-empty section because
   we don't know all the fragment types, and it is possible that an
   fr_fix == 0 one still contributes data.  Think of this as
   seg_definitely_not_empty_p.  */
int
seg_not_empty_p(
frchainS *frcP)
{
    fragS *frag;

	if(frcP == NULL)
	    return(0);
 
	for(frag = frcP->frch_root; frag; frag = frag->fr_next){
	    if(frag->fr_fix != 0)
	      return 1;
	}
	return 0;
}


struct frchain *
get_section_by_nsect(
uint32_t nsect)
{
    struct frchain *frchainP;

	for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
	    if(frchainP->frch_nsect == nsect)
		return(frchainP);
	}
	return(NULL);
}

struct frchain *
get_section_by_name(
char *segname,
char *sectname)
{
    struct frchain *frchainP;

	for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
	    if(strncmp(frchainP->frch_section.segname, segname,
		       sizeof(frchainP->frch_section.segname)) == 0 &&
	       strncmp(frchainP->frch_section.sectname, sectname,
		       sizeof(frchainP->frch_section.sectname)) == 0)
		return(frchainP);
	}
	return(NULL);
}

uint32_t
is_section_coalesced(
uint32_t n_sect)
{
    struct frchain *frchainP;

	for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
	    if(frchainP->frch_nsect == n_sect &&
	       (frchainP->frch_section.flags & SECTION_TYPE) == S_COALESCED)
		return(1); /* TRUE */
	}
	return(0); /* FALSE */
}

uint32_t
is_section_non_lazy_symbol_pointers(
uint32_t n_sect)
{
    struct frchain *frchainP;

	for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
	    if(frchainP->frch_nsect == n_sect &&
	       (frchainP->frch_section.flags & SECTION_TYPE) ==
	        S_NON_LAZY_SYMBOL_POINTERS)
		return(1); /* TRUE */
	}
	return(0); /* FALSE */
}

uint32_t
is_section_debug(
uint32_t n_sect)
{
    struct frchain *frchainP;

	for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
	    if(frchainP->frch_nsect == n_sect &&
	       (frchainP->frch_section.flags & SECTION_ATTRIBUTES) ==
	        S_ATTR_DEBUG)
		return(1); /* TRUE */
	}
	return(0); /* FALSE */
}

uint32_t
is_section_cstring_literals(
uint32_t n_sect)
{
    struct frchain *frchainP;

	for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
	    if(frchainP->frch_nsect == n_sect &&
	       (frchainP->frch_section.flags & SECTION_TYPE) ==
		S_CSTRING_LITERALS)
		return(1); /* TRUE */
	}
	return(0); /* FALSE */
}

uint32_t
is_end_section_address(
uint32_t n_sect,
addressT addr)
{
    struct frchain *frchainP;
    uint32_t section_type;

	for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
	    if(frchainP->frch_nsect == n_sect){
		section_type = frchainP->frch_section.flags & SECTION_TYPE;
		if(section_type == S_ZEROFILL ||
		   section_type == S_THREAD_LOCAL_ZEROFILL)
		    return(0); /* FALSE */
		if(frchainP->frch_last->fr_address == addr)
		    return(1); /* TRUE */
		else
		    return(0); /* FALSE */
	    }
	}
	return(0); /* FALSE */
}

uint32_t
section_has_fixed_size_data(
uint32_t n_sect)
{
    struct frchain *frchainP;

	for(frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next){
	    if(frchainP->frch_nsect == n_sect){
			switch (frchainP->frch_section.flags & SECTION_TYPE){
				case S_CSTRING_LITERALS:
				case S_4BYTE_LITERALS:
				case S_8BYTE_LITERALS:
				case S_16BYTE_LITERALS:
				case S_LITERAL_POINTERS:
				case S_NON_LAZY_SYMBOL_POINTERS:
				case S_LAZY_SYMBOL_POINTERS:
				case S_MOD_INIT_FUNC_POINTERS:
				case S_MOD_TERM_FUNC_POINTERS:
				case S_INTERPOSING:
					return(1); /* TRUE */
			}
		}
	}
	return(0); /* FALSE */
}
