/* SPDX-License-Identifier: BSD-2-Clause */
/* Copyright 1996-2025 The NASM Authors - All Rights Reserved */

#ifndef NASM_OUTLIB_H
#define NASM_OUTLIB_H

#include "compiler.h"
#include "nasm.h"
#include "error.h"
#include "hashtbl.h"
#include "saa.h"
#include "rbtree.h"

uint64_t realsize(enum out_type type, uint64_t size);

/* Do-nothing versions of some output routines */
enum directive_result
null_directive(enum directive directive, char *value);
void null_sectalign(int32_t seg, unsigned int value);
void null_reset(void);
int32_t null_segbase(int32_t seg);

/* Do-nothing versions of all the debug routines */
void null_debug_init(void);
void null_debug_linenum(const char *filename, int32_t linenumber,
			int32_t segto);
void null_debug_deflabel(char *name, int32_t segment, int64_t offset,
                         int is_global, char *special);
void null_debug_directive(const char *directive, const char *params);
void null_debug_typevalue(int32_t type);
void null_debug_output(int type, void *param);
void null_debug_cleanup(void);
extern const struct dfmt * const null_debug_arr[2];

/*
 * This macro expands the legacy output information into separate
 * variables, to make gradual porting of backends easier.
 */
#define OUT_LEGACY(_out,_segto,_data,_type,_size,_segment,_wrt)       \
    int32_t _segto      = (_out)->loc.segment;                        \
    const void *_data   = (_out)->legacy.data;                        \
    enum out_type _type = (_out)->legacy.type;                        \
    uint64_t _size      = (_out)->legacy.size;                        \
    int32_t _segment    = (_out)->legacy.tsegment;                    \
    int32_t _wrt        = (_out)->legacy.twrt

/*
 * Common routines for tasks that really should migrate into the core.
 * This provides a common interface for maintaining sections and symbols,
 * and provide quick lookups as well as declared-order sequential walks.
 *
 * These structures are intended to be embedded at the *top* of a
 * backend-specific structure containing additional information.
 *
 * The tokens O_Section, O_Symbol and O_Reloc are intended to be
 * defined as macros by the backend before including this file!
 */

struct ol_sect;
struct ol_sym;

#ifndef O_Section
typedef struct ol_sect O_Section;
#endif
#ifndef O_Symbol
typedef struct ol_sym O_Symbol;
#endif
#ifndef O_Reloc
typedef void * O_Reloc;
#endif

/* Common section structure */

/*
 * Common flags for sections and symbols; low values reserved for
 * backend.  Note that both ol_sect and ol_sym begin with a flags
 * field, so if a section pointer points to an external symbol instead
 * they can be trivially resolved.
 */
#define OF_SYMBOL 0x80000000
#define OF_GLOBAL 0x40000000
#define OF_IMPSEC 0x20000000
#define OF_COMMON 0x10000000

struct ol_sym;

struct ol_symlist {
    struct ol_symlist *next;
    struct rbtree tree;
};
struct ol_symhead {
    struct ol_symlist *head, **tail;
    struct rbtree *tree;
    uint64_t n;
};

struct ol_sect {
    uint32_t flags;             /* Section/symbol flags */
    struct ol_sect *next;       /* Next section in declared order */
    const char *name;           /* Name of section */
    struct ol_symhead syml;     /* All symbols in this section */
    struct ol_symhead symg;     /* Global symbols in this section */
    struct SAA *data;           /* Contents of section */
    struct SAA *reloc;          /* Section relocations */
    uint32_t index;             /* Primary section index */
    uint32_t subindex;          /* Current subsection index */
};

/* Segment reference */
enum ol_seg_type {
    OS_NOSEG  = 0,                /* Plain number (no segment) */
    OS_SEGREF = 1,                /* It is a segment reference */
    OS_ABS    = 1,                /* Absolute segment reference */
    OS_SECT   = 2,                /* It is a real section */
    OS_OFFS   = OS_SECT,          /* Offset reference in section */
    OS_SEG    = OS_SECT|OS_SEGREF /* Section reference */
};

union ol_segval {
    struct ol_sect *sect;   /* Section structure */
    struct ol_sym  *sym;    /* External symbol structure */
};

struct ol_seg {
    union ol_segval  s;
    enum ol_seg_type t;

    /*
     * For a section:          subsection index
     * For a metasymbol:       virtual segment index
     * For an absolute symbol: absolute value
     */
    uint32_t index;
};

/* seg:offs representing the full location value and type */
struct ol_loc {
    int64_t offs;
    struct ol_seg seg;
};

/* Common symbol structure */
struct ol_sym {
    uint32_t flags;             /* Section/symbol flags */
    uint32_t size;              /* Size value (for backend) */
    struct ol_sym *next;        /* Next symbol in declared order */
    const char *name;           /* Symbol name */
    struct ol_symlist syml;     /* Section-local symbol list */
    struct ol_symlist symg;     /* Section-local global symbol list */
    struct ol_loc p;            /* Symbol position ("where") */
    struct ol_loc v;            /* Symbol value ("what") */
};

/*
 * Operations
 */
void ol_init(void);
void ol_cleanup(void);

/* Convert offs:seg to a location structure */
extern void
ol_mkloc(struct ol_loc *loc, int64_t offs, int32_t seg);

/* Get the section or external symbol from a struct ol_seg */
static inline O_Section *seg_sect(struct ol_seg *seg)
{
    return (O_Section *)seg->s.sect;
}
static inline O_Symbol *seg_xsym(struct ol_seg *seg)
{
    return (O_Symbol *)seg->s.sym;
}

/*
 * Return a pointer to the symbol structure if and only if a section is
 * really a symbol of some kind (extern, common...)
 */
static inline struct ol_sym *_seg_extsym(struct ol_sect *sect)
{
    return (sect->flags & OF_SYMBOL) ? (struct ol_sym *)sect : NULL;
}
static inline O_Symbol *seg_extsym(O_Section *sect)
{
    return (O_Symbol *)_seg_extsym((struct ol_sect *)sect);
}

/*
 * Find a section or create a new section structure if it does not exist
 * and allocate it an index value via seg_alloc().
 */
extern struct ol_sect *
_ol_get_sect(const char *name, size_t ssize, size_t rsize);
static inline O_Section *ol_get_sect(const char *name)
{
    return (O_Section *)_ol_get_sect(name, sizeof(O_Section), sizeof(O_Reloc));
}

/* Find a section by name without creating one */
extern struct ol_sect *_ol_sect_by_name(const char *);
static inline O_Section *ol_sect_by_name(const char *name)
{
    return (O_Section *)_ol_sect_by_name(name);
}

/* Find a section or external symbol by index; NULL if not valid */
extern struct ol_sect *_ol_sect_by_index(int32_t index);
static inline O_Section *ol_sect_by_index(int32_t index)
{
    return (O_Section *)_ol_sect_by_index(index);
}

/* Global list of sections (not including external symbols) */
extern struct ol_sect *_ol_sect_list;
static inline O_Section *ol_sect_list(void)
{
    return (O_Section *)_ol_sect_list;
}

/* Count of sections (not including external symbols) */
extern uint64_t _ol_nsects;
static inline uint64_t ol_nsects(void)
{
    return _ol_nsects;
}

/*
 * Start a new subsection for the given section. At the moment, once a
 * subsection has been created, it is not possible to revert to an
 * earlier subsection. ol_sect_by_index() will return the main section
 * structure. Returns the new section index.  This is used to prevent
 * the front end from optimizing across subsection boundaries.
 */
extern int32_t _ol_new_subsection(struct ol_sect *sect);
static inline int32_t ol_new_subsection(O_Section *sect)
{
    return _ol_new_subsection((struct ol_sect *)sect);
}

/*
 * Create a new symbol. If this symbol is OS_OFFS, add it to the relevant
 * section, too. If the symbol already exists, return NULL; this is
 * different from ol_get_section() as a single section may be invoked
 * many times. On the contrary, the front end will prevent a single symbol
 * from being defined more than once.
 *
 * If flags has OF_GLOBAL set, add it to the global symbol hash for the
 * containing section. If flags has OF_IMPSEC set, allocate a segment
 * index for it via seg_alloc() and add it to the section by index list.
 */
extern struct ol_sym *_ol_new_sym(const char *name, const struct ol_loc *v,
                                  uint32_t flags, size_t size);
static inline O_Symbol *ol_new_sym(const char *name, const struct ol_loc *v,
                                   uint32_t flags)
{
    return (O_Symbol *)_ol_new_sym(name, v, flags, sizeof(O_Symbol));
}

/* Find a symbol by name in the global namespace */
extern struct ol_sym *_ol_sym_by_name(const char *name);
static inline O_Symbol *ol_sym_by_name(const char *name)
{
    return (O_Symbol *)_ol_sym_by_name(name);
}

/*
 * Find a symbol by address in a specific section. If no symbol is defined
 * at that exact address, return the immediately previously defined one.
 * If global is set, then only return global symbols.
 */
extern struct ol_sym *_ol_sym_by_address(struct ol_sect *sect, int64_t addr,
                                         bool global);
static inline O_Symbol *ol_sym_by_address(O_Section *sect, int64_t addr,
                                          bool global)
{
    return (O_Symbol *)_ol_sym_by_address((struct ol_sect *)sect, addr, global);
}

/* Global list of symbols */
extern struct ol_sym *_ol_sym_list;
static inline O_Symbol *ol_sym_list(void)
{
    return (O_Symbol *)_ol_sym_list;
}

/* Global count of symbols */
extern uint64_t _ol_nsyms;
static inline uint64_t ol_nsyms(void)
{
    return _ol_nsyms;
}

#endif /* NASM_OUTLIB_H */
