mirror of
https://github.com/netwide-assembler/nasm.git
synced 2024-12-15 09:09:58 +08:00
7ad25b2e18
NASM convention is to use all-upper-case for "real" information, and mixed-case (upper case common prefix, lower case description) for meta-information. This is a highly useful distinction. Thus "LBL_NONE" implies an actual label of type "NONE", as opposed to no label at all. Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
721 lines
21 KiB
C
721 lines
21 KiB
C
/* ----------------------------------------------------------------------- *
|
|
*
|
|
* Copyright 1996-2018 The NASM Authors - All Rights Reserved
|
|
* See the file AUTHORS included with the NASM distribution for
|
|
* the specific copyright holders.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following
|
|
* conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* ----------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* labels.c label handling for the Netwide Assembler
|
|
*/
|
|
|
|
#include "compiler.h"
|
|
|
|
|
|
#include "nasm.h"
|
|
#include "nasmlib.h"
|
|
#include "error.h"
|
|
#include "hashtbl.h"
|
|
#include "labels.h"
|
|
|
|
/*
|
|
* A dot-local label is one that begins with exactly one period. Things
|
|
* that begin with _two_ periods are NASM-specific things.
|
|
*
|
|
* If TASM compatibility is enabled, a local label can also begin with
|
|
* @@.
|
|
*/
|
|
static bool islocal(const char *l)
|
|
{
|
|
if (tasm_compatible_mode) {
|
|
if (l[0] == '@' && l[1] == '@')
|
|
return true;
|
|
}
|
|
|
|
return (l[0] == '.' && l[1] != '.');
|
|
}
|
|
|
|
/*
|
|
* Return true if this falls into NASM's '..' namespace
|
|
*/
|
|
static bool ismagic(const char *l)
|
|
{
|
|
return l[0] == '.' && l[1] == '.' && l[2] != '@';
|
|
}
|
|
|
|
/*
|
|
* Return true if we should update the local label base
|
|
* as a result of this symbol. We must exclude local labels
|
|
* as well as any kind of special labels, including ..@ ones.
|
|
*/
|
|
static bool set_prevlabel(const char *l)
|
|
{
|
|
if (tasm_compatible_mode) {
|
|
if (l[0] == '@' && l[1] == '@')
|
|
return false;
|
|
}
|
|
|
|
return l[0] != '.';
|
|
}
|
|
|
|
#define LABEL_BLOCK 128 /* no. of labels/block */
|
|
#define LBLK_SIZE (LABEL_BLOCK * sizeof(union label))
|
|
|
|
#define END_LIST -3 /* don't clash with NO_SEG! */
|
|
#define END_BLOCK -2
|
|
|
|
#define PERMTS_SIZE 16384 /* size of text blocks */
|
|
#if (PERMTS_SIZE < IDLEN_MAX)
|
|
#error "IPERMTS_SIZE must be greater than or equal to IDLEN_MAX"
|
|
#endif
|
|
|
|
/* string values for enum label_type */
|
|
static const char * const types[] = {
|
|
"local", "static", "global", "extern", "required", "common",
|
|
"special", "output format special"
|
|
};
|
|
|
|
union label { /* actual label structures */
|
|
struct {
|
|
int32_t segment;
|
|
int32_t subsection; /* Available for ofmt->herelabel() */
|
|
int64_t offset;
|
|
int64_t size;
|
|
int64_t defined; /* 0 if undefined, passn+1 for when defn seen */
|
|
int64_t lastref; /* Last pass where we saw a reference */
|
|
char *label, *mangled, *special;
|
|
const char *def_file; /* Where defined */
|
|
int32_t def_line;
|
|
enum label_type type, mangled_type;
|
|
} defn;
|
|
struct {
|
|
int32_t movingon;
|
|
int64_t dummy;
|
|
union label *next;
|
|
} admin;
|
|
};
|
|
|
|
struct permts { /* permanent text storage */
|
|
struct permts *next; /* for the linked list */
|
|
unsigned int size, usage; /* size and used space in ... */
|
|
char data[PERMTS_SIZE]; /* ... the data block itself */
|
|
};
|
|
#define PERMTS_HEADER offsetof(struct permts, data)
|
|
|
|
uint64_t global_offset_changed; /* counter for global offset changes */
|
|
|
|
static struct hash_table ltab; /* labels hash table */
|
|
static union label *ldata; /* all label data blocks */
|
|
static union label *lfree; /* labels free block */
|
|
static struct permts *perm_head; /* start of perm. text storage */
|
|
static struct permts *perm_tail; /* end of perm. text storage */
|
|
|
|
static void init_block(union label *blk);
|
|
static char *perm_alloc(size_t len);
|
|
static char *perm_copy(const char *string);
|
|
static char *perm_copy3(const char *s1, const char *s2, const char *s3);
|
|
static const char *mangle_label_name(union label *lptr);
|
|
|
|
static const char *prevlabel;
|
|
|
|
static bool initialized = false;
|
|
|
|
/*
|
|
* Emit a symdef to the output and the debug format backends.
|
|
*/
|
|
static void out_symdef(union label *lptr)
|
|
{
|
|
int backend_type;
|
|
int64_t backend_offset;
|
|
|
|
/* Backend-defined special segments are passed to symdef immediately */
|
|
if (pass_final()) {
|
|
/* Emit special fixups for globals and commons */
|
|
switch (lptr->defn.type) {
|
|
case LBL_GLOBAL:
|
|
case LBL_REQUIRED:
|
|
case LBL_COMMON:
|
|
if (lptr->defn.special)
|
|
ofmt->symdef(lptr->defn.mangled, 0, 0, 3, lptr->defn.special);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (pass_type() != PASS_STAB && lptr->defn.type != LBL_BACKEND)
|
|
return;
|
|
|
|
/* Clean up this hack... */
|
|
switch(lptr->defn.type) {
|
|
case LBL_EXTERN:
|
|
/* If not seen in the previous or this pass, drop it */
|
|
if (lptr->defn.lastref < pass_count())
|
|
return;
|
|
|
|
/* Otherwise, promote to LBL_REQUIRED at this time */
|
|
lptr->defn.type = LBL_REQUIRED;
|
|
|
|
/* fall through */
|
|
case LBL_GLOBAL:
|
|
case LBL_REQUIRED:
|
|
backend_type = 1;
|
|
backend_offset = lptr->defn.offset;
|
|
break;
|
|
case LBL_COMMON:
|
|
backend_type = 2;
|
|
backend_offset = lptr->defn.size;
|
|
break;
|
|
default:
|
|
backend_type = 0;
|
|
backend_offset = lptr->defn.offset;
|
|
break;
|
|
}
|
|
|
|
/* Might be necessary for a backend symbol */
|
|
mangle_label_name(lptr);
|
|
|
|
ofmt->symdef(lptr->defn.mangled, lptr->defn.segment,
|
|
backend_offset, backend_type,
|
|
lptr->defn.special);
|
|
|
|
/*
|
|
* NASM special symbols are not passed to the debug format; none
|
|
* of the current backends want to see them.
|
|
*/
|
|
if (lptr->defn.type == LBL_SPECIAL || lptr->defn.type == LBL_BACKEND)
|
|
return;
|
|
|
|
dfmt->debug_deflabel(lptr->defn.mangled, lptr->defn.segment,
|
|
lptr->defn.offset, backend_type,
|
|
lptr->defn.special);
|
|
}
|
|
|
|
/*
|
|
* Internal routine: finds the `union label' corresponding to the
|
|
* given label name. Creates a new one, if it isn't found, and if
|
|
* `create' is true.
|
|
*/
|
|
static union label *find_label(const char *label, bool create, bool *created)
|
|
{
|
|
union label *lptr, **lpp;
|
|
char *label_str = NULL;
|
|
struct hash_insert ip;
|
|
|
|
nasm_assert(label != NULL);
|
|
|
|
if (islocal(label))
|
|
label = label_str = nasm_strcat(prevlabel, label);
|
|
|
|
lpp = (union label **) hash_find(<ab, label, &ip);
|
|
lptr = lpp ? *lpp : NULL;
|
|
|
|
if (lptr || !create) {
|
|
if (created)
|
|
*created = false;
|
|
return lptr;
|
|
}
|
|
|
|
/* Create a new label... */
|
|
if (lfree->admin.movingon == END_BLOCK) {
|
|
/*
|
|
* must allocate a new block
|
|
*/
|
|
lfree->admin.next = nasm_malloc(LBLK_SIZE);
|
|
lfree = lfree->admin.next;
|
|
init_block(lfree);
|
|
}
|
|
|
|
if (created)
|
|
*created = true;
|
|
|
|
nasm_zero(*lfree);
|
|
lfree->defn.label = perm_copy(label);
|
|
lfree->defn.subsection = NO_SEG;
|
|
if (label_str)
|
|
nasm_free(label_str);
|
|
|
|
hash_add(&ip, lfree->defn.label, lfree);
|
|
return lfree++;
|
|
}
|
|
|
|
enum label_type lookup_label(const char *label,
|
|
int32_t *segment, int64_t *offset)
|
|
{
|
|
union label *lptr;
|
|
|
|
if (!initialized)
|
|
return LBL_none;
|
|
|
|
lptr = find_label(label, false, NULL);
|
|
if (lptr && lptr->defn.defined) {
|
|
int64_t lpass = pass_count() + 1;
|
|
|
|
lptr->defn.lastref = lpass;
|
|
*segment = lptr->defn.segment;
|
|
*offset = lptr->defn.offset;
|
|
return lptr->defn.type;
|
|
}
|
|
|
|
return LBL_none;
|
|
}
|
|
|
|
static inline bool is_global(enum label_type type)
|
|
{
|
|
return type == LBL_GLOBAL || type == LBL_COMMON;
|
|
}
|
|
|
|
static const char *mangle_strings[] = {"", "", "", ""};
|
|
static bool mangle_string_set[ARRAY_SIZE(mangle_strings)];
|
|
|
|
/*
|
|
* Set a prefix or suffix
|
|
*/
|
|
void set_label_mangle(enum mangle_index which, const char *what)
|
|
{
|
|
if (mangle_string_set[which])
|
|
return; /* Once set, do not change */
|
|
|
|
mangle_strings[which] = perm_copy(what);
|
|
mangle_string_set[which] = true;
|
|
}
|
|
|
|
/*
|
|
* Format a label name with appropriate prefixes and suffixes
|
|
*/
|
|
static const char *mangle_label_name(union label *lptr)
|
|
{
|
|
const char *prefix;
|
|
const char *suffix;
|
|
|
|
if (likely(lptr->defn.mangled &&
|
|
lptr->defn.mangled_type == lptr->defn.type))
|
|
return lptr->defn.mangled; /* Already mangled */
|
|
|
|
switch (lptr->defn.type) {
|
|
case LBL_GLOBAL:
|
|
case LBL_STATIC:
|
|
case LBL_EXTERN:
|
|
case LBL_REQUIRED:
|
|
prefix = mangle_strings[LM_GPREFIX];
|
|
suffix = mangle_strings[LM_GSUFFIX];
|
|
break;
|
|
case LBL_BACKEND:
|
|
case LBL_SPECIAL:
|
|
prefix = suffix = "";
|
|
break;
|
|
default:
|
|
prefix = mangle_strings[LM_LPREFIX];
|
|
suffix = mangle_strings[LM_LSUFFIX];
|
|
break;
|
|
}
|
|
|
|
lptr->defn.mangled_type = lptr->defn.type;
|
|
|
|
if (!(*prefix) && !(*suffix))
|
|
lptr->defn.mangled = lptr->defn.label;
|
|
else
|
|
lptr->defn.mangled = perm_copy3(prefix, lptr->defn.label, suffix);
|
|
|
|
return lptr->defn.mangled;
|
|
}
|
|
|
|
static void
|
|
handle_herelabel(union label *lptr, int32_t *segment, int64_t *offset)
|
|
{
|
|
int32_t oldseg;
|
|
|
|
if (likely(!ofmt->herelabel))
|
|
return;
|
|
|
|
if (unlikely(location.segment == NO_SEG))
|
|
return;
|
|
|
|
oldseg = *segment;
|
|
|
|
if (oldseg == location.segment && *offset == location.offset) {
|
|
/* This label is defined at this location */
|
|
int32_t newseg;
|
|
bool copyoffset = false;
|
|
|
|
nasm_assert(lptr->defn.mangled);
|
|
newseg = ofmt->herelabel(lptr->defn.mangled, lptr->defn.type,
|
|
oldseg, &lptr->defn.subsection, ©offset);
|
|
if (likely(newseg == oldseg))
|
|
return;
|
|
|
|
*segment = newseg;
|
|
if (copyoffset) {
|
|
/* Maintain the offset from the old to the new segment */
|
|
switch_segment(newseg);
|
|
location.offset = *offset;
|
|
} else {
|
|
/* Keep a separate offset for the new segment */
|
|
*offset = switch_segment(newseg);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool declare_label_lptr(union label *lptr,
|
|
enum label_type type, const char *special)
|
|
{
|
|
enum label_type oldtype = lptr->defn.type;
|
|
|
|
if (special && !special[0])
|
|
special = NULL;
|
|
|
|
if (oldtype == type || (!pass_stable() && oldtype == LBL_LOCAL) ||
|
|
(oldtype == LBL_EXTERN && type == LBL_REQUIRED)) {
|
|
lptr->defn.type = type;
|
|
|
|
if (special) {
|
|
if (!lptr->defn.special)
|
|
lptr->defn.special = perm_copy(special);
|
|
else if (nasm_stricmp(lptr->defn.special, special))
|
|
nasm_nonfatal("symbol `%s' has inconsistent attributes `%s' and `%s'",
|
|
lptr->defn.label, lptr->defn.special, special);
|
|
}
|
|
return true;
|
|
} else if (is_extern(oldtype) && is_global(type)) {
|
|
/* EXTERN or REQUIRED can be replaced with GLOBAL or COMMON */
|
|
lptr->defn.type = type;
|
|
|
|
/* Override special unconditionally */
|
|
if (special)
|
|
lptr->defn.special = perm_copy(special);
|
|
return true;
|
|
} else if (is_extern(type) && (is_global(oldtype) || is_extern(oldtype))) {
|
|
/*
|
|
* GLOBAL or COMMON ignore subsequent EXTERN or REQUIRED;
|
|
* REQUIRED ignores subsequent EXTERN.
|
|
*/
|
|
|
|
/* Ignore special unless we don't already have one */
|
|
if (!lptr->defn.special)
|
|
lptr->defn.special = perm_copy(special);
|
|
|
|
return false; /* Don't call define_label() after this! */
|
|
}
|
|
|
|
nasm_nonfatal("symbol `%s' declared both as %s and %s",
|
|
lptr->defn.label, types[lptr->defn.type], types[type]);
|
|
return false;
|
|
}
|
|
|
|
bool declare_label(const char *label, enum label_type type, const char *special)
|
|
{
|
|
union label *lptr = find_label(label, true, NULL);
|
|
return declare_label_lptr(lptr, type, special);
|
|
}
|
|
|
|
/*
|
|
* The "normal" argument decides if we should update the local segment
|
|
* base name or not.
|
|
*/
|
|
void define_label(const char *label, int32_t segment,
|
|
int64_t offset, bool normal)
|
|
{
|
|
union label *lptr;
|
|
bool created, changed;
|
|
int64_t size;
|
|
int64_t lpass, lastdef;
|
|
|
|
/*
|
|
* The backend may invoke this during initialization, at which
|
|
* pass_count() is zero, so add one so we never have a zero value
|
|
* for a defined variable.
|
|
*/
|
|
lpass = pass_count() + 1;
|
|
|
|
/*
|
|
* Phase errors here can be one of two types: a new label appears,
|
|
* or the offset changes. Increment global_offset_changed when that
|
|
* happens, to tell the assembler core to make another pass.
|
|
*/
|
|
lptr = find_label(label, true, &created);
|
|
|
|
lastdef = lptr->defn.defined;
|
|
|
|
if (segment) {
|
|
/* We are actually defining this label */
|
|
if (is_extern(lptr->defn.type)) {
|
|
/* auto-promote EXTERN/REQUIRED to GLOBAL */
|
|
lptr->defn.type = LBL_GLOBAL;
|
|
lastdef = 0; /* We are "re-creating" this label */
|
|
}
|
|
} else {
|
|
/* It's a pseudo-segment (extern, required, common) */
|
|
segment = lptr->defn.segment ? lptr->defn.segment : seg_alloc();
|
|
}
|
|
|
|
if (lastdef || lptr->defn.type == LBL_BACKEND) {
|
|
/*
|
|
* We have seen this on at least one previous pass, or
|
|
* potentially earlier in this same pass (in which case we
|
|
* will probably error out further down.)
|
|
*/
|
|
mangle_label_name(lptr);
|
|
handle_herelabel(lptr, &segment, &offset);
|
|
}
|
|
|
|
if (ismagic(label) && lptr->defn.type == LBL_LOCAL)
|
|
lptr->defn.type = LBL_SPECIAL;
|
|
|
|
if (set_prevlabel(label) && normal)
|
|
prevlabel = lptr->defn.label;
|
|
|
|
if (lptr->defn.type == LBL_COMMON) {
|
|
size = offset;
|
|
offset = 0;
|
|
} else {
|
|
size = 0; /* This is a hack... */
|
|
}
|
|
|
|
changed = created || !lastdef ||
|
|
lptr->defn.segment != segment ||
|
|
lptr->defn.offset != offset ||
|
|
lptr->defn.size != size;
|
|
global_offset_changed += changed;
|
|
|
|
if (lastdef == lpass) {
|
|
int32_t saved_line = 0;
|
|
const char *saved_fname = NULL;
|
|
int noteflags;
|
|
|
|
/*
|
|
* Defined elsewhere in the program, seen in this pass.
|
|
*/
|
|
if (changed) {
|
|
nasm_nonfatal("label `%s' inconsistently redefined", lptr->defn.label);
|
|
noteflags = ERR_NONFATAL|ERR_HERE|ERR_NO_SEVERITY;
|
|
} else {
|
|
/*!
|
|
*!label-redef [off] label redefined to an identical value
|
|
*! warns if a label is defined more than once, but the
|
|
*! value is identical. It is an unconditional error to
|
|
*! define the same label more than once to \e{different} values.
|
|
*/
|
|
nasm_warn(WARN_LABEL_REDEF,
|
|
"info: label `%s' redefined to an identical value", lptr->defn.label);
|
|
noteflags = ERR_WARNING|ERR_HERE|ERR_NO_SEVERITY|WARN_LABEL_REDEF;
|
|
}
|
|
|
|
src_get(&saved_line, &saved_fname);
|
|
src_set(lptr->defn.def_line, lptr->defn.def_file);
|
|
nasm_error(noteflags, "info: label `%s' originally defined", lptr->defn.label);
|
|
src_set(saved_line, saved_fname);
|
|
} else if (changed && pass_final() && lptr->defn.type != LBL_SPECIAL) {
|
|
/*!
|
|
*!label-redef-late [err] label (re)defined during code generation
|
|
*! the value of a label changed during the final, code-generation
|
|
*! pass. This may be the result of strange use of the
|
|
*! preprocessor. This is very likely to produce incorrect code and
|
|
*! may end up being an unconditional error in a future
|
|
*! version of NASM.
|
|
*
|
|
* WARN_LABEL_LATE defaults to an error, as this should never
|
|
* actually happen. Just in case this is a backwards
|
|
* compatibility problem, still make it a warning so that the
|
|
* user can suppress or demote it.
|
|
*
|
|
* Note: As a special case, LBL_SPECIAL symbols are allowed
|
|
* to be changed even during the last pass.
|
|
*/
|
|
nasm_warn(WARN_LABEL_REDEF_LATE|ERR_UNDEAD,
|
|
"label `%s' %s during code generation",
|
|
lptr->defn.label, created ? "defined" : "changed");
|
|
}
|
|
lptr->defn.segment = segment;
|
|
lptr->defn.offset = offset;
|
|
lptr->defn.size = size;
|
|
lptr->defn.defined = lpass;
|
|
|
|
if (changed || lastdef != lpass)
|
|
src_get(&lptr->defn.def_line, &lptr->defn.def_file);
|
|
|
|
if (lastdef != lpass)
|
|
out_symdef(lptr);
|
|
}
|
|
|
|
/*
|
|
* Define a special backend label
|
|
*/
|
|
void backend_label(const char *label, int32_t segment, int64_t offset)
|
|
{
|
|
if (!declare_label(label, LBL_BACKEND, NULL))
|
|
return;
|
|
|
|
define_label(label, segment, offset, false);
|
|
}
|
|
|
|
int init_labels(void)
|
|
{
|
|
ldata = lfree = nasm_malloc(LBLK_SIZE);
|
|
init_block(lfree);
|
|
|
|
perm_head = perm_tail =
|
|
nasm_malloc(sizeof(struct permts));
|
|
|
|
perm_head->next = NULL;
|
|
perm_head->size = PERMTS_SIZE;
|
|
perm_head->usage = 0;
|
|
|
|
prevlabel = "";
|
|
|
|
initialized = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void cleanup_labels(void)
|
|
{
|
|
union label *lptr, *lhold;
|
|
|
|
initialized = false;
|
|
|
|
hash_free(<ab);
|
|
|
|
lptr = lhold = ldata;
|
|
while (lptr) {
|
|
lptr = &lptr[LABEL_BLOCK-1];
|
|
lptr = lptr->admin.next;
|
|
nasm_free(lhold);
|
|
lhold = lptr;
|
|
}
|
|
|
|
while (perm_head) {
|
|
perm_tail = perm_head;
|
|
perm_head = perm_head->next;
|
|
nasm_free(perm_tail);
|
|
}
|
|
}
|
|
|
|
static void init_block(union label *blk)
|
|
{
|
|
int j;
|
|
|
|
for (j = 0; j < LABEL_BLOCK - 1; j++)
|
|
blk[j].admin.movingon = END_LIST;
|
|
blk[LABEL_BLOCK - 1].admin.movingon = END_BLOCK;
|
|
blk[LABEL_BLOCK - 1].admin.next = NULL;
|
|
}
|
|
|
|
static char * safe_alloc perm_alloc(size_t len)
|
|
{
|
|
char *p;
|
|
|
|
if (perm_tail->size - perm_tail->usage < len) {
|
|
size_t alloc_len = (len > PERMTS_SIZE) ? len : PERMTS_SIZE;
|
|
perm_tail->next = nasm_malloc(PERMTS_HEADER + alloc_len);
|
|
perm_tail = perm_tail->next;
|
|
perm_tail->next = NULL;
|
|
perm_tail->size = alloc_len;
|
|
perm_tail->usage = 0;
|
|
}
|
|
p = perm_tail->data + perm_tail->usage;
|
|
perm_tail->usage += len;
|
|
return p;
|
|
}
|
|
|
|
static char *perm_copy(const char *string)
|
|
{
|
|
char *p;
|
|
size_t len;
|
|
|
|
if (!string)
|
|
return NULL;
|
|
|
|
len = strlen(string)+1; /* Include final NUL */
|
|
|
|
p = perm_alloc(len);
|
|
memcpy(p, string, len);
|
|
|
|
return p;
|
|
}
|
|
|
|
static char *
|
|
perm_copy3(const char *s1, const char *s2, const char *s3)
|
|
{
|
|
char *p;
|
|
size_t l1 = strlen(s1);
|
|
size_t l2 = strlen(s2);
|
|
size_t l3 = strlen(s3)+1; /* Include final NUL */
|
|
|
|
p = perm_alloc(l1+l2+l3);
|
|
memcpy(p, s1, l1);
|
|
memcpy(p+l1, s2, l2);
|
|
memcpy(p+l1+l2, s3, l3);
|
|
|
|
return p;
|
|
}
|
|
|
|
const char *local_scope(const char *label)
|
|
{
|
|
return islocal(label) ? prevlabel : "";
|
|
}
|
|
|
|
/*
|
|
* Notes regarding bug involving redefinition of external segments.
|
|
*
|
|
* Up to and including v0.97, the following code didn't work. From 0.97
|
|
* developers release 2 onwards, it will generate an error.
|
|
*
|
|
* EXTERN extlabel
|
|
* newlabel EQU extlabel + 1
|
|
*
|
|
* The results of allowing this code through are that two import records
|
|
* are generated, one for 'extlabel' and one for 'newlabel'.
|
|
*
|
|
* The reason for this is an inadequacy in the defined interface between
|
|
* the label manager and the output formats. The problem lies in how the
|
|
* output format driver tells that a label is an external label for which
|
|
* a label import record must be produced. Most (all except bin?) produce
|
|
* the record if the segment number of the label is not one of the internal
|
|
* segments that the output driver is producing.
|
|
*
|
|
* A simple fix to this would be to make the output formats keep track of
|
|
* which symbols they've produced import records for, and make them not
|
|
* produce import records for segments that are already defined.
|
|
*
|
|
* The best way, which is slightly harder but reduces duplication of code
|
|
* and should therefore make the entire system smaller and more stable is
|
|
* to change the interface between assembler, define_label(), and
|
|
* the output module. The changes that are needed are:
|
|
*
|
|
* The semantics of the 'isextern' flag passed to define_label() need
|
|
* examining. This information may or may not tell us what we need to
|
|
* know (ie should we be generating an import record at this point for this
|
|
* label). If these aren't the semantics, the semantics should be changed
|
|
* to this.
|
|
*
|
|
* The output module interface needs changing, so that the `isextern' flag
|
|
* is passed to the module, so that it can be easily tested for.
|
|
*/
|