📄 outmacho.c
字号:
/* outmacho.c output routines for the Netwide Assembler to produce
* NeXTstep/OpenStep/Rhapsody/Darwin/MacOS X 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.
*/
/* Most of this file is, like Mach-O itself, based on a.out. For more
* guidelines see outaout.c. */
#include "compiler.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <inttypes.h>
#include "nasm.h"
#include "nasmlib.h"
#include "outform.h"
#include "compiler.h"
#if defined(OF_MACHO)
/* Mach-O in-file header structure sizes */
#define MACHO_HEADER_SIZE (28)
#define MACHO_SEGCMD_SIZE (56)
#define MACHO_SECTCMD_SIZE (68)
#define MACHO_SYMCMD_SIZE (24)
#define MACHO_NLIST_SIZE (12)
#define MACHO_RELINFO_SIZE (8)
/* Mach-O file header values */
#define MH_MAGIC (0xfeedface)
#define CPU_TYPE_I386 (7) /* x86 platform */
#define CPU_SUBTYPE_I386_ALL (3) /* all-x86 compatible */
#define MH_OBJECT (0x1) /* object file */
#define LC_SEGMENT (0x1) /* segment load command */
#define LC_SYMTAB (0x2) /* symbol table load command */
#define VM_PROT_NONE (0x00)
#define VM_PROT_READ (0x01)
#define VM_PROT_WRITE (0x02)
#define VM_PROT_EXECUTE (0x04)
#define VM_PROT_DEFAULT (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE)
#define VM_PROT_ALL (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE)
struct section {
/* nasm internal data */
struct section *next;
struct SAA *data;
int32_t index;
struct reloc *relocs;
int align;
/* data that goes into the file */
char sectname[16]; /* what this section is called */
char segname[16]; /* segment this section will be in */
uint32_t size; /* in-memory and -file size */
uint32_t nreloc; /* relocation entry count */
uint32_t flags; /* type and attributes (masked) */
};
#define SECTION_TYPE 0x000000ff /* section type mask */
#define S_REGULAR (0x0) /* standard section */
#define S_ZEROFILL (0x1) /* zerofill, in-memory only */
#define SECTION_ATTRIBUTES_SYS 0x00ffff00 /* system setable attributes */
#define S_ATTR_SOME_INSTRUCTIONS 0x00000400 /* section contains some
machine instructions */
#define S_ATTR_EXT_RELOC 0x00000200 /* section has external
relocation entries */
#define S_ATTR_LOC_RELOC 0x00000100 /* section has local
relocation entries */
static struct sectmap {
const char *nasmsect;
const char *segname;
const char *sectname;
const int32_t flags;
} sectmap[] = {
{".text", "__TEXT", "__text", S_REGULAR|S_ATTR_SOME_INSTRUCTIONS},
{".data", "__DATA", "__data", S_REGULAR},
{".rodata", "__DATA", "__const", S_REGULAR},
{".bss", "__DATA", "__bss", S_ZEROFILL},
{NULL, NULL, NULL, 0}
};
struct reloc {
/* nasm internal data */
struct reloc *next;
/* data that goes into the file */
int32_t addr; /* op's offset in section */
unsigned int snum:24, /* contains symbol index if
** ext otherwise in-file
** section number */
pcrel:1, /* relative relocation */
length:2, /* 0=byte, 1=word, 2=int32_t */
ext:1, /* external symbol referenced */
type:4; /* reloc type, 0 for us */
};
#define R_ABS 0 /* absolute relocation */
#define R_SCATTERED 0x80000000 /* reloc entry is scattered if
** highest bit == 1 */
struct symbol {
/* nasm internal data */
struct symbol *next; /* next symbol in the list */
char *name; /* name of this symbol */
int32_t initial_snum; /* symbol number used above in
reloc */
int32_t snum; /* true snum for reloc */
/* data that goes into the file */
int32_t strx; /* string table index */
uint8_t type; /* symbol type */
uint8_t sect; /* NO_SECT or section number */
int16_t desc; /* for stab debugging, 0 for us */
uint32_t value; /* offset of symbol in section */
};
/* symbol type bits */
#define N_EXT 0x01 /* global or external symbol */
#define N_UNDF 0x0 /* undefined symbol | n_sect == */
#define N_ABS 0x2 /* absolute symbol | NO_SECT */
#define N_SECT 0xe /* defined symbol, n_sect holds
** section number */
#define N_TYPE 0x0e /* type bit mask */
#define DEFAULT_SECTION_ALIGNMENT 0 /* byte (i.e. no) alignment */
/* special section number values */
#define NO_SECT 0 /* no section, invalid */
#define MAX_SECT 255 /* maximum number of sections */
static struct section *sects, **sectstail;
static struct symbol *syms, **symstail;
static uint32_t nsyms;
/* These variables are set by macho_layout_symbols() to organize
the symbol table and string table in order the dynamic linker
expects. They are then used in macho_write() to put out the
symbols and strings in that order.
The order of the symbol table is:
local symbols
defined external symbols (sorted by name)
undefined external symbols (sorted by name)
The order of the string table is:
strings for external symbols
strings for local symbols
*/
static uint32_t ilocalsym = 0;
static uint32_t iextdefsym = 0;
static uint32_t iundefsym = 0;
static uint32_t nlocalsym;
static uint32_t nextdefsym;
static uint32_t nundefsym;
static struct symbol **extdefsyms = NULL;
static struct symbol **undefsyms = NULL;
static struct RAA *extsyms;
static struct SAA *strs;
static uint32_t strslen;
static FILE *machofp;
static efunc error;
static evalfunc evaluate;
extern struct ofmt of_macho;
/* Global file information. This should be cleaned up into either
a structure or as function arguments. */
uint32_t head_ncmds = 0;
uint32_t head_sizeofcmds = 0;
uint32_t seg_filesize = 0;
uint32_t seg_vmsize = 0;
uint32_t seg_nsects = 0;
uint32_t rel_padcnt = 0;
#define xstrncpy(xdst, xsrc) \
memset(xdst, '\0', sizeof(xdst)); /* zero out whole buffer */ \
strncpy(xdst, xsrc, sizeof(xdst)); /* copy over string */ \
xdst[sizeof(xdst) - 1] = '\0'; /* proper null-termination */
#define align(x, y) \
(((x) + (y) - 1) & ~((y) - 1)) /* align x to multiple of y */
#define alignint32_t(x) \
align(x, sizeof(int32_t)) /* align x to int32_t boundary */
static void debug_reloc (struct reloc *);
static void debug_section_relocs (struct section *) _unused;
static int exact_log2 (uint32_t align)
{
if (align == 0) {
return 0;
} else if (align & (align-1)) {
return -1; /* Not a power of 2 */
} else {
#ifdef HAVE_GNUC_4
return __builtin_ctzl (align);
#else
uint32_t result = 0;
/* We know exactly one bit is set at this point. */
if (align & 0xffff0000)
result |= 16;
if (align & 0xff00ff00)
result |= 8;
if (align & 0xf0f0f0f0)
result |= 4;
if (align & 0xcccccccc)
result |= 2;
if (align & 0xaaaaaaaa)
result |= 1;
return result;
#endif
}
}
static struct section *get_section_by_name(const char *segname,
const char *sectname)
{
struct section *s;
for (s = sects; s != NULL; s = s->next)
if (!strcmp(s->segname, segname) && !strcmp(s->sectname, sectname))
break;
return s;
}
static struct section *get_section_by_index(const int32_t index)
{
struct section *s;
for (s = sects; s != NULL; s = s->next)
if (index == s->index)
break;
return s;
}
static int32_t get_section_index_by_name(const char *segname,
const char *sectname)
{
struct section *s;
for (s = sects; s != NULL; s = s->next)
if (!strcmp(s->segname, segname) && !strcmp(s->sectname, sectname))
return s->index;
return -1;
}
static char *get_section_name_by_index(const int32_t index)
{
struct section *s;
for (s = sects; s != NULL; s = s->next)
if (index == s->index)
return s->sectname;
return NULL;
}
static uint8_t get_section_fileindex_by_index(const int32_t index)
{
struct section *s;
uint8_t i = 1;
for (s = sects; s != NULL && i < MAX_SECT; s = s->next, ++i)
if (index == s->index)
return i;
if (i == MAX_SECT)
error(ERR_WARNING,
"too many sections (>255) - clipped by fileindex");
return NO_SECT;
}
static void macho_init(FILE * fp, efunc errfunc, ldfunc ldef,
evalfunc eval)
{
char zero = 0;
machofp = fp;
error = errfunc;
evaluate = eval;
(void)ldef; /* placate optimisers */
sects = NULL;
sectstail = §s;
syms = NULL;
symstail = &syms;
nsyms = 0;
nlocalsym = 0;
nextdefsym = 0;
nundefsym = 0;
extsyms = raa_init();
strs = saa_init(1L);
/* string table starts with a zero byte - don't ask why */
saa_wbytes(strs, &zero, sizeof(char));
strslen = 1;
}
static int macho_setinfo(enum geninfo type, char **val)
{
(void)type;
(void)val;
return 0;
}
static void sect_write(struct section *sect,
const uint8_t *data, uint32_t len)
{
saa_wbytes(sect->data, data, len);
sect->size += len;
}
static void add_reloc(struct section *sect, int32_t section,
int pcrel, int bytes)
{
struct reloc *r;
int32_t fi;
/* NeXT as puts relocs in reversed order (address-wise) into the
** files, so we do the same, doesn't seem to make much of a
** difference either way */
r = nasm_malloc(sizeof(struct reloc));
r->next = sect->relocs;
sect->relocs = r;
/* the current end of the section will be the symbol's address for
** now, might have to be fixed by macho_fixup_relocs() later on. make
** sure we don't make the symbol scattered by setting the highest
** bit by accident */
r->addr = sect->size & ~R_SCATTERED;
r->ext = 0;
r->pcrel = pcrel;
/* match byte count 1, 2, 4 to length codes 0, 1, 2 respectively */
r->length = bytes >> 1;
/* vanilla relocation (GENERIC_RELOC_VANILLA) */
r->type = 0;
if (section == NO_SEG) {
/* absolute local symbol if no section index given */
r->snum = R_ABS;
} else {
fi = get_section_fileindex_by_index(section);
if (fi == NO_SECT) {
/* external symbol if no section with that index known,
** symbol number was saved in macho_symdef() */
r->snum = raa_read(extsyms, section);
r->ext = 1;
} else {
/* local symbol in section fi */
r->snum = fi;
}
}
++sect->nreloc;
}
static void macho_output(int32_t secto, const void *data, uint32_t type,
int32_t section, int32_t wrt)
{
struct section *s, *sbss;
int32_t realbytes = type & OUT_SIZMASK;
int32_t addr;
uint8_t mydata[4], *p;
type &= OUT_TYPMASK;
if (wrt != NO_SEG) {
wrt = NO_SEG;
error(ERR_NONFATAL, "WRT not supported by Mach-O output format");
/* continue to do _something_ */
}
if (secto == NO_SEG) {
if (type != OUT_RESERVE)
error(ERR_NONFATAL, "attempt to assemble code in "
"[ABSOLUTE] space");
return;
}
s = get_section_by_index(secto);
if (s == NULL) {
error(ERR_WARNING, "attempt to assemble code in"
" section %d: defaulting to `.text'", secto);
s = get_section_by_name("__TEXT", "__text");
/* should never happen */
if (s == NULL)
error(ERR_PANIC, "text section not found");
}
sbss = get_section_by_name("__DATA", "__bss");
if (s == sbss && type != OUT_RESERVE) {
error(ERR_WARNING, "attempt to initialize memory in the"
" BSS section: ignored");
switch (type) {
case OUT_REL2ADR:
realbytes = 2;
break;
case OUT_REL4ADR:
realbytes = 4;
break;
default:
break;
}
s->size += realbytes;
return;
}
switch (type) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -