📄 outaout.c
字号:
/* outaout.c output routines for the Netwide Assembler to produce
* Linux a.out object files
*
* The Netwide Assembler is copyright (C) 1996 Simon Tatham and
* Julian Hall. All rights reserved. The software is
* redistributable under the licence given in the file "Licence"
* distributed in the NASM archive.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "nasm.h"
#include "nasmlib.h"
#include "outform.h"
#if defined OF_AOUT || defined OF_AOUTB
#define RELTYPE_ABSOLUTE 0x00
#define RELTYPE_RELATIVE 0x01
#define RELTYPE_GOTPC 0x01 /* no explicit GOTPC in a.out */
#define RELTYPE_GOTOFF 0x10
#define RELTYPE_GOT 0x10 /* distinct from GOTOFF bcos sym not sect */
#define RELTYPE_PLT 0x21
#define RELTYPE_SYMFLAG 0x08
struct Reloc {
struct Reloc *next;
long address; /* relative to _start_ of section */
long symbol; /* symbol number or -ve section id */
int bytes; /* 2 or 4 */
int reltype; /* see above */
};
struct Symbol {
long strpos; /* string table position of name */
int type; /* symbol type - see flags below */
long value; /* address, or COMMON variable size */
long size; /* size for data or function exports */
long segment; /* back-reference used by gsym_reloc */
struct Symbol *next; /* list of globals in each section */
struct Symbol *nextfwd; /* list of unresolved-size symbols */
char *name; /* for unresolved-size symbols */
long symnum; /* index into symbol table */
};
/*
* Section IDs - used in Reloc.symbol when negative, and in
* Symbol.type when positive.
*/
#define SECT_ABS 2 /* absolute value */
#define SECT_TEXT 4 /* text section */
#define SECT_DATA 6 /* data section */
#define SECT_BSS 8 /* bss section */
#define SECT_MASK 0xE /* mask out any of the above */
/*
* More flags used in Symbol.type.
*/
#define SYM_GLOBAL 1 /* it's a global symbol */
#define SYM_DATA 0x100 /* used for shared libs */
#define SYM_FUNCTION 0x200 /* used for shared libs */
#define SYM_WITH_SIZE 0x4000 /* not output; internal only */
/*
* Bit more explanation of symbol types: SECT_xxx denotes a local
* symbol. SECT_xxx|SYM_GLOBAL denotes a global symbol, defined in
* this module. Just SYM_GLOBAL, with zero value, denotes an
* external symbol referenced in this module. And just SYM_GLOBAL,
* but with a non-zero value, declares a C `common' variable, of
* size `value'.
*/
struct Section {
struct SAA *data;
unsigned long len, size, nrelocs;
long index;
struct Reloc *head, **tail;
struct Symbol *gsyms, *asym;
};
static struct Section stext, sdata, sbss;
static struct SAA *syms;
static unsigned long nsyms;
static struct RAA *bsym;
static struct SAA *strs;
static unsigned long strslen;
static struct Symbol *fwds;
static FILE *aoutfp;
static efunc error;
static evalfunc evaluate;
static int bsd;
static int is_pic;
static void aout_write(void);
static void aout_write_relocs(struct Reloc *);
static void aout_write_syms(void);
static void aout_sect_write(struct Section *, const unsigned char *, unsigned long);
static void aout_pad_sections(void);
static void aout_fixup_relocs(struct Section *);
/*
* Special section numbers which are used to define special
* symbols, which can be used with WRT to provide PIC relocation
* types.
*/
static long aout_gotpc_sect, aout_gotoff_sect;
static long aout_got_sect, aout_plt_sect;
static long aout_sym_sect;
static void aoutg_init(FILE *fp, efunc errfunc, ldfunc ldef, evalfunc eval)
{
aoutfp = fp;
error = errfunc;
evaluate = eval;
(void) ldef; /* placate optimisers */
stext.data = saa_init(1L); stext.head = NULL; stext.tail = &stext.head;
sdata.data = saa_init(1L); sdata.head = NULL; sdata.tail = &sdata.head;
stext.len = stext.size = sdata.len = sdata.size = sbss.len = 0;
stext.nrelocs = sdata.nrelocs = 0;
stext.gsyms = sdata.gsyms = sbss.gsyms = NULL;
stext.index = seg_alloc();
sdata.index = seg_alloc();
sbss.index = seg_alloc();
stext.asym = sdata.asym = sbss.asym = NULL;
syms = saa_init((long)sizeof(struct Symbol));
nsyms = 0;
bsym = raa_init();
strs = saa_init(1L);
strslen = 0;
fwds = NULL;
}
#ifdef OF_AOUT
static void aout_init(FILE *fp, efunc errfunc, ldfunc ldef, evalfunc eval)
{
bsd = FALSE;
aoutg_init (fp, errfunc, ldef, eval);
aout_gotpc_sect = aout_gotoff_sect = aout_got_sect =
aout_plt_sect = aout_sym_sect = NO_SEG;
}
#endif
#ifdef OF_AOUTB
extern struct ofmt of_aoutb;
static void aoutb_init(FILE *fp, efunc errfunc, ldfunc ldef, evalfunc eval)
{
bsd = TRUE;
aoutg_init (fp, errfunc, ldef, eval);
is_pic = 0x00; /* may become 0x40 */
aout_gotpc_sect = seg_alloc();
ldef("..gotpc", aout_gotpc_sect+1, 0L, NULL, FALSE,FALSE,&of_aoutb,error);
aout_gotoff_sect = seg_alloc();
ldef("..gotoff", aout_gotoff_sect+1, 0L,NULL,FALSE,FALSE,&of_aoutb,error);
aout_got_sect = seg_alloc();
ldef("..got", aout_got_sect+1, 0L, NULL, FALSE,FALSE,&of_aoutb,error);
aout_plt_sect = seg_alloc();
ldef("..plt", aout_plt_sect+1, 0L, NULL, FALSE,FALSE,&of_aoutb,error);
aout_sym_sect = seg_alloc();
ldef("..sym", aout_sym_sect+1, 0L, NULL, FALSE,FALSE,&of_aoutb,error);
}
#endif
static void aout_cleanup(int debuginfo)
{
struct Reloc *r;
(void) debuginfo;
aout_pad_sections();
aout_fixup_relocs(&stext);
aout_fixup_relocs(&sdata);
aout_write();
fclose (aoutfp);
saa_free (stext.data);
while (stext.head) {
r = stext.head;
stext.head = stext.head->next;
nasm_free (r);
}
saa_free (sdata.data);
while (sdata.head) {
r = sdata.head;
sdata.head = sdata.head->next;
nasm_free (r);
}
saa_free (syms);
raa_free (bsym);
saa_free (strs);
}
static long aout_section_names (char *name, int pass, int *bits)
{
/*
* Default to 32 bits.
*/
if (!name)
*bits = 32;
if (!name)
return stext.index;
if (!strcmp(name, ".text"))
return stext.index;
else if (!strcmp(name, ".data"))
return sdata.index;
else if (!strcmp(name, ".bss"))
return sbss.index;
else
return NO_SEG;
}
static void aout_deflabel (char *name, long segment, long offset,
int is_global, char *special)
{
int pos = strslen+4;
struct Symbol *sym;
int special_used = FALSE;
if (name[0] == '.' && name[1] == '.' && name[2] != '@') {
/*
* This is a NASM special symbol. We never allow it into
* the a.out symbol table, even if it's a valid one. If it
* _isn't_ a valid one, we should barf immediately.
*/
if (strcmp(name, "..gotpc") && strcmp(name, "..gotoff") &&
strcmp(name, "..got") && strcmp(name, "..plt") &&
strcmp(name, "..sym"))
error (ERR_NONFATAL, "unrecognised special symbol `%s'", name);
return;
}
if (is_global == 3) {
struct Symbol **s;
/*
* Fix up a forward-reference symbol size from the first
* pass.
*/
for (s = &fwds; *s; s = &(*s)->nextfwd)
if (!strcmp((*s)->name, name)) {
struct tokenval tokval;
expr *e;
char *p = special;
while (*p && !isspace(*p)) p++;
while (*p && isspace(*p)) p++;
stdscan_reset();
stdscan_bufptr = p;
tokval.t_type = TOKEN_INVALID;
e = evaluate(stdscan, NULL, &tokval, NULL, 1, error, NULL);
if (e) {
if (!is_simple(e))
error (ERR_NONFATAL, "cannot use relocatable"
" expression as symbol size");
else
(*s)->size = reloc_value(e);
}
/*
* Remove it from the list of unresolved sizes.
*/
nasm_free ((*s)->name);
*s = (*s)->nextfwd;
return;
}
return; /* it wasn't an important one */
}
saa_wbytes (strs, name, (long)(1+strlen(name)));
strslen += 1+strlen(name);
sym = saa_wstruct (syms);
sym->strpos = pos;
sym->type = is_global ? SYM_GLOBAL : 0;
sym->segment = segment;
if (segment == NO_SEG)
sym->type |= SECT_ABS;
else if (segment == stext.index) {
sym->type |= SECT_TEXT;
if (is_global) {
sym->next = stext.gsyms;
stext.gsyms = sym;
} else if (!stext.asym)
stext.asym = sym;
} else if (segment == sdata.index) {
sym->type |= SECT_DATA;
if (is_global) {
sym->next = sdata.gsyms;
sdata.gsyms = sym;
} else if (!sdata.asym)
sdata.asym = sym;
} else if (segment == sbss.index) {
sym->type |= SECT_BSS;
if (is_global) {
sym->next = sbss.gsyms;
sbss.gsyms = sym;
} else if (!sbss.asym)
sbss.asym = sym;
} else
sym->type = SYM_GLOBAL;
if (is_global == 2)
sym->value = offset;
else
sym->value = (sym->type == SYM_GLOBAL ? 0 : offset);
if (is_global && sym->type != SYM_GLOBAL) {
/*
* Global symbol exported _from_ this module. We must check
* the special text for type information.
*/
if (special) {
int n = strcspn(special, " ");
if (!nasm_strnicmp(special, "function", n))
sym->type |= SYM_FUNCTION;
else if (!nasm_strnicmp(special, "data", n) ||
!nasm_strnicmp(special, "object", n))
sym->type |= SYM_DATA;
else
error(ERR_NONFATAL, "unrecognised symbol type `%.*s'",
n, special);
if (special[n]) {
struct tokenval tokval;
expr *e;
int fwd = FALSE;
char *saveme=stdscan_bufptr; /* bugfix? fbk 8/10/00 */
if (!bsd) {
error(ERR_NONFATAL, "Linux a.out does not support"
" symbol size information");
} else {
while (special[n] && isspace(special[n]))
n++;
/*
* We have a size expression; attempt to
* evaluate it.
*/
sym->type |= SYM_WITH_SIZE;
stdscan_reset();
stdscan_bufptr = special+n;
tokval.t_type = TOKEN_INVALID;
e = evaluate(stdscan, NULL, &tokval, &fwd, 0, error, NULL);
if (fwd) {
sym->nextfwd = fwds;
fwds = sym;
sym->name = nasm_strdup(name);
} else if (e) {
if (!is_simple(e))
error (ERR_NONFATAL, "cannot use relocatable"
" expression as symbol size");
else
sym->size = reloc_value(e);
}
}
stdscan_bufptr=saveme; /* bugfix? fbk 8/10/00 */
}
special_used = TRUE;
}
}
/*
* define the references from external-symbol segment numbers
* to these symbol records.
*/
if (segment != NO_SEG && segment != stext.index &&
segment != sdata.index && segment != sbss.index)
bsym = raa_write (bsym, segment, nsyms);
sym->symnum = nsyms;
nsyms++;
if (sym->type & SYM_WITH_SIZE)
nsyms++; /* and another for the size */
if (special && !special_used)
error(ERR_NONFATAL, "no special symbol features supported here");
}
static void aout_add_reloc (struct Section *sect, long segment,
int reltype, int bytes)
{
struct Reloc *r;
r = *sect->tail = nasm_malloc(sizeof(struct Reloc));
sect->tail = &r->next;
r->next = NULL;
r->address = sect->len;
r->symbol = (segment == NO_SEG ? -SECT_ABS :
segment == stext.index ? -SECT_TEXT :
segment == sdata.index ? -SECT_DATA :
segment == sbss.index ? -SECT_BSS :
raa_read(bsym, segment));
r->reltype = reltype;
if (r->symbol >= 0)
r->reltype |= RELTYPE_SYMFLAG;
r->bytes = bytes;
sect->nrelocs++;
}
/*
* This routine deals with ..got and ..sym relocations: the more
* complicated kinds. In shared-library writing, some relocations
* with respect to global symbols must refer to the precise symbol
* rather than referring to an offset from the base of the section
* _containing_ the symbol. Such relocations call to this routine,
* which searches the symbol list for the symbol in question.
*
* RELTYPE_GOT references require the _exact_ symbol address to be
* used; RELTYPE_ABSOLUTE references can be at an offset from the
* symbol. The boolean argument `exact' tells us this.
*
* Return value is the adjusted value of `addr', having become an
* offset from the symbol rather than the section. Should always be
* zero when returning from an exact call.
*
* Limitation: if you define two symbols at the same place,
* confusion will occur.
*
* Inefficiency: we search, currently, using a linked list which
* isn't even necessarily sorted.
*/
static long aout_add_gsym_reloc (struct Section *sect,
long segment, long offset,
int type, int bytes, int exact)
{
struct Symbol *sym, *sm, *shead;
struct Reloc *r;
/*
* First look up the segment to find whether it's text, data,
* bss or an external symbol.
*/
shead = NULL;
if (segment == stext.index)
shead = stext.gsyms;
else if (segment == sdata.index)
shead = sdata.gsyms;
else if (segment == sbss.index)
shead = sbss.gsyms;
if (!shead) {
if (exact && offset != 0)
error (ERR_NONFATAL, "unable to find a suitable global symbol"
" for this reference");
else
aout_add_reloc (sect, segment, type, bytes);
return offset;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -