NASM 0.96

This commit is contained in:
H. Peter Anvin 2002-04-30 20:52:49 +00:00
parent 6768eb71d8
commit 76690a12ad
74 changed files with 18239 additions and 3854 deletions

166
Changes
View file

@ -129,8 +129,8 @@ be output when absolute labels were made global.
Updates to RDOFF subdirectory, and changes to outrdf.c.
0.95 released July 1997
-----------------------
0.95 not released yet
---------------------
Fixed yet another ELF bug. This one manifested if the user relied on
the default segment, and attempted to define global symbols without
@ -241,3 +241,165 @@ can be implemented.
Fixed the implementation of WRT, which was too restrictive in that
you couldn't do `mov ax,[di+abc wrt dgroup]' because (di+abc) wasn't
a relocatable reference.
0.96 not released yet
---------------------
Fixed a bug whereby, if `nasm sourcefile' would cause a filename
collision warning and put output into `nasm.out', then `nasm
sourcefile -o outputfile' still gave the warning even though the
`-o' was honoured.
Fixed name pollution under Digital UNIX: one of its header files
defined R_SP, which broke the enum in nasm.h.
Fixed minor instruction table problems: FUCOM and FUCOMP didn't have
two-operand forms; NDISASM didn't recognise the longer register
forms of PUSH and POP (eg FF F3 for PUSH BX); TEST mem,imm32 was
flagged as undocumented; the 32-bit forms of CMOV had 16-bit operand
size prefixes; `AAD imm' and `AAM imm' are no longer flagged as
undocumented because the Intel Architecture reference documents
them.
Fixed a problem with the local-label mechanism, whereby strange
types of symbol (EQUs, auto-defined OBJ segment base symbols)
interfered with the `previous global label' value and screwed up
local labels.
Fixed a bug whereby the stub preprocessor didn't communicate with
the listing file generator, so that the -a and -l options in
conjunction would produce a useless listing file.
Merged `os2' object file format back into `obj', after discovering
that `obj' _also_ shouldn't have a link pass separator in a module
containing a non-trivial MODEND. Flat segments are now declared
using the FLAT attribute. `os2' is no longer a valid object format
name: use `obj'.
Removed the fixed-size temporary storage in the evaluator. Very very
long expressions (like `mov ax,1+1+1+1+...' for two hundred 1s or
so) should now no longer crash NASM.
Fixed a bug involving segfaults on disassembly of MMX instructions,
by changing the meaning of one of the operand-type flags in nasm.h.
This may cause other apparently unrelated MMX problems; it needs to
be tested thoroughly.
Fixed some buffer overrun problems with large OBJ output files.
Thanks to DJ Delorie for the bug report and fix.
Made preprocess-only mode actually listen to the %line markers as it
prints them, so that it can report errors more sanely.
Re-designed the evaluator to keep more sensible track of expressions
involving forward references: can now cope with previously-nightmare
situations such as
mov ax,foo | bar
foo equ 1
bar equ 2
Added the ALIGN and ALIGNB standard macros.
Added PIC support in ELF: use of WRT to obtain the four extra
relocation types needed.
Added the ability for output file formats to define their own
extensions to the GLOBAL, COMMON and EXTERN directives.
Implemented common-variable alignment, and global-symbol type and
size declarations, in ELF.
Implemented NEAR and FAR keywords for common variables, plus
far-common element size specification, in OBJ.
Added a feature whereby EXTERNs and COMMONs in OBJ can be given a
default WRT specification (either a segment or a group).
Transformed the Unix NASM archive into an auto-configuring package.
Added a sanity-check for people applying SEG to things which are
already segment bases: this previously went unnoticed by the SEG
processing and caused OBJ-driver panics later.
Added the ability, in OBJ format, to deal with `MOV EAX,<segment>'
type references: OBJ doesn't directly support dword-size segment
base fixups, but as long as the low two bytes of the constant term
are zero, a word-size fixup can be generated instead and it will
work.
Added the ability to specify sections' alignment requirements in
Win32 object files and pure binary files.
Added preprocess-time expression evaluation: the %assign (and
%iassign) directive and the bare %if (and %elif) conditional. Added
relational operators to the evaluator, for use only in %if
constructs: the standard relationals = < > <= >= <> (and C-like
synonyms == and !=) plus low-precedence logical operators &&, ^^ and
||.
Added a preprocessor repeat construct: %rep / %exitrep / %endrep.
Added the __FILE__ and __LINE__ standard macros.
Added a sanity check for number constants being greater than
0xFFFFFFFF. The warning can be disabled.
Added the %0 token whereby a variadic multi-line macro can tell how
many parameters it's been given in a specific invocation.
Added %rotate, allowing multi-line macro parameters to be cycled.
Added the `*' option for the maximum parameter count on multi-line
macros, allowing them to take arbitrarily many parameters.
Added the ability for the user-level forms of EXTERN, GLOBAL and
COMMON to take more than one argument.
Added the IMPORT and EXPORT directives in OBJ format, to deal with
Windows DLLs.
Added some more preprocessor %if constructs: %ifidn / %ifidni (exact
textual identity), and %ifid / %ifnum / %ifstr (token type testing).
Added the ability to distinguish SHL AX,1 (the 8086 version) from
SHL AX,BYTE 1 (the 286-and-upwards version whose constant happens to
be 1).
Added NetBSD/FreeBSD/OpenBSD's variant of a.out format, complete
with PIC shared library features.
Changed NASM's idiosyncratic handling of FCLEX, FDISI, FENI, FINIT,
FSAVE, FSTCW, FSTENV, and FSTSW to bring it into line with the
otherwise accepted standard. The previous behaviour, though it was a
deliberate feature, was a deliberate feature based on a
misunderstanding. Apologies for the inconvenience.
Improved the flexibility of ABSOLUTE: you can now give it an
expression rather than being restricted to a constant, and it can
take relocatable arguments as well.
Added the ability for a variable to be declared as EXTERN multiple
times, and the subsequent definitions are just ignored.
We now allow instruction prefixes (CS, DS, LOCK, REPZ etc) to be
alone on a line (without a following instruction).
Improved sanity checks on whether the arguments to EXTERN, GLOBAL
and COMMON are valid identifiers.
Added misc/exebin.mac to allow direct generation of .EXE files by
hacking up an EXE header using DB and DW; also added test/binexe.asm
to demonstrate the use of this. Thanks to Yann Guidon for
contributing the EXE header code.
ndisasm forgot to check whether the input file had been successfully
opened. Now it does. Doh!
Added the Cyrix extensions to the MMX instruction set.
Added a hinting mechanism to allow [EAX+EBX] and [EBX+EAX] to be
assembled differently. This is important since [ESI+EBP] and
[EBP+ESI] have different default base segment registers.
Added support for the PharLap OMF extension for 4096-byte segment
alignment.

109
Makefile
View file

@ -1,109 +0,0 @@
# Makefile for the Netwide Assembler
#
# 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.
#
# This Makefile is designed for use under Unix (probably fairly
# portably). It can also be used without change to build NASM using
# DJGPP. The makefile "Makefile.dos" can be used to build NASM using
# a 16-bit DOS C compiler such as Microsoft C.
#
# The `make dist' section at the end of the Makefile is not
# guaranteed to work anywhere except Linux. Come to think of it,
# I'm not sure I want to guarantee it to work anywhere except on
# _my_ computer. :-)
CC = gcc
CCFLAGS = -c -g -O -Wall -ansi -pedantic
LINK = gcc
LINKFLAGS = -o nasm
DLINKFLAGS = -o ndisasm
LIBRARIES =
STRIP = strip
EXE =#
OBJ = o#
.c.$(OBJ):
$(CC) $(CCFLAGS) $*.c
NASMOBJS = nasm.$(OBJ) nasmlib.$(OBJ) float.$(OBJ) insnsa.$(OBJ) \
assemble.$(OBJ) labels.$(OBJ) parser.$(OBJ) outform.$(OBJ) \
outbin.$(OBJ) outaout.$(OBJ) outcoff.$(OBJ) outelf.$(OBJ) \
outobj.$(OBJ) outas86.$(OBJ) outrdf.$(OBJ) outdbg.$(OBJ) \
preproc.$(OBJ) listing.$(OBJ)
NDISASMOBJS = ndisasm.$(OBJ) disasm.$(OBJ) sync.$(OBJ) nasmlib.$(OBJ) \
insnsd.$(OBJ)
all : nasm$(EXE) ndisasm$(EXE)
nasm$(EXE): $(NASMOBJS)
$(LINK) $(LINKFLAGS) $(NASMOBJS) $(LIBRARIES)
ndisasm$(EXE): $(NDISASMOBJS)
$(LINK) $(DLINKFLAGS) $(NDISASMOBJS) $(LIBRARIES)
assemble.$(OBJ): assemble.c nasm.h nasmlib.h assemble.h insns.h
disasm.$(OBJ): disasm.c nasm.h disasm.h sync.h insns.h names.c
float.$(OBJ): float.c nasm.h
insnsa.$(OBJ): insnsa.c nasm.h insns.h
insnsd.$(OBJ): insnsd.c nasm.h insns.h
labels.$(OBJ): labels.c nasm.h nasmlib.h
listing.$(OBJ): listing.c nasm.h nasmlib.h listing.h
macros.$(OBJ): macros.c
names.$(OBJ): names.c
nasm.$(OBJ): nasm.c nasm.h nasmlib.h preproc.h parser.h assemble.h labels.h \
outform.h listing.h
nasmlib.$(OBJ): nasmlib.c nasm.h nasmlib.h
ndisasm.$(OBJ): ndisasm.c nasm.h nasmlib.h sync.h disasm.h
outaout.$(OBJ): outaout.c nasm.h nasmlib.h outform.h
outas86.$(OBJ): outas86.c nasm.h nasmlib.h outform.h
outbin.$(OBJ): outbin.c nasm.h nasmlib.h outform.h
outcoff.$(OBJ): outcoff.c nasm.h nasmlib.h outform.h
outdbg.$(OBJ): outdbg.c nasm.h nasmlib.h outform.h
outelf.$(OBJ): outelf.c nasm.h nasmlib.h outform.h
outform.$(OBJ): outform.c outform.h nasm.h
outobj.$(OBJ): outobj.c nasm.h nasmlib.h outform.h
outrdf.$(OBJ): outrdf.c nasm.h nasmlib.h outform.h
parser.$(OBJ): parser.c nasm.h nasmlib.h parser.h float.h names.c
preproc.$(OBJ): preproc.c nasm.h nasmlib.h macros.c
sync.$(OBJ): sync.c sync.h
# These two source files are automagically generated from a single
# instruction-table file by a Perl script. They're distributed,
# though, so it isn't necessary to have Perl just to recompile NASM
# from the distribution.
AUTOSRCS = insnsa.c insnsd.c
$(AUTOSRCS): insns.dat insns.pl
perl insns.pl
# This source file is generated from the standard macros file
# `standard.mac' by another Perl script. Again, it's part of the
# standard distribution.
macros.c: standard.mac
perl macros.pl
# Clean the whole thing up after compilation.
clean :
rm -f $(NASMOBJS) $(NDISASMOBJS) nasm$(EXE) ndisasm$(EXE)
make -C rdoff clean
make -C test clean
# Here the `make dist' section begins. Nothing is guaranteed hereafter
# unless you're using the Makefile under Linux, running bash, with
# gzip, GNU tar and a sensible version of zip readily available.
MANPAGES = nasm.man ndisasm.man
.SUFFIXES: .man .1
.1.man:
-man ./$< | ul > $@
dist: $(AUTOSRCS) $(MANPAGES) clean
makedist.sh

View file

@ -69,7 +69,7 @@ DCCFLAGS = /d /c /O /A /mh /n$(OBJD) #compiler flags for NDISASM
#/A=ANSI standard C
#/mh=Model huge
#/n$(OBJD)= put the OBJ files in the diectory given.
#NOTE: Huge modle is used, and the array in insnsd.c is large enough to
#NOTE: Huge model is used, and the array in insnsd.c is large enough to
#over size the d-group in large mode.
LINKFLAGS = /c /x #linker flags
@ -95,7 +95,7 @@ DASM_ASM=$(CC) $(DCCFLAGS) $&.c #command line for NDISASM
NASMOBJS = $(OBJD)nasm.$(OBJ) $(OBJD)nasmlib.$(OBJ) $(OBJD)float.$(OBJ) \
$(OBJD)insnsa.$(OBJ) $(OBJD)assemble.$(OBJ) $(OBJD)labels.$(OBJ) \
$(OBJD)parser.$(OBJ) $(OBJD)outform.$(OBJ) $(OBJD)preproc.$(OBJ) \
$(OBJD)listing.$(OBJ)
$(OBJD)listing.$(OBJ) $(OBJD)eval.$(OBJ)
################################################################
#The OBJ files that NDISASM is dependent on
@ -150,6 +150,9 @@ $(OBJD)labels.$(OBJ): labels.c nasm.h nasmlib.h
$(OBJD)listing.$(OBJ): listing.c nasm.h nasmlib.h listing.h
$(NASM_ASM)
$(OBJD)eval.$(OBJ): eval.c nasm.h nasmlib.h eval.h
$(NASM_ASM)
$(OBJD)nasm.$(OBJ): nasm.c nasm.h nasmlib.h parser.h assemble.h labels.h \
listing.h outform.h
$(NASM_ASM)

View file

@ -27,7 +27,7 @@ NASMOBJS1 = nasm.$(OBJ) nasmlib.$(OBJ) float.$(OBJ) insnsa.$(OBJ)
NASMOBJS2 = assemble.$(OBJ) labels.$(OBJ) parser.$(OBJ) outform.$(OBJ)
NASMOBJS3 = outbin.$(OBJ) outaout.$(OBJ) outcoff.$(OBJ) outelf.$(OBJ)
NASMOBJS4 = outobj.$(OBJ) outas86.$(OBJ) outdbg.$(OBJ) outrdf.$(OBJ)
NASMOBJS5 = preproc.$(OBJ) listing.$(OBJ)
NASMOBJS5 = preproc.$(OBJ) listing.$(OBJ) eval.$(OBJ)
NASMOBJS = $(NASMOBJS1) $(NASMOBJS2) $(NASMOBJS3) $(NASMOBJS4) $(NASMOBJS5)
@ -53,6 +53,7 @@ ndisasm$(EXE): $(NDISASMOBJS)
assemble.$(OBJ): assemble.c nasm.h assemble.h insns.h
disasm.$(OBJ): disasm.c nasm.h disasm.h sync.h insns.h names.c
eval.$(OBJ): eval.c nasm.h nasmlib.h eval.h
float.$(OBJ): float.c nasm.h
insnsa.$(OBJ): insnsa.c nasm.h insns.h
insnsd.$(OBJ): insnsd.c nasm.h insns.h

View file

@ -10,8 +10,8 @@
# It's been tested with Microsoft C 5.x plus Borland Make. (Yes, I
# know it's silly, but...)
CC = cl /c /O /AL
QCL = qcl /c /AL
CC = cl /c /O /AL /Gt
QCL = qcl /c /AL /Gt
LINK = cl
LINKFLAGS =
LIBRARIES =
@ -25,7 +25,7 @@ NASMOBJS = nasm.$(OBJ) nasmlib.$(OBJ) float.$(OBJ) insnsa.$(OBJ) \
assemble.$(OBJ) labels.$(OBJ) parser.$(OBJ) outform.$(OBJ) \
outbin.$(OBJ) outaout.$(OBJ) outcoff.$(OBJ) outelf.$(OBJ) \
outobj.$(OBJ) outas86.$(OBJ) outrdf.$(OBJ) outdbg.$(OBJ) \
preproc.$(OBJ) listing.$(OBJ)
preproc.$(OBJ) listing.$(OBJ) eval.$(OBJ)
NDISASMOBJS = ndisasm.$(OBJ) disasm.$(OBJ) sync.$(OBJ) nasmlib.$(OBJ) \
insnsd.$(OBJ)
@ -34,7 +34,7 @@ all : nasm$(EXE) ndisasm$(EXE)
# We have to have a horrible kludge here to get round the 128 character
# limit, as usual...
LINKOBJS = a*.obj f*.obj insnsa.obj l*.obj na*.obj o*.obj p*.obj
LINKOBJS = a*.obj e*.obj f*.obj insnsa.obj l*.obj na*.obj o*.obj p*.obj
nasm$(EXE): $(NASMOBJS)
cl /Fenasm.exe /F 4000 $(LINKOBJS)
@ -43,6 +43,7 @@ ndisasm$(EXE): $(NDISASMOBJS)
assemble.$(OBJ): assemble.c nasm.h assemble.h insns.h
disasm.$(OBJ): disasm.c nasm.h disasm.h sync.h insns.h names.c
eval.$(OBJ): eval.c eval.h nasm.h nasmlib.h
float.$(OBJ): float.c nasm.h
labels.$(OBJ): labels.c nasm.h nasmlib.h
listing.$(OBJ): listing.c nasm.h nasmlib.h listing.h

99
Makefile.in Normal file
View file

@ -0,0 +1,99 @@
#
# Auto-configuring Makefile for the Netwide Assembler.
#
# 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.
srcdir = @srcdir@
VPATH = @srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
mandir = @mandir@
CC = @CC@
CFLAGS = @CFLAGS@ @GCCFLAGS@ -I$(srcdir) -I.
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_DATA = @INSTALL_DATA@
.c.o:
$(CC) -c $(CFLAGS) $<
NASM = nasm.o nasmlib.o float.o insnsa.o assemble.o labels.o \
parser.o outform.o outbin.o outaout.o outcoff.o outelf.o \
outobj.o outas86.o outrdf.o outdbg.o preproc.o listing.o \
eval.o
NDISASM = ndisasm.o disasm.o sync.o nasmlib.o insnsd.o
all: nasm ndisasm
nasm: $(NASM)
$(CC) -o nasm $(NASM)
ndisasm: $(NDISASM)
$(CC) -o ndisasm $(NDISASM)
assemble.o: assemble.c nasm.h nasmlib.h assemble.h insns.h
disasm.o: disasm.c nasm.h disasm.h sync.h insns.h names.c
eval.o: eval.c eval.h nasm.h nasmlib.h
float.o: float.c nasm.h
insnsa.o: insnsa.c nasm.h insns.h
insnsd.o: insnsd.c nasm.h insns.h
labels.o: labels.c nasm.h nasmlib.h
listing.o: listing.c nasm.h nasmlib.h listing.h
nasm.o: nasm.c nasm.h nasmlib.h preproc.h parser.h assemble.h labels.h \
outform.h listing.h
nasmlib.o: nasmlib.c nasm.h nasmlib.h
ndisasm.o: ndisasm.c nasm.h nasmlib.h sync.h disasm.h
outaout.o: outaout.c nasm.h nasmlib.h outform.h
outas86.o: outas86.c nasm.h nasmlib.h outform.h
outbin.o: outbin.c nasm.h nasmlib.h outform.h
outcoff.o: outcoff.c nasm.h nasmlib.h outform.h
outdbg.o: outdbg.c nasm.h nasmlib.h outform.h
outelf.o: outelf.c nasm.h nasmlib.h outform.h
outform.o: outform.c outform.h nasm.h
outobj.o: outobj.c nasm.h nasmlib.h outform.h
outrdf.o: outrdf.c nasm.h nasmlib.h outform.h
parser.o: parser.c nasm.h nasmlib.h parser.h float.h names.c
preproc.o: preproc.c nasm.h nasmlib.h macros.c
sync.o: sync.c sync.h
# These two source files are automagically generated from a single
# instruction-table file by a Perl script. They're distributed,
# though, so it isn't necessary to have Perl just to recompile NASM
# from the distribution.
insnsa.c insnsd.c: insns.dat insns.pl
perl $(srcdir)/insns.pl $(srcdir)/insns.dat
# This source file is generated from the standard macros file
# `standard.mac' by another Perl script. Again, it's part of the
# standard distribution.
macros.c: standard.mac macros.pl
perl $(srcdir)/macros.pl $(srcdir)/standard.mac
install: nasm ndisasm
$(INSTALL_PROGRAM) nasm $(bindir)/nasm
$(INSTALL_PROGRAM) ndisasm $(bindir)/ndisasm
$(INSTALL_DATA) $(srcdir)/nasm.1 $(mandir)/man1/nasm.1
$(INSTALL_DATA) $(srcdir)/ndisasm.1 $(mandir)/man1/ndisasm.1
clean:
rm -f *.o nasm ndisasm
cd rdoff; $(MAKE) clean
spotless: clean
rm -f config.* Makefile
cd rdoff; $(MAKE) spotless
rdf:
cd rdoff; $(MAKE)
rdf_install install_rdf:
cd rdoff; $(MAKE) install

View file

@ -1,222 +1,114 @@
# Makefile for the Netwide Assembler under 32-bit Windows(tm)
# Makefile for the Netwide Assembler under 32-bit DOS(tm)
#
# 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.
#
# This Makefile is designed to build NASM using the 32-bit WIN32 C
# compiler Symantec(tm) C++ 7.5, provided you have a MAKE-utility
# that's compatible to SMAKE.
CC = sc
CCFLAGS = -c -a1 -mn -Nc -w2 -w7 -o+time -5
CCFLAGS = -c -a1 -mx -Nc -w2 -w7 -o+time -5
# -5 optimize for pentium (tm)
# -c compile only
# -o-all no optimizations (to avoid problems in disasm.c)
# -o+time optimize for speed
# -o+space optimize for size
# -A1 byte alignment for structures
# -mn compile for Win32 executable
# -mx compile for DOS386 (DOSX) executable
# -Nc create COMDAT records
# -w2 possible unattended assignment: off
# -w7 for loops with empty instruction-body
LINK = link
LINKFLAGS = /noi /exet:NT /su:console
LINKFLAGS = /noi /exet:DOSX
# /noignorecase all symbols are case-sensitive
# /exet:NT Exetype: NT (Win32)
# /exet:DOSX Exetype: DOSX (DOS32)
# /su:console Subsystem: Console (Console-App)
LIBRARIES =
EXE = .exe
OBJ = obj
.c.$(OBJ):
$(CC) $(CCFLAGS) $*.c
#
# modules needed for different programs
#
NASMOBJS = nasm.$(OBJ) nasmlib.$(OBJ) float.$(OBJ) insnsa.$(OBJ) \
assemble.$(OBJ) labels.$(OBJ) parser.$(OBJ) outform.$(OBJ) \
outbin.$(OBJ) outaout.$(OBJ) outcoff.$(OBJ) outelf.$(OBJ) \
outobj.$(OBJ) outas86.$(OBJ) outrdf.$(OBJ) outdbg.$(OBJ) \
preproc.$(OBJ) listing.$(OBJ)
preproc.$(OBJ) listing.$(OBJ) eval.$(OBJ)
NDISASMOBJS = ndisasm.$(OBJ) disasm.$(OBJ) sync.$(OBJ) nasmlib.$(OBJ) \
insnsd.$(OBJ)
#
# programs to create
#
all : nasm$(EXE) ndisasm$(EXE)
#
# We have to have a horrible kludge here to get round the 128 character
# limit, as usual... we'll simply use LNK-files :)
#
nasm$(EXE): $(NASMOBJS)
$(LINK) $(LINKFLAGS) @<<
$(NASMOBJS)
nasm.exe;
cx.obj $(NASMOBJS)
nasm.exe
<<
ndisasm$(EXE): $(NDISASMOBJS)
$(LINK) $(LINKFLAGS) @<<
$(NDISASMOBJS)
ndisasm.exe;
cx.obj $(NDISASMOBJS)
ndisasm.exe
<<
#
# modules for programs
#
disasm.$(OBJ): disasm.c nasm.h disasm.h sync.h insns.h names.c
assemble.$(OBJ): assemble.c nasm.h assemble.h insns.h
eval.$(OBJ): eval.c nasm.h nasmlib.h eval.h
float.$(OBJ): float.c nasm.h
labels.$(OBJ): labels.c nasm.h nasmlib.h
listing.$(OBJ): listing.c nasm.h nasmlib.h listing.h
nasm.$(OBJ): nasm.c nasm.h nasmlib.h parser.h assemble.h labels.h \
listing.h outform.h
nasmlib.$(OBJ): nasmlib.c nasm.h nasmlib.h
ndisasm.$(OBJ): ndisasm.c nasm.h sync.h disasm.h
outas86.$(OBJ): outas86.c nasm.h nasmlib.h
outaout.$(OBJ): outaout.c nasm.h nasmlib.h
outbin.$(OBJ): outbin.c nasm.h nasmlib.h
outcoff.$(OBJ): outcoff.c nasm.h nasmlib.h
outdbg.$(OBJ): outdbg.c nasm.h nasmlib.h
outelf.$(OBJ): outelf.c nasm.h nasmlib.h
outobj.$(OBJ): outobj.c nasm.h nasmlib.h
outrdf.$(OBJ): outrdf.c nasm.h nasmlib.h
outform.$(OBJ): outform.c outform.h nasm.h
parser.$(OBJ): parser.c nasm.h nasmlib.h parser.h float.h names.c
preproc.$(OBJ): preproc.c macros.c preproc.h nasm.h nasmlib.h
sync.$(OBJ): sync.c sync.h
insnsa.$(OBJ): insnsa.c nasm.h insns.h
insnsd.$(OBJ): insnsd.c nasm.h insns.h
clean :
del *.obj
del nasm$(EXE)
del ndisasm$(EXE)

114
Makefile.scw Normal file
View file

@ -0,0 +1,114 @@
# Makefile for the Netwide Assembler under 32-bit Windows(tm)
#
# 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.
#
# This Makefile is designed to build NASM using the 32-bit WIN32 C
# compiler Symantec(tm) C++ 7.5, provided you have a MAKE-utility
# that's compatible to SMAKE.
CC = sc
CCFLAGS = -c -a1 -mn -Nc -w2 -w7 -o+time -5
# -5 optimize for pentium (tm)
# -c compile only
# -o-all no optimizations (to avoid problems in disasm.c)
# -o+time optimize for speed
# -o+space optimize for size
# -A1 byte alignment for structures
# -mn compile for Win32 executable
# -mx compile for DOS386 (DOSX) executable
# -Nc create COMDAT records
# -w2 possible unattended assignment: off
# -w7 for loops with empty instruction-body
LINK = link
LINKFLAGS = /noi /exet:NT /su:console
# /noignorecase all symbols are case-sensitive
# /exet:NT Exetype: NT (Win32)
# /exet:DOSX Exetype: DOSX (DOS32)
# /su:console Subsystem: Console (Console-App)
LIBRARIES =
EXE = .exe
OBJ = obj
.c.$(OBJ):
$(CC) $(CCFLAGS) $*.c
#
# modules needed for different programs
#
NASMOBJS = nasm.$(OBJ) nasmlib.$(OBJ) float.$(OBJ) insnsa.$(OBJ) \
assemble.$(OBJ) labels.$(OBJ) parser.$(OBJ) outform.$(OBJ) \
outbin.$(OBJ) outaout.$(OBJ) outcoff.$(OBJ) outelf.$(OBJ) \
outobj.$(OBJ) outas86.$(OBJ) outrdf.$(OBJ) outdbg.$(OBJ) \
preproc.$(OBJ) listing.$(OBJ) eval.$(OBJ)
NDISASMOBJS = ndisasm.$(OBJ) disasm.$(OBJ) sync.$(OBJ) nasmlib.$(OBJ) \
insnsd.$(OBJ)
#
# programs to create
#
all : nasmw$(EXE) ndisasmw$(EXE)
#
# We have to have a horrible kludge here to get round the 128 character
# limit, as usual... we'll simply use LNK-files :)
#
nasmw$(EXE): $(NASMOBJS)
$(LINK) $(LINKFLAGS) @<<
$(NASMOBJS)
nasmw.exe;
<<
ndisasmw$(EXE): $(NDISASMOBJS)
$(LINK) $(LINKFLAGS) @<<
$(NDISASMOBJS)
ndisasmw.exe;
<<
#
# modules for programs
#
disasm.$(OBJ): disasm.c nasm.h disasm.h sync.h insns.h names.c
assemble.$(OBJ): assemble.c nasm.h assemble.h insns.h
eval.$(OBJ): eval.c nasm.h nasmlib.h eval.h
float.$(OBJ): float.c nasm.h
labels.$(OBJ): labels.c nasm.h nasmlib.h
listing.$(OBJ): listing.c nasm.h nasmlib.h listing.h
nasm.$(OBJ): nasm.c nasm.h nasmlib.h parser.h assemble.h labels.h \
listing.h outform.h
nasmlib.$(OBJ): nasmlib.c nasm.h nasmlib.h
ndisasm.$(OBJ): ndisasm.c nasm.h sync.h disasm.h
outas86.$(OBJ): outas86.c nasm.h nasmlib.h
outaout.$(OBJ): outaout.c nasm.h nasmlib.h
outbin.$(OBJ): outbin.c nasm.h nasmlib.h
outcoff.$(OBJ): outcoff.c nasm.h nasmlib.h
outdbg.$(OBJ): outdbg.c nasm.h nasmlib.h
outelf.$(OBJ): outelf.c nasm.h nasmlib.h
outobj.$(OBJ): outobj.c nasm.h nasmlib.h
outrdf.$(OBJ): outrdf.c nasm.h nasmlib.h
outform.$(OBJ): outform.c outform.h nasm.h
parser.$(OBJ): parser.c nasm.h nasmlib.h parser.h float.h names.c
preproc.$(OBJ): preproc.c macros.c preproc.h nasm.h nasmlib.h
sync.$(OBJ): sync.c sync.h
insnsa.$(OBJ): insnsa.c nasm.h insns.h
insnsd.$(OBJ): insnsd.c nasm.h insns.h
clean :
del *.obj
del nasmw$(EXE)
del ndisasmw$(EXE)

97
Makefile.unx Normal file
View file

@ -0,0 +1,97 @@
# Unix fall-back makefile for the Netwide Assembler. For use if
# `configure' fails to generate a workable Makefile.
#
# 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.
# You may need to adjust these values.
prefix = /usr/local
CC = cc
CFLAGS = -O -I.
# You _shouldn't_ need to adjust anything below this line.
exec_prefix = ${prefix}
bindir = ${exec_prefix}/bin
mandir = ${prefix}/man
INSTALL = install -c
INSTALL_PROGRAM = ${INSTALL}
INSTALL_DATA = ${INSTALL} -m 644
.c.o:
$(CC) -c $(CFLAGS) $*.c
NASM = nasm.o nasmlib.o float.o insnsa.o assemble.o labels.o \
parser.o outform.o outbin.o outaout.o outcoff.o outelf.o \
outobj.o outas86.o outrdf.o outdbg.o preproc.o listing.o \
eval.o
NDISASM = ndisasm.o disasm.o sync.o nasmlib.o insnsd.o
all: nasm ndisasm
nasm: $(NASM)
$(CC) -o nasm $(NASM)
ndisasm: $(NDISASM)
$(CC) -o ndisasm $(NDISASM)
assemble.o: assemble.c nasm.h nasmlib.h assemble.h insns.h
disasm.o: disasm.c nasm.h disasm.h sync.h insns.h names.c
eval.o: eval.c eval.h nasm.h nasmlib.h
float.o: float.c nasm.h
insnsa.o: insnsa.c nasm.h insns.h
insnsd.o: insnsd.c nasm.h insns.h
labels.o: labels.c nasm.h nasmlib.h
listing.o: listing.c nasm.h nasmlib.h listing.h
nasm.o: nasm.c nasm.h nasmlib.h preproc.h parser.h assemble.h labels.h \
outform.h listing.h
nasmlib.o: nasmlib.c nasm.h nasmlib.h
ndisasm.o: ndisasm.c nasm.h nasmlib.h sync.h disasm.h
outaout.o: outaout.c nasm.h nasmlib.h outform.h
outas86.o: outas86.c nasm.h nasmlib.h outform.h
outbin.o: outbin.c nasm.h nasmlib.h outform.h
outcoff.o: outcoff.c nasm.h nasmlib.h outform.h
outdbg.o: outdbg.c nasm.h nasmlib.h outform.h
outelf.o: outelf.c nasm.h nasmlib.h outform.h
outform.o: outform.c outform.h nasm.h
outobj.o: outobj.c nasm.h nasmlib.h outform.h
outrdf.o: outrdf.c nasm.h nasmlib.h outform.h
parser.o: parser.c nasm.h nasmlib.h parser.h float.h names.c
preproc.o: preproc.c nasm.h nasmlib.h macros.c
sync.o: sync.c sync.h
# These two source files are automagically generated from a single
# instruction-table file by a Perl script. They're distributed,
# though, so it isn't necessary to have Perl just to recompile NASM
# from the distribution.
insnsa.c insnsd.c: insns.dat insns.pl
perl insns.pl insns.dat
# This source file is generated from the standard macros file
# `standard.mac' by another Perl script. Again, it's part of the
# standard distribution.
macros.c: standard.mac macros.pl
perl macros.pl standard.mac
install: nasm ndisasm
$(INSTALL_PROGRAM) nasm $(bindir)/nasm
$(INSTALL_PROGRAM) ndisasm $(bindir)/ndisasm
$(INSTALL_DATA) nasm.1 $(mandir)/man1/nasm.1
$(INSTALL_DATA) ndisasm.1 $(mandir)/man1/ndisasm.1
clean:
rm -f *.o nasm ndisasm
$(MAKE) -C rdoff clean
rdf:
$(MAKE) -C rdoff
rdf_install install_rdf:
$(MAKE) -C rdoff install

View file

@ -24,7 +24,7 @@ NASMOBJS = nasm.$(OBJ) nasmlib.$(OBJ) float.$(OBJ) insnsa.$(OBJ) \
assemble.$(OBJ) labels.$(OBJ) parser.$(OBJ) outform.$(OBJ) \
outbin.$(OBJ) outaout.$(OBJ) outcoff.$(OBJ) outelf.$(OBJ) \
outobj.$(OBJ) outas86.$(OBJ) outrdf.$(OBJ) outdbg.$(OBJ) \
preproc.$(OBJ) listing.$(OBJ)
preproc.$(OBJ) listing.$(OBJ) eval.$(OBJ)
NDISASMOBJS = ndisasm.$(OBJ) disasm.$(OBJ) sync.$(OBJ) nasmlib.$(OBJ) \
insnsd.$(OBJ)
@ -33,7 +33,7 @@ all : nasm$(SUFFIX)$(EXE) ndisasm$(SUFFIX)$(EXE)
# We have to have a horrible kludge here to get round the 128 character
# limit, as usual...
LINKOBJS = a*.obj f*.obj insnsa.obj l*.obj na*.obj o*.obj p*.obj
LINKOBJS = a*.obj e*.obj f*.obj insnsa.obj l*.obj na*.obj o*.obj p*.obj
nasm$(SUFFIX)$(EXE): $(NASMOBJS)
cl /Fenasm$(SUFFIX).exe $(LINKOBJS)
@ -42,6 +42,7 @@ ndisasm$(SUFFIX)$(EXE): $(NDISASMOBJS)
assemble.$(OBJ): assemble.c nasm.h assemble.h insns.h
disasm.$(OBJ): disasm.c nasm.h disasm.h sync.h insns.h names.c
eval.$(OBJ): eval.c nasm.h nasmlib.h eval.h
float.$(OBJ): float.c nasm.h
labels.$(OBJ): labels.c nasm.h nasmlib.h
listing.$(OBJ): listing.c nasm.h nasmlib.h listing.h

View file

@ -42,7 +42,7 @@ NASMOBJS = nasm.$(OBJ) nasmlib.$(OBJ) float.$(OBJ) insnsa.$(OBJ) \
assemble.$(OBJ) labels.$(OBJ) parser.$(OBJ) outform.$(OBJ) \
outbin.$(OBJ) outaout.$(OBJ) outcoff.$(OBJ) outelf.$(OBJ) \
outobj.$(OBJ) outas86.$(OBJ) outrdf.$(OBJ) outdbg.$(OBJ) \
preproc.$(OBJ) listing.$(OBJ)
preproc.$(OBJ) listing.$(OBJ) eval.$(OBJ)
NDISASMOBJS = ndisasm.$(OBJ) disasm.$(OBJ) sync.$(OBJ) nasmlib.$(OBJ) \
insnsd.$(OBJ)
@ -62,10 +62,12 @@ NASM.LNK: makefile.wc
echo N nasm.exe > NASM.LNK
echo F nasm.$(OBJ) >> NASM.LNK
echo F nasmlib.$(OBJ) >> NASM.LNK
echo F eval.$(OBJ) >> NASM.LNK
echo F float.$(OBJ) >> NASM.LNK
echo F insnsa.$(OBJ) >> NASM.LNK
echo F assemble.$(OBJ) >> NASM.LNK
echo F labels.$(OBJ) >> NASM.LNK
echo F listing.$(OBJ) >> NASM.LNK
echo F parser.$(OBJ) >> NASM.LNK
echo F preproc.$(OBJ) >> NASM.LNK
echo F outform.$(OBJ) >> NASM.LNK
@ -88,6 +90,7 @@ NDISASM.LNK: makefile.wc
assemble.$(OBJ): assemble.c nasm.h assemble.h insns.h
disasm.$(OBJ): disasm.c nasm.h disasm.h sync.h insns.h names.c
eval.$(OBJ): eval.c nasm.h nasmlib.h eval.h
float.$(OBJ): float.c nasm.h
insnsa.$(OBJ): insnsa.c nasm.h insns.h
insnsd.$(OBJ): insnsd.c nasm.h insns.h

View file

@ -42,7 +42,7 @@ NASMOBJS = nasm.$(OBJ) nasmlib.$(OBJ) float.$(OBJ) insnsa.$(OBJ) \
assemble.$(OBJ) labels.$(OBJ) parser.$(OBJ) outform.$(OBJ) \
outbin.$(OBJ) outaout.$(OBJ) outcoff.$(OBJ) outelf.$(OBJ) \
outobj.$(OBJ) outas86.$(OBJ) outrdf.$(OBJ) outdbg.$(OBJ) \
preproc.$(OBJ) listing.$(OBJ)
preproc.$(OBJ) listing.$(OBJ) eval.$(OBJ)
NDISASMOBJS = ndisasm.$(OBJ) disasm.$(OBJ) sync.$(OBJ) nasmlib.$(OBJ) \
insnsd.$(OBJ)
@ -62,10 +62,12 @@ NASM.LNK: makefile.wcw
echo N nasm.exe > NASM.LNK
echo F nasm.$(OBJ) >> NASM.LNK
echo F nasmlib.$(OBJ) >> NASM.LNK
echo F eval.$(OBJ) >> NASM.LNK
echo F float.$(OBJ) >> NASM.LNK
echo F insnsa.$(OBJ) >> NASM.LNK
echo F assemble.$(OBJ) >> NASM.LNK
echo F labels.$(OBJ) >> NASM.LNK
echo F listing.$(OBJ) >> NASM.LNK
echo F parser.$(OBJ) >> NASM.LNK
echo F preproc.$(OBJ) >> NASM.LNK
echo F outform.$(OBJ) >> NASM.LNK
@ -88,6 +90,7 @@ NDISASM.LNK: makefile.wcw
assemble.$(OBJ): assemble.c nasm.h assemble.h insns.h
disasm.$(OBJ): disasm.c nasm.h disasm.h sync.h insns.h names.c
eval.$(OBJ): eval.c nasm.h nasmlib.h eval.h
float.$(OBJ): float.c nasm.h
insnsa.$(OBJ): insnsa.c nasm.h insns.h
insnsd.$(OBJ): insnsd.c nasm.h insns.h

30
Readme
View file

@ -13,6 +13,11 @@ search path (maybe /usr/local/bin, or ~/bin if you don't have root
access). You may also want to copy the man page `nasm.1' (and maybe
`ndisasm.1') to somewhere sensible.
To install under DOS, if you don't need to rebuild from the sources,
you can just copy nasm.exe and ndisasm.exe (16-bit DOS executables),
or nasmw.exe and ndisasmw.exe (Win32 console applications - less
likely to run out of memory), to somewhere on your PATH.
To rebuild the DOS sources, various makefiles are provided:
- Makefile.dos, the one I build the standard 16-bit releases from,
@ -26,7 +31,11 @@ To rebuild the DOS sources, various makefiles are provided:
- Makefile.bc2, also for Borland C, contributed by Fox Cutter.
Reported to work better than Makefile.bor on some systems.
- Makefile.sc, for Symantec C++. Contributed by Mark Junker.
- Makefile.sc, for Symantec C++, compiling to a 32-bit extended DOS
executable.. Contributed by Mark Junker.
- Makefile.scw, also for Symantec C++, compiling to a Win32 command-
line application. Also contributed by Mark Junker.
- Makefile.wc, for Watcom C, compiling to a 32-bit extended DOS
executable. Contributed by Dominik Behr.
- Makefile.wcw, also for Watcom C, compiling to a Win32 command-
@ -53,6 +62,9 @@ NDISASM.EXE) into standalone executables incorporating Tran's
PMODE/W DOS extender, rather than depending on an external extender
program.
Some of the Windows makefiles produce executables called nasmw.exe
and ndisasmw.exe, and some don't. Be prepared for either...
If you're trying to unpack the DOS (.ZIP format) archive under Unix
instead of using the .tar.gz version, you can save some time by
doing `unzip -aL', which will convert the DOS-format text files to
@ -65,9 +77,9 @@ equivalently by adding compiler command line options in the
Makefile.
There is a machine description file for the `LCC' retargetable C
compiler, in the directory `lcc', along with instructions for its
use. This means that NASM can now be used as the code-generator back
end for a useful C compiler.
compiler (version 3.6), in the directory `lcc', along with
instructions for its use. This means that NASM can now be used as
the code-generator back end for a useful C compiler.
Michael `Wuschel' Tippach has ported his DOS extender `WDOSX' to
enable it to work with the 32-bit binary files NASM can output: the
@ -83,7 +95,11 @@ JED programmers' editor (see http://space.mit.edu/~davis/jed.html
for details about JED). The comment at the start of the file gives
instructions on how to install the mode. This directory also
contains a file (`magic') containing lines to add to /etc/magic on
Unix systems to allow the `file' command to recognise RDF files.
Unix systems to allow the `file' command to recognise RDF files, and
a zip file (`exasm.zip') containing the necessary files for syntax
highlighting in the Aurora DOS editor. (The Aurora files were
contributed by <U993847220@aol.com>; I haven't tested them as I
don't have Aurora.)
The `rdoff' directory contains sources for a linker and loader for
the RDF object file format, to run under Linux, and also
@ -93,7 +109,9 @@ For information about how you can distribute and use NASM, see the
file Licence. We were tempted to put NASM under the GPL, but decided
that in many ways it was too restrictive for developers.
For information about how to use NASM, see `nasm.doc'. For
For information about how to use NASM, see the various forms of
documentation in the `doc' directory: documentation is provided in
HTML, PostScript, plain text, Texinfo, and Windows Help formats. For
information about how to use NDISASM, see `ndisasm.doc'. For
information about the internal structure of NASM, see
`internal.doc'. (In particular, _please_ read `internal.doc' before

97
Wishlist Normal file
View file

@ -0,0 +1,97 @@
NASM Wishlist
=============
- PUSH WORD EAX silently becomes PUSH EAX. Should warn.
- ndisasm hangs at eof.
- missing heading in documentation - some subsect in chapter 4.
- Add support for lcc 4.0.
* If-when this happens, remember to bump the `supported lcc
version' number in Readme.
- Re-work the evaluator, again, with a per-object-format fixup
routine, so as to be able to cope with section offsets "really"
being pure numbers; should be able to allow at _least_ the two
common idioms
TIMES 510-$ DB 0 ; bootsector
MOV AX,(PROG_END-100H)/16 ; .COM TSR
Would need to call the fixup throughout the evaluator, and the
fixup would have to be allowed to return UNKNOWN on pass one if it
had to. (_Always_ returning UNKNOWN on pass one, though a lovely
clean design, breaks the first of the above examples.)
- Preprocessor identifier concatenation?
- Arbitrary section names in `bin'.
- Ability to read from a pipe. Obviously not useful under dos, so
memory problems with storing entire input file aren't a problem
either.
- Subsection support?
- A good ALIGN mechanism, similar to GAS's. GAS pads out space by
means of the following (32-bit) instructions:
8DB42600000000 lea esi,[esi+0x0]
8DB600000000 lea esi,[esi+0x0]
8D742600 lea esi,[esi+0x0]
8D7600 lea esi,[esi+0x0]
8D36 lea esi,[esi]
90 nop
It uses up to two of these instructions to do up to 14-byte pads;
when more than 14 bytes are needed, it issues a (short) jump to
the end of the padded section and then NOPs the rest. Come up with
a similar scheme for 16 bit mode, and also come up with a way to
use it - internal to the assembler, so that programs using ALIGN
don't knock over preprocess-only mode.
Also re-work the macro form so that when given one argument in a
code section it calls this feature.
- Possibly a means whereby FP constants can be specified as
immediate operands to non-FP instructions.
* Possible syntax: MOV EAX,FLOAT 1.2 to get a single-precision FP
constant. Then maybe MOV EAX,HI_FLOAT 1.2 and MOV EAX,LO_FLOAT
1.2 to get the two halves of a double-precision one. Best to
ignore extended-precision in case it bites.
* Alternatively, maybe MOV EAX,FLOAT(4,0-4,1.2) to get bytes 0-4
(ie 0-3) of a 4-byte constant. Then HI_FLOAT is FLOAT(8,4-8,x)
and LO_FLOAT is FLOAT(8,0-4,x). But this version allows two-byte
chunks, one-byte chunks, even stranger chunks, and pieces of
ten-byte reals to be bandied around as well.
- A UNION macro might be quite cool, now that ABSOLUTE is sane
enough to be able to handle it.
- An equivalent to gcc's ## stringify operator, plus string
concatenation, somehow implemented without undue ugliness, so as
to be able to do `%include "/my/path/%1"' in a macro, or something
similar...
- Actually _do_ something with the processor, privileged and
undocumented flags in the instruction table.
- Maybe NEC V20/V30 instructions?
- Yet more object formats.
* Possibly direct support for .EXE files?
- Debug information, in all formats it can be usefully done in.
* including line-number record support.
- Symbol map in binary format. Format-specific options...
- REDESIGN: Think about EQU dependency, and about start-point
specification in OBJ. Possibly re-think directive support.
- Think about a wrapper program like gcc? Possibly invent a _patch_
for gcc so that it can take .asm files on the command line?
- If a wrapper happens, think about adding an option to cause the
resulting executable file to be executed immediately, thus
allowing NASM source files to have #!... (probably silly)
- Multi-platform support? If so: definitely Alpha; possibly Java
byte code; probably ARM/StrongARM; maybe Sparc; maybe Mips; maybe
Vax. Perhaps Z80 and 6502, just for a laugh?

View file

@ -592,22 +592,40 @@ static void gencode (long segment, long offset, int bits,
case 014: case 015: case 016:
if (ins->oprs[c-014].offset < -128 || ins->oprs[c-014].offset > 127)
errfunc (ERR_WARNING, "signed byte value exceeds bounds");
bytes[0] = ins->oprs[c-014].offset;
out (offset, segment, bytes, OUT_RAWDATA+1, NO_SEG, NO_SEG);
if (ins->oprs[c-014].segment != NO_SEG) {
data = ins->oprs[c-014].offset;
out (offset, segment, &data, OUT_ADDRESS+1,
ins->oprs[c-014].segment, ins->oprs[c-014].wrt);
} else {
bytes[0] = ins->oprs[c-014].offset;
out (offset, segment, bytes, OUT_RAWDATA+1, NO_SEG, NO_SEG);
}
offset += 1;
break;
case 020: case 021: case 022:
if (ins->oprs[c-020].offset < -256 || ins->oprs[c-020].offset > 255)
errfunc (ERR_WARNING, "byte value exceeds bounds");
bytes[0] = ins->oprs[c-020].offset;
out (offset, segment, bytes, OUT_RAWDATA+1, NO_SEG, NO_SEG);
if (ins->oprs[c-020].segment != NO_SEG) {
data = ins->oprs[c-020].offset;
out (offset, segment, &data, OUT_ADDRESS+1,
ins->oprs[c-020].segment, ins->oprs[c-020].wrt);
} else {
bytes[0] = ins->oprs[c-020].offset;
out (offset, segment, bytes, OUT_RAWDATA+1, NO_SEG, NO_SEG);
}
offset += 1;
break;
case 024: case 025: case 026:
if (ins->oprs[c-024].offset < 0 || ins->oprs[c-024].offset > 255)
errfunc (ERR_WARNING, "unsigned byte value exceeds bounds");
bytes[0] = ins->oprs[c-024].offset;
out (offset, segment, bytes, OUT_RAWDATA+1, NO_SEG, NO_SEG);
if (ins->oprs[c-024].segment != NO_SEG) {
data = ins->oprs[c-024].offset;
out (offset, segment, &data, OUT_ADDRESS+1,
ins->oprs[c-024].segment, ins->oprs[c-024].wrt);
} else {
bytes[0] = ins->oprs[c-024].offset;
out (offset, segment, bytes, OUT_RAWDATA+1, NO_SEG, NO_SEG);
}
offset += 1;
break;
case 030: case 031: case 032:
@ -757,8 +775,9 @@ static void gencode (long segment, long offset, int bits,
errfunc (ERR_PANIC, "non-constant BSS size in pass two");
else {
long size = ins->oprs[0].offset << (c-0340);
out (offset, segment, NULL,
OUT_RESERVE+size, NO_SEG, NO_SEG);
if (size > 0)
out (offset, segment, NULL,
OUT_RESERVE+size, NO_SEG, NO_SEG);
offset += size;
}
break;
@ -792,9 +811,16 @@ static void gencode (long segment, long offset, int bits,
case 0:
break;
case 1:
*bytes = ins->oprs[(c>>3)&7].offset;
out (offset, segment, bytes, OUT_RAWDATA+1,
NO_SEG, NO_SEG);
if (ins->oprs[(c>>3)&7].segment != NO_SEG) {
data = ins->oprs[(c>>3)&7].offset;
out (offset, segment, &data, OUT_ADDRESS+1,
ins->oprs[(c>>3)&7].segment,
ins->oprs[(c>>3)&7].wrt);
} else {
*bytes = ins->oprs[(c>>3)&7].offset;
out (offset, segment, bytes, OUT_RAWDATA+1,
NO_SEG, NO_SEG);
}
s++;
break;
case 2:
@ -887,6 +913,9 @@ static int matches (struct itemplate *itemp, insn *instruction) {
if (itemp->flags & IF_SB) {
size = BITS8;
oprs = itemp->operands;
} else if (itemp->flags & IF_SW) {
size = BITS16;
oprs = itemp->operands;
} else if (itemp->flags & IF_SD) {
size = BITS32;
oprs = itemp->operands;
@ -939,6 +968,8 @@ static ea *process_ea (operand *input, ea *output, int addrbits, int rfield,
} else { /* it's an indirection */
int i=input->indexreg, b=input->basereg, s=input->scale;
long o=input->offset, seg=input->segment;
int hb=input->hintbase, ht=input->hinttype;
int t;
if (s==0) i = -1; /* make this easy, at least */
@ -960,11 +991,15 @@ static ea *process_ea (operand *input, ea *output, int addrbits, int rfield,
return NULL;
/* now reorganise base/index */
if (s == 1 && b != i && b != -1 && i != -1 &&
((hb==b&&ht==EAH_NOTBASE) || (hb==i&&ht==EAH_MAKEBASE)))
t = b, b = i, i = t; /* swap if hints say so */
if (b==i) /* convert EAX+2*EAX to 3*EAX */
b = -1, s++;
if (b==-1 && s==1) /* single register should be base */
b = i, i = -1;
if (((s==2 && i!=R_ESP) || s==3 || s==5 || s==9) && b==-1)
if (b==-1 && s==1 && !(hb == i && ht == EAH_NOTBASE))
b = i, i = -1; /* make single reg base, unless hint */
if (((s==2 && i!=R_ESP && !(input->eaflags & EAF_TIMESTWO)) ||
s==3 || s==5 || s==9) && b==-1)
b = i, s--; /* convert 3*EAX to EAX+2*EAX */
if (s==1 && i==R_ESP) /* swap ESP into base if scale is 1 */
i = b, b = R_ESP;
@ -986,11 +1021,15 @@ static ea *process_ea (operand *input, ea *output, int addrbits, int rfield,
return NULL;
}
if (b==-1 || (b!=R_EBP && o==0 &&
seg==NO_SEG && !forw_ref))
seg==NO_SEG && !forw_ref &&
!(input->eaflags &
(EAF_BYTEOFFS|EAF_WORDOFFS))))
mod = 0;
else if (o>=-128 && o<=127 && seg==NO_SEG && !forw_ref)
else if (input->eaflags & EAF_BYTEOFFS ||
(o>=-128 && o<=127 && seg==NO_SEG && !forw_ref &&
!(input->eaflags & EAF_WORDOFFS))) {
mod = 1;
else
} else
mod = 2;
output->sib_present = FALSE;
@ -1036,9 +1075,13 @@ static ea *process_ea (operand *input, ea *output, int addrbits, int rfield,
}
if (b==-1 || (b!=R_EBP && o==0 &&
seg==NO_SEG && !forw_ref))
seg==NO_SEG && !forw_ref &&
!(input->eaflags &
(EAF_BYTEOFFS|EAF_WORDOFFS))))
mod = 0;
else if (o>=-128 && o<=127 && seg==NO_SEG && !forw_ref)
else if (input->eaflags & EAF_BYTEOFFS ||
(o>=-128 && o<=127 && seg==NO_SEG && !forw_ref &&
!(input->eaflags & EAF_WORDOFFS)))
mod = 1;
else
mod = 2;
@ -1089,9 +1132,12 @@ static ea *process_ea (operand *input, ea *output, int addrbits, int rfield,
if (rm==-1) /* can't happen, in theory */
return NULL; /* so panic if it does */
if (o==0 && seg==NO_SEG && !forw_ref && rm!=6)
if (o==0 && seg==NO_SEG && !forw_ref && rm!=6 &&
!(input->eaflags & (EAF_BYTEOFFS|EAF_WORDOFFS)))
mod = 0;
else if (o>=-128 && o<=127 && seg==NO_SEG && !forw_ref)
else if (input->eaflags & EAF_BYTEOFFS ||
(o>=-128 && o<=127 && seg==NO_SEG && !forw_ref &&
!(input->eaflags & EAF_WORDOFFS)))
mod = 1;
else
mod = 2;

View file

@ -73,6 +73,10 @@ static int whichreg(long regflags, int regval) {
return R_ST0;
if (!(REG_CS & ~regflags))
return R_CS;
if (!(REG_DESS & ~regflags))
return (regval == 0 || regval == 2 || regval == 3 ? sreg[regval] : 0);
if (!(REG_FSGS & ~regflags))
return (regval == 4 || regval == 5 ? sreg[regval] : 0);
if (!((REGMEM|BITS8) & ~regflags))
return reg8[regval];
if (!((REGMEM|BITS16) & ~regflags))
@ -488,11 +492,22 @@ long disasm (unsigned char *data, char *output, int segsize, long offset,
* Final check to make sure the types of r/m match up.
*/
for (i = 0; i < (*p)->operands; i++)
if (((ins.oprs[i].segment & SEG_RMREG) &&
if (
/* If it's a mem-only EA but we have a register, die. */
((ins.oprs[i].segment & SEG_RMREG) &&
!(MEMORY & ~(*p)->opd[i])) ||
/* If it's a reg-only EA but we have a memory ref, die. */
(!(ins.oprs[i].segment & SEG_RMREG) &&
!(REGNORM & ~(*p)->opd[i]) &&
!((*p)->opd[i] & REG_SMASK)))
!((*p)->opd[i] & REG_SMASK)) ||
/* Register type mismatch (eg FS vs REG_DESS): die. */
((((*p)->opd[i] & (REGISTER | FPUREG)) ||
(ins.oprs[i].segment & SEG_RMREG)) &&
!whichreg ((*p)->opd[i], ins.oprs[i].basereg)))
works = FALSE;
if (works)
break;
@ -559,7 +574,7 @@ long disasm (unsigned char *data, char *output, int segsize, long offset,
ins.oprs[i].basereg = whichreg ((*p)->opd[i],
ins.oprs[i].basereg);
slen += sprintf(output+slen, "%s",
reg_names[ins.oprs[i].basereg]);
reg_names[ins.oprs[i].basereg-EXPR_REG_START]);
} else if (!(UNITY & ~(*p)->opd[i])) {
output[slen++] = '1';
} else if ( (*p)->opd[i] & IMMEDIATE ) {
@ -617,14 +632,16 @@ long disasm (unsigned char *data, char *output, int segsize, long offset,
}
if (ins.oprs[i].basereg != -1) {
slen += sprintf(output+slen, "%s",
reg_names[ins.oprs[i].basereg]);
reg_names[(ins.oprs[i].basereg -
EXPR_REG_START)]);
started = TRUE;
}
if (ins.oprs[i].indexreg != -1) {
if (started)
output[slen++] = '+';
slen += sprintf(output+slen, "%s",
reg_names[ins.oprs[i].indexreg]);
reg_names[(ins.oprs[i].indexreg -
EXPR_REG_START)]);
if (ins.oprs[i].scale > 1)
slen += sprintf(output+slen, "*%d", ins.oprs[i].scale);
started = TRUE;

8530
doc/nasmdoc.src Normal file

File diff suppressed because it is too large Load diff

2134
doc/rdsrc.pl Normal file

File diff suppressed because it is too large Load diff

761
eval.c Normal file
View file

@ -0,0 +1,761 @@
/* eval.c expression evaluator for the Netwide Assembler
*
* 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.
*
* initial version 27/iii/95 by Simon Tatham
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include "nasm.h"
#include "nasmlib.h"
#include "eval.h"
static expr **tempexprs = NULL;
static int ntempexprs, tempexprs_size = 0;
#define TEMPEXPRS_DELTA 128
static expr *tempexpr;
static int ntempexpr, tempexpr_size;
#define TEMPEXPR_DELTA 8
static scanner scan;
static void *scpriv;
static struct tokenval *tokval;
static efunc error;
static int i;
static int seg, ofs;
static char *label = NULL, special_empty_string[] = "";
static lfunc labelfunc;
static struct ofmt *outfmt;
static int *forward;
static struct eval_hints *hint;
/*
* Construct a temporary expression.
*/
static void begintemp(void) {
tempexpr = NULL;
tempexpr_size = ntempexpr = 0;
}
static void addtotemp(long type, long value) {
while (ntempexpr >= tempexpr_size) {
tempexpr_size += TEMPEXPR_DELTA;
tempexpr = nasm_realloc(tempexpr,
tempexpr_size*sizeof(*tempexpr));
}
tempexpr[ntempexpr].type = type;
tempexpr[ntempexpr++].value = value;
}
static expr *finishtemp(void) {
addtotemp (0L, 0L); /* terminate */
while (ntempexprs >= tempexprs_size) {
tempexprs_size += TEMPEXPRS_DELTA;
tempexprs = nasm_realloc(tempexprs,
tempexprs_size*sizeof(*tempexprs));
}
return tempexprs[ntempexprs++] = tempexpr;
}
/*
* Add two vector datatypes. We have some bizarre behaviour on far-
* absolute segment types: we preserve them during addition _only_
* if one of the segments is a truly pure scalar.
*/
static expr *add_vectors(expr *p, expr *q) {
int preserve;
preserve = is_really_simple(p) || is_really_simple(q);
begintemp();
while (p->type && q->type &&
p->type < EXPR_SEGBASE+SEG_ABS &&
q->type < EXPR_SEGBASE+SEG_ABS) {
int lasttype;
if (p->type > q->type) {
addtotemp(q->type, q->value);
lasttype = q++->type;
} else if (p->type < q->type) {
addtotemp(p->type, p->value);
lasttype = p++->type;
} else { /* *p and *q have same type */
addtotemp(p->type, p->value + q->value);
lasttype = p->type;
p++, q++;
}
if (lasttype == EXPR_UNKNOWN) {
return finishtemp();
}
}
while (p->type &&
(preserve || p->type < EXPR_SEGBASE+SEG_ABS)) {
addtotemp(p->type, p->value);
p++;
}
while (q->type &&
(preserve || q->type < EXPR_SEGBASE+SEG_ABS)) {
addtotemp(q->type, q->value);
q++;
}
return finishtemp();
}
/*
* Multiply a vector by a scalar. Strip far-absolute segment part
* if present.
*
* Explicit treatment of UNKNOWN is not required in this routine,
* since it will silently do the Right Thing anyway.
*
* If `affect_hints' is set, we also change the hint type to
* NOTBASE if a MAKEBASE hint points at a register being
* multiplied. This allows [eax*1+ebx] to hint EBX rather than EAX
* as the base register.
*/
static expr *scalar_mult(expr *vect, long scalar, int affect_hints) {
expr *p = vect;
while (p->type && p->type < EXPR_SEGBASE+SEG_ABS) {
p->value = scalar * (p->value);
if (hint && hint->type == EAH_MAKEBASE &&
p->type == hint->base && affect_hints)
hint->type = EAH_NOTBASE;
p++;
}
p->type = 0;
return vect;
}
static expr *scalarvect (long scalar) {
begintemp();
addtotemp(EXPR_SIMPLE, scalar);
return finishtemp();
}
static expr *unknown_expr (void) {
begintemp();
addtotemp(EXPR_UNKNOWN, 1L);
return finishtemp();
}
/*
* The SEG operator: calculate the segment part of a relocatable
* value. Return NULL, as usual, if an error occurs. Report the
* error too.
*/
static expr *segment_part (expr *e) {
long seg;
if (is_unknown(e))
return unknown_expr();
if (!is_reloc(e)) {
error(ERR_NONFATAL, "cannot apply SEG to a non-relocatable value");
return NULL;
}
seg = reloc_seg(e);
if (seg == NO_SEG) {
error(ERR_NONFATAL, "cannot apply SEG to a non-relocatable value");
return NULL;
} else if (seg & SEG_ABS) {
return scalarvect(seg & ~SEG_ABS);
} else if (seg & 1) {
error(ERR_NONFATAL, "SEG applied to something which"
" is already a segment base");
return NULL;
}
else {
long base = outfmt->segbase(seg+1);
begintemp();
addtotemp((base == NO_SEG ? EXPR_UNKNOWN : EXPR_SEGBASE+base), 1L);
return finishtemp();
}
}
/*
* Recursive-descent parser. Called with a single boolean operand,
* which is TRUE if the evaluation is critical (i.e. unresolved
* symbols are an error condition). Must update the global `i' to
* reflect the token after the parsed string. May return NULL.
*
* evaluate() should report its own errors: on return it is assumed
* that if NULL has been returned, the error has already been
* reported.
*/
/*
* Grammar parsed is:
*
* expr : bexpr [ WRT expr6 ]
* bexpr : rexp0 or expr0 depending on relative-mode setting
* rexp0 : rexp1 [ {||} rexp1...]
* rexp1 : rexp2 [ {^^} rexp2...]
* rexp2 : rexp3 [ {&&} rexp3...]
* rexp3 : expr0 [ {=,==,<>,!=,<,>,<=,>=} expr0 ]
* expr0 : expr1 [ {|} expr1...]
* expr1 : expr2 [ {^} expr2...]
* expr2 : expr3 [ {&} expr3...]
* expr3 : expr4 [ {<<,>>} expr4...]
* expr4 : expr5 [ {+,-} expr5...]
* expr5 : expr6 [ {*,/,%,//,%%} expr6...]
* expr6 : { ~,+,-,SEG } expr6
* | (bexpr)
* | symbol
* | $
* | number
*/
static expr *rexp0(int), *rexp1(int), *rexp2(int), *rexp3(int);
static expr *expr0(int), *expr1(int), *expr2(int), *expr3(int);
static expr *expr4(int), *expr5(int), *expr6(int);
static expr *(*bexpr)(int);
static expr *rexp0(int critical) {
expr *e, *f;
e = rexp1(critical);
if (!e)
return NULL;
while (i == TOKEN_DBL_OR) {
i = scan(scpriv, tokval);
f = rexp1(critical);
if (!f)
return NULL;
if (!(is_simple(e) || is_just_unknown(e)) ||
!(is_simple(f) || is_just_unknown(f))) {
error(ERR_NONFATAL, "`|' operator may only be applied to"
" scalar values");
}
if (is_just_unknown(e) || is_just_unknown(f))
e = unknown_expr();
else
e = scalarvect ((long) (reloc_value(e) || reloc_value(f)));
}
return e;
}
static expr *rexp1(int critical) {
expr *e, *f;
e = rexp2(critical);
if (!e)
return NULL;
while (i == TOKEN_DBL_XOR) {
i = scan(scpriv, tokval);
f = rexp2(critical);
if (!f)
return NULL;
if (!(is_simple(e) || is_just_unknown(e)) ||
!(is_simple(f) || is_just_unknown(f))) {
error(ERR_NONFATAL, "`^' operator may only be applied to"
" scalar values");
}
if (is_just_unknown(e) || is_just_unknown(f))
e = unknown_expr();
else
e = scalarvect ((long) (!reloc_value(e) ^ !reloc_value(f)));
}
return e;
}
static expr *rexp2(int critical) {
expr *e, *f;
e = rexp3(critical);
if (!e)
return NULL;
while (i == TOKEN_DBL_AND) {
i = scan(scpriv, tokval);
f = rexp3(critical);
if (!f)
return NULL;
if (!(is_simple(e) || is_just_unknown(e)) ||
!(is_simple(f) || is_just_unknown(f))) {
error(ERR_NONFATAL, "`&' operator may only be applied to"
" scalar values");
}
if (is_just_unknown(e) || is_just_unknown(f))
e = unknown_expr();
else
e = scalarvect ((long) (reloc_value(e) && reloc_value(f)));
}
return e;
}
static expr *rexp3(int critical) {
expr *e, *f;
long v;
e = expr0(critical);
if (!e)
return NULL;
while (i == TOKEN_EQ || i == TOKEN_LT || i == TOKEN_GT ||
i == TOKEN_NE || i == TOKEN_LE || i == TOKEN_GE) {
int j = i;
i = scan(scpriv, tokval);
f = expr0(critical);
if (!f)
return NULL;
e = add_vectors (e, scalar_mult(f, -1L, FALSE));
switch (j) {
case TOKEN_EQ: case TOKEN_NE:
if (is_unknown(e))
v = -1; /* means unknown */
else if (!is_really_simple(e) || reloc_value(e) != 0)
v = (j == TOKEN_NE); /* unequal, so return TRUE if NE */
else
v = (j == TOKEN_EQ); /* equal, so return TRUE if EQ */
break;
default:
if (is_unknown(e))
v = -1; /* means unknown */
else if (!is_really_simple(e)) {
error(ERR_NONFATAL, "`%s': operands differ by a non-scalar",
(j == TOKEN_LE ? "<=" : j == TOKEN_LT ? "<" :
j == TOKEN_GE ? ">=" : ">"));
v = 0; /* must set it to _something_ */
} else {
int vv = reloc_value(e);
if (vv == 0)
v = (j == TOKEN_LE || j == TOKEN_GE);
else if (vv > 0)
v = (j == TOKEN_GE || j == TOKEN_GT);
else /* vv < 0 */
v = (j == TOKEN_LE || j == TOKEN_LT);
}
break;
}
if (v == -1)
e = unknown_expr();
else
e = scalarvect(v);
}
return e;
}
static expr *expr0(int critical) {
expr *e, *f;
e = expr1(critical);
if (!e)
return NULL;
while (i == '|') {
i = scan(scpriv, tokval);
f = expr1(critical);
if (!f)
return NULL;
if (!(is_simple(e) || is_just_unknown(e)) ||
!(is_simple(f) || is_just_unknown(f))) {
error(ERR_NONFATAL, "`|' operator may only be applied to"
" scalar values");
}
if (is_just_unknown(e) || is_just_unknown(f))
e = unknown_expr();
else
e = scalarvect (reloc_value(e) | reloc_value(f));
}
return e;
}
static expr *expr1(int critical) {
expr *e, *f;
e = expr2(critical);
if (!e)
return NULL;
while (i == '^') {
i = scan(scpriv, tokval);
f = expr2(critical);
if (!f)
return NULL;
if (!(is_simple(e) || is_just_unknown(e)) ||
!(is_simple(f) || is_just_unknown(f))) {
error(ERR_NONFATAL, "`^' operator may only be applied to"
" scalar values");
}
if (is_just_unknown(e) || is_just_unknown(f))
e = unknown_expr();
else
e = scalarvect (reloc_value(e) ^ reloc_value(f));
}
return e;
}
static expr *expr2(int critical) {
expr *e, *f;
e = expr3(critical);
if (!e)
return NULL;
while (i == '&') {
i = scan(scpriv, tokval);
f = expr3(critical);
if (!f)
return NULL;
if (!(is_simple(e) || is_just_unknown(e)) ||
!(is_simple(f) || is_just_unknown(f))) {
error(ERR_NONFATAL, "`&' operator may only be applied to"
" scalar values");
}
if (is_just_unknown(e) || is_just_unknown(f))
e = unknown_expr();
else
e = scalarvect (reloc_value(e) & reloc_value(f));
}
return e;
}
static expr *expr3(int critical) {
expr *e, *f;
e = expr4(critical);
if (!e)
return NULL;
while (i == TOKEN_SHL || i == TOKEN_SHR) {
int j = i;
i = scan(scpriv, tokval);
f = expr4(critical);
if (!f)
return NULL;
if (!(is_simple(e) || is_just_unknown(e)) ||
!(is_simple(f) || is_just_unknown(f))) {
error(ERR_NONFATAL, "shift operator may only be applied to"
" scalar values");
} else if (is_just_unknown(e) || is_just_unknown(f)) {
e = unknown_expr();
} else switch (j) {
case TOKEN_SHL:
e = scalarvect (reloc_value(e) << reloc_value(f));
break;
case TOKEN_SHR:
e = scalarvect (((unsigned long)reloc_value(e)) >>
reloc_value(f));
break;
}
}
return e;
}
static expr *expr4(int critical) {
expr *e, *f;
e = expr5(critical);
if (!e)
return NULL;
while (i == '+' || i == '-') {
int j = i;
i = scan(scpriv, tokval);
f = expr5(critical);
if (!f)
return NULL;
switch (j) {
case '+':
e = add_vectors (e, f);
break;
case '-':
e = add_vectors (e, scalar_mult(f, -1L, FALSE));
break;
}
}
return e;
}
static expr *expr5(int critical) {
expr *e, *f;
e = expr6(critical);
if (!e)
return NULL;
while (i == '*' || i == '/' || i == '%' ||
i == TOKEN_SDIV || i == TOKEN_SMOD) {
int j = i;
i = scan(scpriv, tokval);
f = expr6(critical);
if (!f)
return NULL;
if (j != '*' && (!(is_simple(e) || is_just_unknown(e)) ||
!(is_simple(f) || is_just_unknown(f)))) {
error(ERR_NONFATAL, "division operator may only be applied to"
" scalar values");
return NULL;
}
if (j != '*' && !is_unknown(f) && reloc_value(f) == 0) {
error(ERR_NONFATAL, "division by zero");
return NULL;
}
switch (j) {
case '*':
if (is_simple(e))
e = scalar_mult (f, reloc_value(e), TRUE);
else if (is_simple(f))
e = scalar_mult (e, reloc_value(f), TRUE);
else if (is_just_unknown(e) && is_just_unknown(f))
e = unknown_expr();
else {
error(ERR_NONFATAL, "unable to multiply two "
"non-scalar objects");
return NULL;
}
break;
case '/':
if (is_just_unknown(e) || is_just_unknown(f))
e = unknown_expr();
else
e = scalarvect (((unsigned long)reloc_value(e)) /
((unsigned long)reloc_value(f)));
break;
case '%':
if (is_just_unknown(e) || is_just_unknown(f))
e = unknown_expr();
else
e = scalarvect (((unsigned long)reloc_value(e)) %
((unsigned long)reloc_value(f)));
break;
case TOKEN_SDIV:
if (is_just_unknown(e) || is_just_unknown(f))
e = unknown_expr();
else
e = scalarvect (((signed long)reloc_value(e)) /
((signed long)reloc_value(f)));
break;
case TOKEN_SMOD:
if (is_just_unknown(e) || is_just_unknown(f))
e = unknown_expr();
else
e = scalarvect (((signed long)reloc_value(e)) %
((signed long)reloc_value(f)));
break;
}
}
return e;
}
static expr *expr6(int critical) {
long type;
expr *e;
long label_seg, label_ofs;
if (i == '-') {
i = scan(scpriv, tokval);
e = expr6(critical);
if (!e)
return NULL;
return scalar_mult (e, -1L, FALSE);
} else if (i == '+') {
i = scan(scpriv, tokval);
return expr6(critical);
} else if (i == '~') {
i = scan(scpriv, tokval);
e = expr6(critical);
if (!e)
return NULL;
if (is_just_unknown(e))
return unknown_expr();
else if (!is_simple(e)) {
error(ERR_NONFATAL, "`~' operator may only be applied to"
" scalar values");
return NULL;
}
return scalarvect(~reloc_value(e));
} else if (i == TOKEN_SEG) {
i = scan(scpriv, tokval);
e = expr6(critical);
if (!e)
return NULL;
e = segment_part(e);
if (is_unknown(e) && critical) {
error(ERR_NONFATAL, "unable to determine segment base");
return NULL;
}
return e;
} else if (i == '(') {
i = scan(scpriv, tokval);
e = bexpr(critical);
if (!e)
return NULL;
if (i != ')') {
error(ERR_NONFATAL, "expecting `)'");
return NULL;
}
i = scan(scpriv, tokval);
return e;
} else if (i == TOKEN_NUM || i == TOKEN_REG || i == TOKEN_ID ||
i == TOKEN_HERE || i == TOKEN_BASE) {
begintemp();
switch (i) {
case TOKEN_NUM:
addtotemp(EXPR_SIMPLE, tokval->t_integer);
break;
case TOKEN_REG:
addtotemp(tokval->t_integer, 1L);
if (hint && hint->type == EAH_NOHINT)
hint->base = tokval->t_integer, hint->type = EAH_MAKEBASE;
break;
case TOKEN_ID:
case TOKEN_HERE:
case TOKEN_BASE:
/*
* If "label" begins with "%", this indicates that no
* symbol, Here or Base references are valid because we
* are in preprocess-only mode.
*/
if (*label == '%') {
error(ERR_NONFATAL,
"%s not supported in preprocess-only mode",
(i == TOKEN_ID ? "symbol references" :
i == TOKEN_HERE ? "`$'" : "`$$'"));
addtotemp(EXPR_UNKNOWN, 1L);
break;
}
/*
* Since the whole line is parsed before the label it
* defines is given to the label manager, we have
* problems with lines such as
*
* end: TIMES 512-(end-start) DB 0
*
* where `end' is not known on pass one, despite not
* really being a forward reference, and due to
* criticality it is _needed_. Hence we check our label
* against the currently defined one, and do our own
* resolution of it if we have to.
*/
type = EXPR_SIMPLE; /* might get overridden by UNKNOWN */
if (i == TOKEN_BASE) {
label_seg = seg;
label_ofs = 0;
} else if (i == TOKEN_HERE || !strcmp(tokval->t_charptr, label)) {
label_seg = seg;
label_ofs = ofs;
} else if (!labelfunc(tokval->t_charptr,&label_seg,&label_ofs)) {
if (critical == 2) {
error (ERR_NONFATAL, "symbol `%s' undefined",
tokval->t_charptr);
return NULL;
} else if (critical == 1) {
error (ERR_NONFATAL, "symbol `%s' not defined before use",
tokval->t_charptr);
return NULL;
} else {
if (forward)
*forward = TRUE;
type = EXPR_UNKNOWN;
label_seg = NO_SEG;
label_ofs = 1;
}
}
addtotemp(type, label_ofs);
if (label_seg!=NO_SEG)
addtotemp(EXPR_SEGBASE + label_seg, 1L);
break;
}
i = scan(scpriv, tokval);
return finishtemp();
} else {
error(ERR_NONFATAL, "expression syntax error");
return NULL;
}
}
void eval_global_info (struct ofmt *output, lfunc lookup_label) {
outfmt = output;
labelfunc = lookup_label;
}
void eval_info (char *labelname, long segment, long offset) {
if (label != special_empty_string)
nasm_free (label);
if (labelname)
label = nasm_strdup(labelname);
else {
label = special_empty_string;
seg = segment;
ofs = offset;
}
}
expr *evaluate (scanner sc, void *scprivate, struct tokenval *tv,
int *fwref, int critical, efunc report_error,
struct eval_hints *hints) {
expr *e;
expr *f = NULL;
hint = hints;
if (hint)
hint->type = EAH_NOHINT;
if (critical & 0x10) {
critical &= ~0x10;
bexpr = rexp0;
} else
bexpr = expr0;
scan = sc;
scpriv = scprivate;
tokval = tv;
error = report_error;
forward = fwref;
if (tokval->t_type == TOKEN_INVALID)
i = scan(scpriv, tokval);
else
i = tokval->t_type;
while (ntempexprs) /* initialise temporary storage */
nasm_free (tempexprs[--ntempexprs]);
e = bexpr (critical);
if (!e)
return NULL;
if (i == TOKEN_WRT) {
i = scan(scpriv, tokval); /* eat the WRT */
f = expr6 (critical);
if (!f)
return NULL;
}
e = scalar_mult (e, 1L, FALSE); /* strip far-absolute segment part */
if (f) {
expr *g;
if (is_just_unknown(f))
g = unknown_expr();
else {
long value;
begintemp();
if (!is_reloc(f)) {
error(ERR_NONFATAL, "invalid right-hand operand to WRT");
return NULL;
}
value = reloc_seg(f);
if (value == NO_SEG)
value = reloc_value(f) | SEG_ABS;
else if (!(value & SEG_ABS) && !(value % 2) && critical) {
error(ERR_NONFATAL, "invalid right-hand operand to WRT");
return NULL;
}
addtotemp(EXPR_WRT, value);
g = finishtemp();
}
e = add_vectors (e, g);
}
return e;
}

34
eval.h Normal file
View file

@ -0,0 +1,34 @@
/* eval.h header file for eval.c
*
* 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.
*/
#ifndef NASM_EVAL_H
#define NASM_EVAL_H
/*
* Called once to tell the evaluator what output format is
* providing segment-base details, and what function can be used to
* look labels up.
*/
void eval_global_info (struct ofmt *output, lfunc lookup_label);
/*
* Called to set the information the evaluator needs: the value of
* $ is set from `segment' and `offset' if `labelname' is NULL, and
* otherwise the name of the current line's label is set from
* `labelname' instead.
*/
void eval_info (char *labelname, long segment, long offset);
/*
* The evaluator itself.
*/
expr *evaluate (scanner sc, void *scprivate, struct tokenval *tv,
int *fwref, int critical, efunc report_error,
struct eval_hints *hints);
#endif

535
insns.bas Normal file
View file

@ -0,0 +1,535 @@
' INFO_1: Converter for INSNS.DAT to INSNSA.C and INSNSD.C
'
' INFO_2: Written by Mark Junker in 1997
' InterNet: mjs@prg.hannover.sgh-net.de
' FIDO: Mark Junker@2:2437/47.21
'
' COMMENT: While I wrote this program I often asked me, if it isn't easier
' to write an interpreter for pearl-scripts :]
'
' COMMENT: To start the program press SHIFT+F5 within the QBasic IDE
' or start it from the command-line with QBASIC /RUN MACROS
'
DEFINT A-Z
DECLARE FUNCTION ReplaceOp$ (a$)
DECLARE FUNCTION StrTrimLeft$ (a$, b$)
DECLARE FUNCTION StrTrimRight$ (a$, b$)
DECLARE FUNCTION StrTrim$ (a$, b$)
DECLARE SUB StrSplitString (SplitString$, SplitChars$, SplitField$(), SplitCount%)
DECLARE FUNCTION Min% (a%, b%)
DECLARE FUNCTION StrInstrLeft% (SearchStart%, SearchIn$, SearchFor$)
DECLARE FUNCTION StrAscii% (a$)
CONST MaxOpCodeBase = 3
CONST MaxOpCodeType = 8
CLS
DIM LineData$(1 TO 2)
DIM StrucData$(1 TO 5)
DIM OpCodeList$(0 TO 255)
DIM OpCodeByte(1 TO MaxOpCodeType, 1 TO MaxOpCodeBase)
DIM OpCodeStat(1 TO 10) ' don't need mode :)
Instructs$ = ""
LineOfs$ = ""
OPEN "I", 1, "insns.dat"
OPEN "B", 3, "insns.tmp"
qt$ = CHR$(34)
crlf$ = CHR$(13) + CHR$(10)
'
' preprocessing the current file
'
HexChar$ = "0123456789ABCDEF"
PRINT "Preprocessing INSNS.DAT"
OpCodes = 0
OpCodeDebug = 0
NowLineOfs& = 1
lineNr = 0
WHILE NOT EOF(1)
lineNr = lineNr + 1
IF (lineNr AND 15) = 0 THEN
LOCATE , 1
PRINT lineNr, OpCodes, OpCodeDebug;
END IF
LINE INPUT #1, l$
CALL StrSplitString(l$, ";", LineData$(), SplitCount)
IF SplitCount THEN
LineData$(1) = StrTrim$(LineData$(1), CHR$(9) + " ")
IF LEN(LineData$(1)) THEN
CALL StrSplitString(LineData$(1), " ", StrucData$(), cntSplit)
IF cntSplit <> 4 THEN
PRINT "line"; lineNr; " does not contain four fields"
END
END IF
tst$ = UCASE$(StrucData$(2))
res$ = ""
cnt% = 1
isfirst = 1
op = 1
p = StrInstrLeft(1, tst$ + ",", "|:,")
WHILE p
h$ = ReplaceOp$(MID$(tst$, op, p - op))
IF LEN(h$) THEN
SELECT CASE MID$(tst$, p, 1)
CASE ""
IF isfirst THEN
res$ = res$ + h$
ELSE
res$ = res$ + "|" + h$
END IF
isfirst = 0
CASE ","
IF isfirst THEN
res$ = res$ + h$ + ","
ELSE
res$ = res$ + "|" + h$ + ","
END IF
cnt% = cnt% + 1
isfirst = 1
CASE "|"
IF isfirst THEN
res$ = res$ + h$
ELSE
res$ = res$ + "|" + h$
END IF
isfirst = 0
CASE ":"
res$ = res$ + h$ + "|COLON,"
cnt% = cnt% + 1
END SELECT
END IF
op = p + 1
p = StrInstrLeft(op, tst$ + ",", "|:,")
WEND
FOR a = cnt% + 1 TO 3
res$ = res$ + ",0"
NEXT
StrucData$(2) = res$
IF LEFT$(res$, 2) = "0," THEN cnt% = cnt% - 1
StrucData$(5) = LTRIM$(STR$(cnt%))
NoDebug = 0
res$ = ""
tst$ = UCASE$(StrucData$(4))
op = 1
p = INSTR(tst$ + ",", ",")
isfirst = 1
WHILE p
h$ = MID$(tst$, op, p - op)
IF h$ = "ND" THEN
NoDebug = 1
ELSE
IF isfirst THEN
res$ = res$ + "IF_" + h$
ELSE
res$ = res$ + "|IF_" + h$
END IF
isfirst = 0
END IF
op = p + 1
p = INSTR(op, tst$ + ",", ",")
WEND
StrucData$(4) = res$
tst$ = UCASE$(StrucData$(3))
SELECT CASE tst$
CASE "IGNORE"
GOTO skipOpCode
CASE "\0", "\340"
OpCodeDebug = OpCodeDebug + 1 ' don't forget to increment
GOTO skipOpCode
END SELECT
AddRegs = 0
AddCCode = 0
NextIsOpCode = 0
opCodeVal$ = ""
op = 1
p = INSTR(tst$ + "\", "\")
DO WHILE p
h$ = MID$(tst$, op, p - op)
IF LEFT$(h$, 1) = "X" THEN
opCodeVal$ = CHR$(VAL("&H" + MID$(h$, 2)))
EXIT DO
ELSE
SELECT CASE h$
CASE "1", "2", "3"
NextIsOpCode = 1
CASE "4"
opCodeVal$ = CHR$(&H7) + CHR$(&H17) + CHR$(&H1F)
EXIT DO
CASE "5"
opCodeVal$ = CHR$(&HA1) + CHR$(&HA9)
EXIT DO
CASE "6"
opCodeVal$ = CHR$(&H6) + CHR$(&HE) + CHR$(&H16) + CHR$(&H1E)
EXIT DO
CASE "7"
opCodeVal$ = CHR$(&HA0) + CHR$(&HA8)
EXIT DO
CASE "10", "11", "12"
NextIsOpCode = 1
AddRegs = VAL(h$) - 9
CASE "330"
NextIsOpCode = 1
AddCCode = VAL(h$) - 329
CASE "17"
opCodeVal$ = CHR$(0)
EXIT DO
CASE ELSE
IF NextIsOpCode THEN
PRINT "Line:"; lineNr
PRINT "Unknown value: " + h$
END
END IF
END SELECT
END IF
op = p + 1
p = INSTR(op, tst$ + "\", "\")
LOOP
IF (p = 0) THEN
PRINT "No opcode found in line"; lineNr
PRINT "Line:"
PRINT l$
END
END IF
IF NoDebug = 0 THEN
FOR a = 1 TO LEN(opCodeVal$)
h = ASC(MID$(opCodeVal$, a, 1))
OpCodeStr$ = MKI$(OpCodeDebug)
IF AddRegs THEN
EndNr = 7
ELSEIF AddCCode THEN
EndNr = 15
ELSE
EndNr = 0
END IF
FOR b = 0 TO EndNr
OpCodeList$(h + b) = OpCodeList$(h + b) + OpCodeStr$
NEXT
NEXT
OpCodeDebug = OpCodeDebug + 1
END IF
skipOpCode:
OpCodes = OpCodes + 1
LineOfs$ = LineOfs$ + MKL$(NowLineOfs&)
LineLg = 1
h$ = CHR$(NoDebug)
PUT #3, NowLineOfs&, h$
NowLineOfs& = NowLineOfs& + 1
FOR a = 1 TO 5
lg = LEN(StrucData$(a))
h$ = CHR$(lg) + StrucData$(a)
PUT #3, NowLineOfs&, h$
NowLineOfs& = NowLineOfs& + lg + 1
LineLg = LineLg + lg + 1
NEXT
LineOfs$ = LineOfs$ + MKI$(LineLg)
END IF
END IF
WEND
LOCATE , 1
PRINT lineNr, OpCodes, OpCodeDebug
'
' creating insnsa.c
'
PRINT "Creating INSNSA.C"
OPEN "O", 2, "insnsa.c"
strBegStart$ = "static struct itemplate instrux_"
strBegEnd$ = "[] = {"
strEnd$ = " {-1}" + crlf$ + "};" + crlf$
PRINT #2, "/* This file auto-generated from insns.dat by insns.bas - don't edit it */"
PRINT #2, ""
PRINT #2, "#include <stdio.h>"
PRINT #2, "#include " + qt$ + "nasm.h" + qt$
PRINT #2, "#include " + qt$ + "insns.h" + qt$
PRINT #2, ""
oldOpCode$ = ""
pOfs = 1
FOR a = 1 TO OpCodes
LineOfs& = CVL(MID$(LineOfs$, pOfs, 4))
l$ = SPACE$(CVI(MID$(LineOfs$, pOfs + 4, 2)))
pOfs = pOfs + 6
GET #3, LineOfs&, l$
' split data into fields
NoDebug = ASC(LEFT$(l$, 1))
pLn = 2
FOR b = 1 TO 5
lgLn = ASC(MID$(l$, pLn, 1))
StrucData$(b) = MID$(l$, pLn + 1, lgLn)
pLn = pLn + lgLn + 1
NEXT
IF oldOpCode$ <> StrucData$(1) THEN
Instructs$ = Instructs$ + StrucData$(1) + CHR$(0)
IF LEN(oldOpCode$) THEN PRINT #2, strEnd$
PRINT #2, strBegStart$ + StrucData$(1) + strBegEnd$
oldOpCode$ = StrucData$(1)
END IF
SELECT CASE UCASE$(StrucData$(3))
CASE "IGNORE"
CASE ELSE
PRINT #2, " {I_" + oldOpCode$ + ", " + StrucData$(5) + ", {" + StrucData$(2) + "}, " + qt$ + StrucData$(3) + qt$ + ", " + StrucData$(4) + "},"
END SELECT
NEXT
IF LEN(oldOpCode$) THEN PRINT #2, strEnd$
PRINT #2, "struct itemplate *nasm_instructions[] = {"
op = 1
p = INSTR(Instructs$, CHR$(0))
WHILE p
h$ = MID$(Instructs$, op, p - op)
PRINT #2, " instrux_" + h$ + ","
op = p + 1
p = INSTR(op, Instructs$, CHR$(0))
WEND
PRINT #2, "};"
CLOSE 2
'
' creating insnsd.c
'
PRINT "Creating INSNSD.C"
OPEN "O", 2, "insnsd.c"
PRINT #2, "/* This file auto-generated from insns.dat by insns.bas - don't edit it */"
PRINT #2, ""
PRINT #2, "#include <stdio.h>"
PRINT #2, "#include " + qt$ + "nasm.h" + qt$
PRINT #2, "#include " + qt$ + "insns.h" + qt$
PRINT #2, ""
PRINT #2, "static struct itemplate instrux[] = {"
pOfs = 1
FOR a = 1 TO OpCodes
LineOfs& = CVL(MID$(LineOfs$, pOfs, 4))
l$ = SPACE$(CVI(MID$(LineOfs$, pOfs + 4, 2)))
pOfs = pOfs + 6
GET #3, LineOfs&, l$
' split data into fields
NoDebug = ASC(LEFT$(l$, 1))
pLn = 2
FOR b = 1 TO 5
lgLn = ASC(MID$(l$, pLn, 1))
StrucData$(b) = MID$(l$, pLn + 1, lgLn)
pLn = pLn + lgLn + 1
NEXT
IF NoDebug OR (UCASE$(StrucData$(3)) = "IGNORE") THEN
' ignorieren
ELSE
PRINT #2, " {I_" + StrucData$(1) + ", " + StrucData$(5) + ", {" + StrucData$(2) + "}, " + qt$ + StrucData$(3) + qt$ + ", " + StrucData$(4) + "},"
END IF
NEXT
PRINT #2, " {-1}" + crlf$ + "};" + crlf$
OpCodeBegS$ = "static struct itemplate *itable_"
OpCodeBegE$ = "[] = {"
OpCodeEnd$ = " NULL" + crlf$ + "};" + crlf$
FOR a = 0 TO 255
PRINT #2, OpCodeBegS$ + RIGHT$("00" + HEX$(a), 2) + OpCodeBegE$
h$ = OpCodeList$(a)
FOR b = 1 TO LEN(h$) STEP 2
OpCodePos = CVI(MID$(h$, b, 2))
PRINT #2, " instrux +" + STR$(OpCodePos) + ","
NEXT
PRINT #2, OpCodeEnd$
NEXT
PRINT #2, "struct itemplate **itable[] = {"
FOR a = 0 TO 255
PRINT #2, " itable_" + RIGHT$("00" + HEX$(a), 2) + ","
NEXT
PRINT #2, "};"
CLOSE 2
CLOSE 3
KILL "insns.tmp"
CLOSE 1
SYSTEM
FUNCTION ReplaceOp$ (a$)
tst$ = UCASE$(a$)
SELECT CASE tst$
' CASE "ND"
' ReplaceOp$ = ""
CASE "VOID", ""
ReplaceOp$ = "0"
CASE "IMM"
ReplaceOp$ = "IMMEDIATE"
CASE "MEM"
ReplaceOp$ = "MEMORY"
CASE "MEM8", "MEM16", "MEM32", "MEM64", "MEM80"
ReplaceOp$ = "MEMORY|BITS" + MID$(tst$, 4)
CASE "REG8", "REG16", "REG32"
ReplaceOp$ = tst$
CASE "RM8", "RM16", "RM32"
ReplaceOp$ = "REGMEM|BITS" + MID$(tst$, 3)
CASE "IMM8", "IMM16", "IMM32"
ReplaceOp$ = "IMMEDIATE|BITS" + MID$(tst$, 4)
CASE ELSE
ReplaceOp$ = tst$
END SELECT
END FUNCTION
FUNCTION Min% (a%, b%)
IF a% < b% THEN Min% = a% ELSE Min% = b%
END FUNCTION
FUNCTION StrAscii (a$)
IF LEN(a$) = 0 THEN
StrAscii = -1
ELSE
StrAscii = ASC(a$)
END IF
END FUNCTION
' same as =INSTR(SearchStart, SearchIn, ANY SearchFor$) in PowerBASIC(tm)
'
FUNCTION StrInstrLeft (SearchStart, SearchIn$, SearchFor$)
ValuesCount = LEN(SearchFor$)
MaxValue = LEN(SearchIn$) + 1
MinValue = MaxValue
FOR Counter1 = 1 TO ValuesCount
SearchChar$ = MID$(SearchFor$, Counter1, 1)
hVal2 = INSTR(SearchStart, SearchIn$, SearchChar$)
IF hVal2 > 0 THEN MinValue = Min%(hVal2, MinValue)
NEXT
IF MinValue = MaxValue THEN MinValue = 0
StrInstrLeft = MinValue
END FUNCTION
'
' This is a very damn fuckin' shit version of this splitting routine.
' At this time, it's not very useful :]
'
SUB StrSplitString (SplitString$, SplitChars$, SplitField$(), SplitCount)
StartIndex = LBOUND(SplitField$)
LastIndex = UBOUND(SplitField$)
ActualIndex& = StartIndex
SplitCount = 0
LastPos = 1
FoundPos = StrInstrLeft(LastPos, SplitString$, SplitChars$ + CHR$(34))
GetDirect = 0
EndLoop = 0
TempString$ = ""
DO WHILE FoundPos > 0
FoundCharVal = StrAscii(MID$(SplitString$, FoundPos, 1))
PosDiff = (FoundPos - LastPos) + 1
SELECT CASE FoundCharVal
CASE 34
TempString$ = TempString$ + MID$(SplitString$, LastPos, PosDiff - 1)
SELECT CASE EndLoop
CASE 0
EndLoop = 2
CASE 3
EndLoop = 0
END SELECT
CASE ELSE
TempString$ = TempString$ + MID$(SplitString$, LastPos, PosDiff - 1)
SplitField$(ActualIndex&) = TempString$
TempString$ = ""
ActualIndex& = ActualIndex& + 1
IF ActualIndex& > LastIndex THEN
ActualIndex& = LastIndex
EndLoop = 1
END IF
END SELECT
SELECT CASE EndLoop
CASE 0
DO
LastPos = FoundPos + 1
FoundPos = StrInstrLeft(LastPos, SplitString$, SplitChars$)
LOOP WHILE LastPos = FoundPos
FoundPos = StrInstrLeft(LastPos, SplitString$, SplitChars$ + CHR$(34))
CASE 1
FoundPos = 0
LastPos = LEN(SplitString$) + 1
CASE 2
EndLoop = 3
LastPos = FoundPos + 1
FoundPos = StrInstrLeft(LastPos, SplitString$, CHR$(34))
IF FoundPos = 0 THEN
SplitString$ = SplitString$ + CHR$(34)
FoundPos = LEN(SplitString$)
END IF
END SELECT
LOOP
IF EndLoop = 0 THEN
IF LEN(TempString$) > 0 THEN
SplitField$(ActualIndex&) = TempString$
ELSEIF LastPos <= LEN(SplitString$) THEN
SplitField$(ActualIndex&) = MID$(SplitString$, LastPos)
ELSE
ActualIndex& = ActualIndex& - 1
END IF
END IF
FOR a = ActualIndex& + 1 TO LastIndex
SplitField$(a) = ""
NEXT
SplitCount = (ActualIndex& - StartIndex) + 1
END SUB
FUNCTION StrTrim$ (a$, b$)
StrTrim$ = StrTrimRight$(StrTrimLeft$(a$, b$), b$)
END FUNCTION
FUNCTION StrTrimLeft$ (a$, b$) 'public
p = 0
l = LEN(a$)
DO
p = p + 1
t$ = MID$(a$, p, 1)
LOOP WHILE (p < l) AND (INSTR(b$, t$) > 0)
StrTrimLeft$ = MID$(a$, p)
END FUNCTION
FUNCTION StrTrimRight$ (a$, b$) 'public
l = LEN(a$)
p = l + 1
DO
p = p - 1
IF p > 0 THEN
t$ = MID$(a$, p, 1)
ELSE
t$ = ""
END IF
LOOP WHILE (p > 0) AND (INSTR(b$, t$) > 0)
StrTrimRight$ = LEFT$(a$, p)
END FUNCTION

155
insns.dat
View file

@ -14,9 +14,9 @@
AAA void \1\x37 8086
AAD void \2\xD5\x0A 8086
AAD imm \1\xD5\24 8086,UNDOC
AAD imm \1\xD5\24 8086
AAM void \2\xD4\x0A 8086
AAM imm \1\xD4\24 8086,UNDOC
AAM imm \1\xD4\24 8086
AAS void \1\x3F 8086
ADC mem,reg8 \300\1\x10\101 8086,SM
ADC reg8,reg8 \300\1\x10\101 8086
@ -225,7 +225,7 @@ FBLD mem \300\1\xDF\204 8086,FPU
FBSTP mem80 \300\1\xDF\206 8086,FPU
FBSTP mem \300\1\xDF\206 8086,FPU
FCHS void \2\xD9\xE0 8086,FPU
FCLEX void \2\xDB\xE2 8086,FPU
FCLEX void \3\x9B\xDB\xE2 8086,FPU
FCMOVB fpureg \1\xDA\10\xC0 P6,FPU
FCMOVB fpu0,fpureg \1\xDA\11\xC0 P6,FPU
FCMOVBE fpureg \1\xDA\10\xD0 P6,FPU
@ -257,7 +257,7 @@ FCOMP fpu0,fpureg \1\xD8\11\xD8 8086,FPU
FCOMPP void \2\xDE\xD9 8086,FPU
FCOS void \2\xD9\xFF 386,FPU
FDECSTP void \2\xD9\xF6 8086,FPU
FDISI void \2\xDB\xE1 8086,FPU
FDISI void \3\x9B\xDB\xE1 8086,FPU
FDIV mem32 \300\1\xD8\206 8086,FPU
FDIV mem64 \300\1\xDC\206 8086,FPU
FDIV fpureg|to \1\xDC\10\xF8 8086,FPU
@ -274,7 +274,7 @@ FDIVR fpureg \1\xD8\10\xF8 8086,FPU
FDIVR fpu0,fpureg \1\xD8\11\xF8 8086,FPU
FDIVRP fpureg \1\xDE\10\xF0 8086,FPU
FDIVRP fpureg,fpu0 \1\xDE\10\xF0 8086,FPU
FENI void \2\xDB\xE0 8086,FPU
FENI void \3\x9B\xDB\xE0 8086,FPU
FFREE fpureg \1\xDD\10\xC0 8086,FPU
FIADD mem32 \300\1\xDA\200 8086,FPU
FIADD mem16 \300\1\xDE\200 8086,FPU
@ -292,7 +292,7 @@ FILD mem64 \300\1\xDF\205 8086,FPU
FIMUL mem32 \300\1\xDA\201 8086,FPU
FIMUL mem16 \300\1\xDE\201 8086,FPU
FINCSTP void \2\xD9\xF7 8086,FPU
FINIT void \2\xDB\xE3 8086,FPU
FINIT void \3\x9B\xDB\xE3 8086,FPU
FIST mem32 \300\1\xDB\202 8086,FPU
FIST mem16 \300\1\xDF\202 8086,FPU
FISTP mem32 \300\1\xDB\203 8086,FPU
@ -323,14 +323,23 @@ FMUL fpureg \1\xD8\10\xC8 8086,FPU
FMUL fpu0,fpureg \1\xD8\11\xC8 8086,FPU
FMULP fpureg \1\xDE\10\xC8 8086,FPU
FMULP fpureg,fpu0 \1\xDE\10\xC8 8086,FPU
FNCLEX void \2\xDB\xE2 8086,FPU
FNDISI void \2\xDB\xE1 8086,FPU
FNENI void \2\xDB\xE0 8086,FPU
FNINIT void \2\xDB\xE3 8086,FPU
FNOP void \2\xD9\xD0 8086,FPU
FNSAVE mem \300\1\xDD\206 8086,FPU
FNSTCW mem \300\1\xD9\207 8086,FPU,SW
FNSTENV mem \300\1\xD9\206 8086,FPU
FNSTSW mem \300\1\xDD\207 8086,FPU,SW
FNSTSW reg_ax \2\xDF\xE0 286,FPU
FPATAN void \2\xD9\xF3 8086,FPU
FPREM void \2\xD9\xF8 8086,FPU
FPREM1 void \2\xD9\xF5 386,FPU
FPTAN void \2\xD9\xF2 8086,FPU
FRNDINT void \2\xD9\xFC 8086,FPU
FRSTOR mem \300\1\xDD\204 8086,FPU
FSAVE mem \300\1\xDD\206 8086,FPU
FSAVE mem \300\2\x9B\xDD\206 8086,FPU
FSCALE void \2\xD9\xFD 8086,FPU
FSETPM void \2\xDB\xE4 286,FPU
FSIN void \2\xD9\xFE 386,FPU
@ -339,14 +348,14 @@ FSQRT void \2\xD9\xFA 8086,FPU
FST mem32 \300\1\xD9\202 8086,FPU
FST mem64 \300\1\xDD\202 8086,FPU
FST fpureg \1\xDD\10\xD0 8086,FPU
FSTCW mem \300\1\xD9\207 8086,FPU,SW
FSTENV mem \300\1\xD9\206 8086,FPU
FSTCW mem \300\2\x9B\xD9\207 8086,FPU,SW
FSTENV mem \300\2\x9B\xD9\206 8086,FPU
FSTP mem32 \300\1\xD9\203 8086,FPU
FSTP mem64 \300\1\xDD\203 8086,FPU
FSTP mem80 \300\1\xDB\207 8086,FPU
FSTP fpureg \1\xDD\10\xD8 8086,FPU
FSTSW mem \300\1\xDD\207 8086,FPU,SW
FSTSW reg_ax \2\xDF\xE0 286,FPU
FSTSW mem \300\2\x9B\xDD\207 8086,FPU,SW
FSTSW reg_ax \3\x9B\xDF\xE0 286,FPU
FSUB mem32 \300\1\xD8\204 8086,FPU
FSUB mem64 \300\1\xDC\204 8086,FPU
FSUB fpureg|to \1\xDC\10\xE8 8086,FPU
@ -365,11 +374,13 @@ FSUBRP fpureg \1\xDE\10\xE0 8086,FPU
FSUBRP fpureg,fpu0 \1\xDE\10\xE0 8086,FPU
FTST void \2\xD9\xE4 8086,FPU
FUCOM fpureg \1\xDD\10\xE0 386,FPU
FUCOM fpu0,fpureg \1\xDD\11\xE0 386,FPU
FUCOMI fpureg \1\xDB\10\xE8 P6,FPU
FUCOMI fpu0,fpureg \1\xDB\11\xE8 P6,FPU
FUCOMIP fpureg \1\xDF\10\xE8 P6,FPU
FUCOMIP fpu0,fpureg \1\xDF\11\xE8 P6,FPU
FUCOMP fpureg \1\xDD\10\xE8 386,FPU
FUCOMP fpu0,fpureg \1\xDD\11\xE8 386,FPU
FUCOMPP void \2\xDA\xE9 386,FPU
FXAM void \2\xD9\xE5 8086,FPU
FXCH void \2\xD9\xC9 8086,FPU
@ -384,7 +395,7 @@ IBTS mem,reg16 \320\300\2\x0F\xA7\101 386,SW,UNDOC,ND
IBTS reg16,reg16 \320\300\2\x0F\xA7\101 386,UNDOC,ND
IBTS mem,reg32 \321\300\2\x0F\xA7\101 386,SD,UNDOC,ND
IBTS reg32,reg32 \321\300\2\x0F\xA7\101 386,UNDOC,ND
ICEBP void \1\xF1 286,UNDOC
ICEBP void \1\xF1 P6,ND
IDIV rm8 \300\1\xF6\207 8086
IDIV rm16 \320\300\1\xF7\207 8086
IDIV rm32 \321\300\1\xF7\207 386
@ -398,7 +409,7 @@ IMUL reg32,reg32 \321\301\2\x0F\xAF\110 386
IMUL reg16,mem,imm8 \320\301\1\x6B\110\16 286,SM
IMUL reg16,reg16,imm8 \320\301\1\x6B\110\16 286
IMUL reg16,mem,imm \320\301\1\x69\110\32 286,SM
IMUL reg16,reg16,imm \320\301\1\x69\110\32 286
IMUL reg16,reg16,imm \320\301\1\x69\110\32 286,SM
IMUL reg32,mem,imm8 \321\301\1\x6B\110\16 386,SM
IMUL reg32,reg32,imm8 \321\301\1\x6B\110\16 386
IMUL reg32,mem,imm \321\301\1\x69\110\42 386,SM
@ -423,8 +434,8 @@ INSB void \1\x6C 186
INSD void \321\1\x6D 386
INSW void \320\1\x6D 186
INT imm \1\xCD\24 8086
INT01 void \1\xF1 286,UNDOC
INT1 void \1\xF1 286,UNDOC
INT01 void \1\xF1 P6,ND
INT1 void \1\xF1 P6
INT3 void \1\xCC 8086
INTO void \1\xCE 8086
INVD void \2\x0F\x08 486
@ -506,16 +517,21 @@ LSS reg32,mem \321\301\2\x0F\xB2\110 386
LTR mem \300\1\x0F\17\203 286,PRIV
LTR mem16 \300\1\x0F\17\203 286,PRIV
LTR reg16 \300\1\x0F\17\203 286,PRIV
MOV mem,reg_cs \300\1\x8C\101 8086,SM
MOV mem,reg_dess \300\1\x8C\101 8086,SM
MOV mem,reg_fsgs \300\1\x8C\101 386,SM
MOV reg16,reg_cs \300\1\x8C\101 8086
MOV reg16,reg_dess \300\1\x8C\101 8086
MOV reg16,reg_fsgs \300\1\x8C\101 386
MOV reg_dess,mem \301\1\x8E\110 8086,SM
MOV reg_dess,reg16 \301\1\x8E\110 8086
MOV reg_fsgs,mem \301\1\x8E\110 386,SM
MOV reg_fsgs,reg16 \301\1\x8E\110 386
MOV mem,reg_cs \320\300\1\x8C\201 8086,SM
MOV mem,reg_dess \320\300\1\x8C\101 8086,SM
MOV mem,reg_fsgs \320\300\1\x8C\101 386,SM
MOV reg16,reg_cs \320\300\1\x8C\201 8086
MOV reg16,reg_dess \320\300\1\x8C\101 8086
MOV reg16,reg_fsgs \320\300\1\x8C\101 386
MOV rm32,reg_cs \321\300\1\x8C\201 8086
MOV rm32,reg_dess \321\300\1\x8C\101 8086
MOV rm32,reg_fsgs \321\300\1\x8C\101 386
MOV reg_dess,mem \320\301\1\x8E\110 8086,SM
MOV reg_fsgs,mem \320\301\1\x8E\110 386,SM
MOV reg_dess,reg16 \320\301\1\x8E\110 8086
MOV reg_fsgs,reg16 \320\301\1\x8E\110 386
MOV reg_dess,rm32 \321\301\1\x8E\110 8086
MOV reg_fsgs,rm32 \321\301\1\x8E\110 386
MOV reg_al,mem_offs \301\1\xA0\35 8086,SM
MOV reg_ax,mem_offs \301\320\1\xA1\35 8086,SM
MOV reg_eax,mem_offs \301\321\1\xA1\35 386,SM
@ -624,6 +640,8 @@ PADDD mmxreg,mem \301\2\x0F\xFE\110 PENT,MMX,SM
PADDD mmxreg,mmxreg \2\x0F\xFE\110 PENT,MMX
PADDSB mmxreg,mem \301\2\x0F\xEC\110 PENT,MMX,SM
PADDSB mmxreg,mmxreg \2\x0F\xEC\110 PENT,MMX
PADDSIW mmxreg,mem \301\2\x0F\x51\110 PENT,MMX,SM,CYRIX
PADDSIW mmxreg,mmxreg \2\x0F\x51\110 PENT,MMX,CYRIX
PADDSW mmxreg,mem \301\2\x0F\xED\110 PENT,MMX,SM
PADDSW mmxreg,mmxreg \2\x0F\xED\110 PENT,MMX
PADDUSB mmxreg,mem \301\2\x0F\xDC\110 PENT,MMX,SM
@ -636,6 +654,8 @@ PAND mmxreg,mem \301\2\x0F\xDB\110 PENT,MMX,SM
PAND mmxreg,mmxreg \2\x0F\xDB\110 PENT,MMX
PANDN mmxreg,mem \301\2\x0F\xDF\110 PENT,MMX,SM
PANDN mmxreg,mmxreg \2\x0F\xDF\110 PENT,MMX
PAVEB mmxreg,mem \301\2\x0F\x50\110 PENT,MMX,SM,CYRIX
PAVEB mmxreg,mmxreg \2\x0F\x50\110 PENT,MMX,CYRIX
PCMPEQB mmxreg,mem \301\2\x0F\x74\110 PENT,MMX,SM
PCMPEQB mmxreg,mmxreg \2\x0F\x74\110 PENT,MMX
PCMPEQD mmxreg,mem \301\2\x0F\x76\110 PENT,MMX,SM
@ -648,19 +668,31 @@ PCMPGTD mmxreg,mem \301\2\x0F\x66\110 PENT,MMX,SM
PCMPGTD mmxreg,mmxreg \2\x0F\x66\110 PENT,MMX
PCMPGTW mmxreg,mem \301\2\x0F\x65\110 PENT,MMX,SM
PCMPGTW mmxreg,mmxreg \2\x0F\x65\110 PENT,MMX
PDISTIB mmxreg,mem \301\2\x0F\x54\110 PENT,MMX,SM,CYRIX
PMACHRIW mmxreg,mem \301\2\x0F\x5E\110 PENT,MMX,SM,CYRIX
PMADDWD mmxreg,mem \301\2\x0F\xF5\110 PENT,MMX,SM
PMADDWD mmxreg,mmxreg \2\x0F\xF5\110 PENT,MMX
PMAGW mmxreg,mem \301\2\x0F\x52\110 PENT,MMX,SM,CYRIX
PMAGW mmxreg,mmxreg \2\x0F\x52\110 PENT,MMX,CYRIX
PMULHRW mmxreg,mem \301\2\x0F\x59\110 PENT,MMX,SM,CYRIX
PMULHRW mmxreg,mmxreg \2\x0F\x59\110 PENT,MMX,CYRIX
PMULHRIW mmxreg,mem \301\2\x0F\x5D\110 PENT,MMX,SM,CYRIX
PMULHRIW mmxreg,mmxreg \2\x0F\x5D\110 PENT,MMX,CYRIX
PMULHW mmxreg,mem \301\2\x0F\xE5\110 PENT,MMX,SM
PMULHW mmxreg,mmxreg \2\x0F\xE5\110 PENT,MMX
PMULLW mmxreg,mem \301\2\x0F\xD5\110 PENT,MMX,SM
PMULLW mmxreg,mmxreg \2\x0F\xD5\110 PENT,MMX
POP mem16 \320\300\1\x8F\200 8086
POP mem32 \321\300\1\x8F\200 386
PMVGEZB mmxreg,mem \301\2\x0F\x5C\110 PENT,MMX,SM,CYRIX
PMVLZB mmxreg,mem \301\2\x0F\x5B\110 PENT,MMX,SM,CYRIX
PMVNZB mmxreg,mem \301\2\x0F\x5A\110 PENT,MMX,SM,CYRIX
PMVZB mmxreg,mem \301\2\x0F\x58\110 PENT,MMX,SM,CYRIX
POP reg16 \320\10\x58 8086
POP reg32 \321\10\x58 386
POP rm16 \320\300\1\x8F\200 8086
POP rm32 \321\300\1\x8F\200 386
POP reg_cs \1\x0F 8086,UNDOC,ND
POP reg_dess \4 8086
POP reg_fsgs \1\x0F\5 386
POP reg16 \320\10\x58 8086
POP reg32 \321\10\x58 386
POPA void \322\1\x61 186
POPAD void \321\1\x61 386
POPAW void \320\1\x61 186
@ -699,6 +731,8 @@ PSUBD mmxreg,mem \301\2\x0F\xFA\110 PENT,MMX,SM
PSUBD mmxreg,mmxreg \2\x0F\xFA\110 PENT,MMX
PSUBSB mmxreg,mem \301\2\x0F\xE8\110 PENT,MMX,SM
PSUBSB mmxreg,mmxreg \2\x0F\xE8\110 PENT,MMX
PSUBSIW mmxreg,mem \301\2\x0F\x55\110 PENT,MMX,SM,CYRIX
PSUBSIW mmxreg,mmxreg \2\x0F\x55\110 PENT,MMX,CYRIX
PSUBSW mmxreg,mem \301\2\x0F\xE9\110 PENT,MMX,SM
PSUBSW mmxreg,mmxreg \2\x0F\xE9\110 PENT,MMX
PSUBUSB mmxreg,mem \301\2\x0F\xD8\110 PENT,MMX,SM
@ -719,12 +753,12 @@ PUNPCKLDQ mmxreg,mem \301\2\x0F\x62\110 PENT,MMX,SM
PUNPCKLDQ mmxreg,mmxreg \2\x0F\x62\110 PENT,MMX
PUNPCKLWD mmxreg,mem \301\2\x0F\x61\110 PENT,MMX,SM
PUNPCKLWD mmxreg,mmxreg \2\x0F\x61\110 PENT,MMX
PUSH mem16 \320\300\1\xFF\206 8086
PUSH mem32 \321\300\1\xFF\206 386
PUSH reg_fsgs \1\x0F\7 386
PUSH reg_sreg \6 8086
PUSH reg16 \320\10\x50 8086
PUSH reg32 \321\10\x50 386
PUSH rm16 \320\300\1\xFF\206 8086
PUSH rm32 \321\300\1\xFF\206 386
PUSH reg_fsgs \1\x0F\7 386
PUSH reg_sreg \6 8086
PUSH imm8 \1\x6A\14 286
PUSH imm16 \320\1\x68\30 286
PUSH imm32 \321\1\x68\40 386
@ -738,22 +772,22 @@ PXOR mmxreg,mem \301\2\x0F\xEF\110 PENT,MMX,SM
PXOR mmxreg,mmxreg \2\x0F\xEF\110 PENT,MMX
RCL rm8,unity \300\1\xD0\202 8086
RCL rm8,reg_cl \300\1\xD2\202 8086
RCL rm8,imm \300\1\xC0\202\25 286
RCL rm8,imm \300\1\xC0\202\25 286,SB
RCL rm16,unity \320\300\1\xD1\202 8086
RCL rm16,reg_cl \320\300\1\xD3\202 8086
RCL rm16,imm \320\300\1\xC1\202\25 286
RCL rm16,imm \320\300\1\xC1\202\25 286,SB
RCL rm32,unity \321\300\1\xD1\202 386
RCL rm32,reg_cl \321\300\1\xD3\202 386
RCL rm32,imm \321\300\1\xC1\202\25 386
RCL rm32,imm \321\300\1\xC1\202\25 386,SB
RCR rm8,unity \300\1\xD0\203 8086
RCR rm8,reg_cl \300\1\xD2\203 8086
RCR rm8,imm \300\1\xC0\203\25 286
RCR rm8,imm \300\1\xC0\203\25 286,SB
RCR rm16,unity \320\300\1\xD1\203 8086
RCR rm16,reg_cl \320\300\1\xD3\203 8086
RCR rm16,imm \320\300\1\xC1\203\25 286
RCR rm16,imm \320\300\1\xC1\203\25 286,SB
RCR rm32,unity \321\300\1\xD1\203 386
RCR rm32,reg_cl \321\300\1\xD3\203 386
RCR rm32,imm \321\300\1\xC1\203\25 386
RCR rm32,imm \321\300\1\xC1\203\25 386,SB
RDMSR void \2\x0F\x32 PENT
RDPMC void \2\x0F\x33 P6
RDTSC void \2\x0F\x31 PENT
@ -770,43 +804,43 @@ RETN void \1\xC3 8086
RETN imm \1\xC2\30 8086
ROL rm8,unity \300\1\xD0\200 8086
ROL rm8,reg_cl \300\1\xD2\200 8086
ROL rm8,imm \300\1\xC0\200\25 286
ROL rm8,imm \300\1\xC0\200\25 286,SB
ROL rm16,unity \320\300\1\xD1\200 8086
ROL rm16,reg_cl \320\300\1\xD3\200 8086
ROL rm16,imm \320\300\1\xC1\200\25 286
ROL rm16,imm \320\300\1\xC1\200\25 286,SB
ROL rm32,unity \321\300\1\xD1\200 386
ROL rm32,reg_cl \321\300\1\xD3\200 386
ROL rm32,imm \321\300\1\xC1\200\25 386
ROL rm32,imm \321\300\1\xC1\200\25 386,SB
ROR rm8,unity \300\1\xD0\201 8086
ROR rm8,reg_cl \300\1\xD2\201 8086
ROR rm8,imm \300\1\xC0\201\25 286
ROR rm8,imm \300\1\xC0\201\25 286,SB
ROR rm16,unity \320\300\1\xD1\201 8086
ROR rm16,reg_cl \320\300\1\xD3\201 8086
ROR rm16,imm \320\300\1\xC1\201\25 286
ROR rm16,imm \320\300\1\xC1\201\25 286,SB
ROR rm32,unity \321\300\1\xD1\201 386
ROR rm32,reg_cl \321\300\1\xD3\201 386
ROR rm32,imm \321\300\1\xC1\201\25 386
ROR rm32,imm \321\300\1\xC1\201\25 386,SB
RSM void \2\x0F\xAA PENT
SAHF void \1\x9E 8086
SAL rm8,unity \300\1\xD0\204 8086,ND
SAL rm8,reg_cl \300\1\xD2\204 8086,ND
SAL rm8,imm \300\1\xC0\204\25 286,ND
SAL rm8,imm \300\1\xC0\204\25 286,ND,SB
SAL rm16,unity \320\300\1\xD1\204 8086,ND
SAL rm16,reg_cl \320\300\1\xD3\204 8086,ND
SAL rm16,imm \320\300\1\xC1\204\25 286,ND
SAL rm16,imm \320\300\1\xC1\204\25 286,ND,SB
SAL rm32,unity \321\300\1\xD1\204 386,ND
SAL rm32,reg_cl \321\300\1\xD3\204 386,ND
SAL rm32,imm \321\300\1\xC1\204\25 386,ND
SAL rm32,imm \321\300\1\xC1\204\25 386,ND,SB
SALC void \1\xD6 8086,UNDOC
SAR rm8,unity \300\1\xD0\207 8086
SAR rm8,reg_cl \300\1\xD2\207 8086
SAR rm8,imm \300\1\xC0\207\25 286
SAR rm8,imm \300\1\xC0\207\25 286,SB
SAR rm16,unity \320\300\1\xD1\207 8086
SAR rm16,reg_cl \320\300\1\xD3\207 8086
SAR rm16,imm \320\300\1\xC1\207\25 286
SAR rm16,imm \320\300\1\xC1\207\25 286,SB
SAR rm32,unity \321\300\1\xD1\207 386
SAR rm32,reg_cl \321\300\1\xD3\207 386
SAR rm32,imm \321\300\1\xC1\207\25 386
SAR rm32,imm \321\300\1\xC1\207\25 386,SB
SBB mem,reg8 \300\1\x18\101 8086,SM
SBB reg8,reg8 \300\1\x18\101 8086
SBB mem,reg16 \320\300\1\x19\101 8086,SM
@ -836,13 +870,13 @@ SCASW void \320\1\xAF 8086
SGDT mem \300\2\x0F\x01\200 286,PRIV
SHL rm8,unity \300\1\xD0\204 8086
SHL rm8,reg_cl \300\1\xD2\204 8086
SHL rm8,imm \300\1\xC0\204\25 286
SHL rm8,imm \300\1\xC0\204\25 286,SB
SHL rm16,unity \320\300\1\xD1\204 8086
SHL rm16,reg_cl \320\300\1\xD3\204 8086
SHL rm16,imm \320\300\1\xC1\204\25 286
SHL rm16,imm \320\300\1\xC1\204\25 286,SB
SHL rm32,unity \321\300\1\xD1\204 386
SHL rm32,reg_cl \321\300\1\xD3\204 386
SHL rm32,imm \321\300\1\xC1\204\25 386
SHL rm32,imm \321\300\1\xC1\204\25 386,SB
SHLD mem,reg16,imm \300\320\2\x0F\xA4\101\26 386,SM2
SHLD reg16,reg16,imm \300\320\2\x0F\xA4\101\26 386,SM2
SHLD mem,reg32,imm \300\321\2\x0F\xA4\101\26 386,SM2
@ -853,13 +887,13 @@ SHLD mem,reg32,reg_cl \300\321\2\x0F\xA5\101 386,SM
SHLD reg32,reg32,reg_cl \300\321\2\x0F\xA5\101 386
SHR rm8,unity \300\1\xD0\205 8086
SHR rm8,reg_cl \300\1\xD2\205 8086
SHR rm8,imm \300\1\xC0\205\25 286
SHR rm8,imm \300\1\xC0\205\25 286,SB
SHR rm16,unity \320\300\1\xD1\205 8086
SHR rm16,reg_cl \320\300\1\xD3\205 8086
SHR rm16,imm \320\300\1\xC1\205\25 286
SHR rm16,imm \320\300\1\xC1\205\25 286,SB
SHR rm32,unity \321\300\1\xD1\205 386
SHR rm32,reg_cl \321\300\1\xD3\205 386
SHR rm32,imm \321\300\1\xC1\205\25 386
SHR rm32,imm \321\300\1\xC1\205\25 386,SB
SHRD mem,reg16,imm \300\320\2\x0F\xAC\101\26 386,SM2
SHRD reg16,reg16,imm \300\320\2\x0F\xAC\101\26 386,SM2
SHRD mem,reg32,imm \300\321\2\x0F\xAC\101\26 386,SM2
@ -874,6 +908,7 @@ SLDT mem16 \300\1\x0F\17\200 286,PRIV
SLDT reg16 \300\1\x0F\17\200 286,PRIV
SMI void \1\xF1 386,UNDOC
SMSW mem \300\2\x0F\x01\204 286,PRIV
SMSW mem16 \300\2\x0F\x01\204 286,PRIV
SMSW reg16 \300\2\x0F\x01\204 286,PRIV
STC void \1\xF9 8086
STD void \1\xFD 8086
@ -921,7 +956,7 @@ TEST rm16,imm \320\300\1\xF7\200\31 8086,SM
TEST rm32,imm \321\300\1\xF7\200\41 386,SM
TEST mem,imm8 \300\1\xF6\200\21 8086,SM
TEST mem,imm16 \320\300\1\xF7\200\31 8086,SM
TEST mem,imm32 \321\300\1\xF7\200\41 386,UNDOC,SM
TEST mem,imm32 \321\300\1\xF7\200\41 386,SM
UMOV mem,reg8 \300\2\x0F\x10\101 386,UNDOC,SM
UMOV reg8,reg8 \300\2\x0F\x10\101 386,UNDOC
UMOV mem,reg16 \320\300\2\x0F\x11\101 386,UNDOC,SM
@ -995,8 +1030,8 @@ XOR mem,imm16 \320\300\1\x81\206\31 8086,SM
XOR mem,imm32 \321\300\1\x81\206\41 386,SM
CMOVcc reg16,mem \320\301\1\x0F\330\x40\110 P6,SM
CMOVcc reg16,reg16 \320\301\1\x0F\330\x40\110 P6
CMOVcc reg32,mem \320\301\1\x0F\330\x40\110 P6,SM
CMOVcc reg32,reg32 \320\301\1\x0F\330\x40\110 P6
CMOVcc reg32,mem \321\301\1\x0F\330\x40\110 P6,SM
CMOVcc reg32,reg32 \321\301\1\x0F\330\x40\110 P6
Jcc imm|near \322\1\x0F\330\x80\64 386
Jcc imm \330\x70\50 8086
Jcc imm|short \330\x70\50 8086

View file

@ -56,6 +56,7 @@ struct itemplate {
#define IF_486 0x0400 /* 486+ instruction */
#define IF_PENT 0x0500 /* Pentium instruction */
#define IF_P6 0x0600 /* P6 instruction */
#define IF_CYRIX 0x0800 /* Cyrix-specific instruction */
#define IF_PMASK 0x0F00 /* the mask for processor types */
#define IF_PRIV 0x1000 /* it's a privileged instruction */
#define IF_UNDOC 0x2000 /* it's an undocumented instruction */

View file

@ -9,7 +9,8 @@
print STDERR "Reading insns.dat...\n";
open (F, "insns.dat") || die "unable to open insns.dat";
$fname = "insns.dat" unless $fname = $ARGV[0];
open (F, $fname) || die "unable to open $fname";
$line = 0;
$opcodes = 0;

View file

@ -29,15 +29,20 @@
#define PERMTS_SIZE 4096 /* size of text blocks */
/* values for label.defn.is_global */
#define DEFINED_BIT 1
#define GLOBAL_BIT 2
#define EXTERN_BIT 4
#define NOT_DEFINED_YET 0
#define LOCAL_SYMBOL 1
#define GLOBAL_SYMBOL 2
#define GLOBAL_PLACEHOLDER 3
#define TYPE_MASK 3
#define LOCAL_SYMBOL (DEFINED_BIT)
#define GLOBAL_PLACEHOLDER (GLOBAL_BIT)
#define GLOBAL_SYMBOL (DEFINED_BIT|GLOBAL_BIT)
union label { /* actual label structures */
struct {
long segment, offset;
char *label;
char *label, *special;
int is_global;
} defn;
struct {
@ -62,6 +67,8 @@ static char *perm_copy (char *string1, char *string2);
static char *prevlabel;
static int initialised = FALSE;
/*
* Internal routine: finds the `union label' corresponding to the
* given label name. Creates a new one, if it isn't found, and if
@ -107,6 +114,7 @@ static union label *find_label (char *label, int create) {
lfree[hash]->admin.movingon = BOGUS_VALUE;
lfree[hash]->defn.label = perm_copy (prev, label);
lfree[hash]->defn.special = NULL;
lfree[hash]->defn.is_global = NOT_DEFINED_YET;
return lfree[hash]++;
} else
@ -116,9 +124,11 @@ static union label *find_label (char *label, int create) {
int lookup_label (char *label, long *segment, long *offset) {
union label *lptr;
if (!initialised)
return 0;
lptr = find_label (label, 0);
if (lptr && (lptr->defn.is_global == LOCAL_SYMBOL ||
lptr->defn.is_global == GLOBAL_SYMBOL)) {
if (lptr && (lptr->defn.is_global & DEFINED_BIT)) {
*segment = lptr->defn.segment;
*offset = lptr->defn.offset;
return 1;
@ -126,6 +136,19 @@ int lookup_label (char *label, long *segment, long *offset) {
return 0;
}
int is_extern (char *label) {
union label *lptr;
if (!initialised)
return 0;
lptr = find_label (label, 0);
if (lptr && (lptr->defn.is_global & EXTERN_BIT))
return 1;
else
return 0;
}
void define_label_stub (char *label, efunc error) {
union label *lptr;
@ -138,26 +161,22 @@ void define_label_stub (char *label, efunc error) {
}
}
void define_label (char *label, long segment, long offset,
struct ofmt *ofmt, efunc error) {
void define_label (char *label, long segment, long offset, char *special,
int is_norm, int isextrn, struct ofmt *ofmt, efunc error) {
union label *lptr;
lptr = find_label (label, 1);
switch (lptr->defn.is_global) {
case NOT_DEFINED_YET:
lptr->defn.is_global = LOCAL_SYMBOL;
break;
case GLOBAL_PLACEHOLDER:
lptr->defn.is_global = GLOBAL_SYMBOL;
break;
default:
if (lptr->defn.is_global & DEFINED_BIT) {
error(ERR_NONFATAL, "symbol `%s' redefined", label);
return;
}
lptr->defn.is_global |= DEFINED_BIT;
if (isextrn)
lptr->defn.is_global |= EXTERN_BIT;
if (label[0] != '.') /* not local, but not special either */
if (label[0] != '.' && is_norm) /* not local, but not special either */
prevlabel = lptr->defn.label;
else if (label[1] != '.' && !*prevlabel)
else if (label[0] == '.' && label[1] != '.' && !*prevlabel)
error(ERR_NONFATAL, "attempt to define a local label before any"
" non-local labels");
@ -165,25 +184,20 @@ void define_label (char *label, long segment, long offset,
lptr->defn.offset = offset;
ofmt->symdef (lptr->defn.label, segment, offset,
lptr->defn.is_global == GLOBAL_SYMBOL);
!!(lptr->defn.is_global & GLOBAL_BIT),
special ? special : lptr->defn.special);
}
void define_common (char *label, long segment, long size,
void define_common (char *label, long segment, long size, char *special,
struct ofmt *ofmt, efunc error) {
union label *lptr;
lptr = find_label (label, 1);
switch (lptr->defn.is_global) {
case NOT_DEFINED_YET:
lptr->defn.is_global = LOCAL_SYMBOL;
break;
case GLOBAL_PLACEHOLDER:
lptr->defn.is_global = GLOBAL_SYMBOL;
break;
default:
if (lptr->defn.is_global & DEFINED_BIT) {
error(ERR_NONFATAL, "symbol `%s' redefined", label);
return;
}
lptr->defn.is_global |= DEFINED_BIT;
if (label[0] != '.') /* not local, but not special either */
prevlabel = lptr->defn.label;
@ -194,10 +208,11 @@ void define_common (char *label, long segment, long size,
lptr->defn.segment = segment;
lptr->defn.offset = 0;
ofmt->symdef (lptr->defn.label, segment, size, 2);
ofmt->symdef (lptr->defn.label, segment, size, 2,
special ? special : lptr->defn.special);
}
void declare_as_global (char *label, efunc error) {
void declare_as_global (char *label, char *special, efunc error) {
union label *lptr;
if (islocal(label)) {
@ -206,16 +221,18 @@ void declare_as_global (char *label, efunc error) {
return;
}
lptr = find_label (label, 1);
switch (lptr->defn.is_global) {
switch (lptr->defn.is_global & TYPE_MASK) {
case NOT_DEFINED_YET:
lptr->defn.is_global = GLOBAL_PLACEHOLDER;
lptr->defn.special = special ? perm_copy(special, "") : NULL;
break;
case GLOBAL_PLACEHOLDER: /* already done: silently ignore */
case GLOBAL_SYMBOL:
break;
case LOCAL_SYMBOL:
error(ERR_NONFATAL, "symbol `%s': [GLOBAL] directive must"
" appear before symbol definition", label);
if (!lptr->defn.is_global & EXTERN_BIT)
error(ERR_NONFATAL, "symbol `%s': GLOBAL directive must"
" appear before symbol definition", label);
break;
}
}
@ -241,12 +258,16 @@ int init_labels (void) {
prevlabel = "";
initialised = TRUE;
return 0;
}
void cleanup_labels (void) {
int i;
initialised = FALSE;
for (i=0; i<LABEL_HASHES; i++) {
union label *lptr, *lhold;

View file

@ -7,11 +7,12 @@
*/
int lookup_label (char *label, long *segment, long *offset);
void define_label (char *label, long segment, long offset,
struct ofmt *ofmt, efunc error);
void define_common (char *label, long segment, long size,
int is_extern (char *label);
void define_label (char *label, long segment, long offset, char *special,
int is_norm, int isextrn, struct ofmt *ofmt, efunc error);
void define_common (char *label, long segment, long size, char *special,
struct ofmt *ofmt, efunc error);
void define_label_stub (char *label, efunc error);
void declare_as_global (char *label, efunc error);
void declare_as_global (char *label, char *special, efunc error);
int init_labels (void);
void cleanup_labels (void);

175
macros.bas Normal file
View file

@ -0,0 +1,175 @@
' INFO_1: Converter for STANDARD.MAC to MACRO.C
'
' INFO_2: Written by Mark Junker in 1997
' InterNet: mjs@prg.hannover.sgh-net.de
' FIDO: Mark Junker@2:2437/47.21
'
' COMMENT: To start the program press SHIFT+F5 within the QBasic IDE
' or start it from the command-line with QBASIC /RUN MACROS
'
DEFINT A-Z
DECLARE FUNCTION StrTrimLeft$ (a$, b$)
DECLARE FUNCTION StrTrimRight$ (a$, b$)
DECLARE FUNCTION StrTrim$ (a$, b$)
DECLARE SUB StrSplitString (SplitString$, SplitChars$, SplitField$(), SplitCount%)
DECLARE FUNCTION Min% (a%, b%)
DECLARE FUNCTION StrInstrLeft% (SearchStart%, SearchIn$, SearchFor$)
DECLARE FUNCTION StrAscii% (a$)
CLS
DIM LineData$(1 TO 2)
OPEN "I", 1, "STANDARD.MAC"
OPEN "O", 2, "macros.c"
PRINT #2, "/* This file auto-generated from standard.mac by macros.bas - don't edit it */"
PRINT #2, ""
PRINT #2, "static char *stdmac[] = {"
WHILE NOT EOF(1)
LINE INPUT #1, l$
CALL StrSplitString(l$, ";", LineData$(), SplitCount)
IF SplitCount THEN
LineData$(1) = StrTrim$(LineData$(1), CHR$(9) + " ")
IF LEN(LineData$(1)) THEN
PRINT #2, " " + CHR$(34) + LineData$(1) + CHR$(34) + ","
END IF
END IF
WEND
PRINT #2, " NULL"
PRINT #2, "};"
CLOSE 2
CLOSE 1
SYSTEM
FUNCTION Min% (a%, b%)
IF a% < b% THEN Min% = a% ELSE Min% = b%
END FUNCTION
FUNCTION StrAscii (a$)
IF LEN(a$) = 0 THEN
StrAscii = -1
ELSE
StrAscii = ASC(a$)
END IF
END FUNCTION
' same as =INSTR(SearchStart, SearchIn, ANY SearchFor$) in PowerBASIC(tm)
'
FUNCTION StrInstrLeft (SearchStart, SearchIn$, SearchFor$)
ValuesCount = LEN(SearchFor$)
MaxValue = LEN(SearchIn$) + 1
MinValue = MaxValue
FOR Counter1 = 1 TO ValuesCount
SearchChar$ = MID$(SearchFor$, Counter1, 1)
hVal2 = INSTR(SearchStart, SearchIn$, SearchChar$)
IF hVal2 > 0 THEN MinValue = Min%(hVal2, MinValue)
NEXT
IF MinValue = MaxValue THEN MinValue = 0
StrInstrLeft = MinValue
END FUNCTION
'
' This is a very damn fuckin' shit version of this splitting routine.
' At this time, it's not very useful :]
'
SUB StrSplitString (SplitString$, SplitChars$, SplitField$(), SplitCount)
StartIndex = LBOUND(SplitField$)
LastIndex = UBOUND(SplitField$)
ActualIndex& = StartIndex
SplitCount = 0
LastPos = 1
FoundPos = StrInstrLeft(LastPos, SplitString$, SplitChars$ + CHR$(34))
GetDirect = 0
EndLoop = 0
TempString$ = ""
DO WHILE FoundPos > 0
FoundCharVal = StrAscii(MID$(SplitString$, FoundPos, 1))
PosDiff = (FoundPos - LastPos) + 1
SELECT CASE FoundCharVal
CASE 34
TempString$ = TempString$ + MID$(SplitString$, LastPos, PosDiff - 1)
SELECT CASE EndLoop
CASE 0
EndLoop = 2
CASE 3
EndLoop = 0
END SELECT
CASE ELSE
TempString$ = TempString$ + MID$(SplitString$, LastPos, PosDiff - 1)
SplitField$(ActualIndex&) = TempString$
TempString$ = ""
ActualIndex& = ActualIndex& + 1
IF ActualIndex& > LastIndex THEN
ActualIndex& = LastIndex
EndLoop = 1
END IF
END SELECT
SELECT CASE EndLoop
CASE 0
DO
LastPos = FoundPos + 1
FoundPos = StrInstrLeft(LastPos, SplitString$, SplitChars$)
LOOP WHILE LastPos = FoundPos
FoundPos = StrInstrLeft(LastPos, SplitString$, SplitChars$ + CHR$(34))
CASE 1
FoundPos = 0
LastPos = LEN(SplitString$) + 1
CASE 2
EndLoop = 3
LastPos = FoundPos + 1
FoundPos = StrInstrLeft(LastPos, SplitString$, CHR$(34))
IF FoundPos = 0 THEN
SplitString$ = SplitString$ + CHR$(34)
FoundPos = LEN(SplitString$)
END IF
END SELECT
LOOP
IF EndLoop = 0 THEN
IF LEN(TempString$) > 0 THEN
SplitField$(ActualIndex&) = TempString$
ELSEIF LastPos <= LEN(SplitString$) THEN
SplitField$(ActualIndex&) = MID$(SplitString$, LastPos)
ELSE
ActualIndex& = ActualIndex& - 1
END IF
END IF
FOR a = ActualIndex& + 1 TO LastIndex
SplitField$(a) = ""
NEXT
SplitCount = (ActualIndex& - StartIndex) + 1
END SUB
FUNCTION StrTrim$ (a$, b$)
StrTrim$ = StrTrimRight$(StrTrimLeft$(a$, b$), b$)
END FUNCTION
FUNCTION StrTrimLeft$ (a$, b$) 'public
p = 0
l = LEN(a$)
DO
p = p + 1
t$ = MID$(a$, p, 1)
LOOP WHILE (p < l) AND (INSTR(b$, t$) > 0)
StrTrimLeft$ = MID$(a$, p)
END FUNCTION
FUNCTION StrTrimRight$ (a$, b$) 'public
l = LEN(a$)
p = l + 1
DO
p = p - 1
IF p > 0 THEN
t$ = MID$(a$, p, 1)
ELSE
t$ = ""
END IF
LOOP WHILE (p > 0) AND (INSTR(b$, t$) > 0)
StrTrimRight$ = LEFT$(a$, p)
END FUNCTION

View file

@ -2,7 +2,9 @@
static char *stdmac[] = {
"%define __NASM_MAJOR__ 0",
"%define __NASM_MINOR__ 95",
"%define __NASM_MINOR__ 96",
"%define __FILE__",
"%define __LINE__",
"%define __SECT__",
"%imacro section 1+.nolist",
"%define __SECT__ [section %1]",
@ -20,6 +22,7 @@ static char *stdmac[] = {
"%push struc",
"%define %$strucname %1",
"[absolute 0]",
"%$strucname:",
"%endmacro",
"%imacro endstruc 0.nolist",
"%{$strucname}_size:",
@ -39,29 +42,32 @@ static char *stdmac[] = {
"times %{$strucname}_size-($-%$strucstart) db 0",
"%pop",
"%endmacro",
"%imacro extern 1+.nolist",
"%imacro align 1-2+.nolist nop",
"times ($$-$) & ((%1)-1) %2",
"%endmacro",
"%imacro alignb 1-2+.nolist resb 1",
"times ($$-$) & ((%1)-1) %2",
"%endmacro",
"%imacro extern 1-*.nolist",
"%rep %0",
"[extern %1]",
"%rotate 1",
"%endrep",
"%endmacro",
"%imacro bits 1+.nolist",
"[bits %1]",
"%endmacro",
"%imacro global 1+.nolist",
"%imacro global 1-*.nolist",
"%rep %0",
"[global %1]",
"%rotate 1",
"%endrep",
"%endmacro",
"%imacro common 1+.nolist",
"%imacro common 1-*.nolist",
"%rep %0",
"[common %1]",
"%endmacro",
"%imacro org 1+.nolist",
"[org %1]",
"%endmacro",
"%imacro group 1+.nolist",
"[group %1]",
"%endmacro",
"%imacro uppercase 1+.nolist",
"[uppercase %1]",
"%endmacro",
"%imacro library 1+.nolist",
"[library %1]",
"%rotate 1",
"%endrep",
"%endmacro",
NULL
};

View file

@ -7,7 +7,8 @@
# redistributable under the licence given in the file "Licence"
# distributed in the NASM archive.
open INPUT,"standard.mac" || die "unable to open standard.mac\n";
$fname = "standard.mac" unless $fname = $ARGV[0];
open INPUT,$fname || die "unable to open $fname\n";
open OUTPUT,">macros.c" || die "unable to open macros.c\n";
print OUTPUT "/* This file auto-generated from standard.mac by macros.pl" .

37
misc/c16.mac Normal file
View file

@ -0,0 +1,37 @@
; NASM macro set to make interfacing to 16-bit programs easier -*- nasm -*-
%imacro proc 1 ; begin a procedure definition
%push proc
global %1
%1: push bp
mov bp,sp
%ifdef FARCODE PASCAL ; arguments may start at bp+4 or bp+6
%assign %$arg 6
%else
%assign %$arg 4
%endif
%define %$procname %1
%endmacro
%imacro arg 0-1 2 ; used with the argument name as a label
equ %$arg
%assign %$arg %1+%$arg
%endmacro
%imacro endproc 0
%ifnctx proc
%error Mismatched `endproc'/`proc'
%else
mov sp,bp
pop bp
%ifdef PASCAL
retf %$arg
%elifdef FARCODE
retf
%else
retn
%endif
__end_%$procname: ; useful for calculating function size
%pop
%endif
%endmacro

26
misc/c32.mac Normal file
View file

@ -0,0 +1,26 @@
; NASM macro set to make interfacing to 32-bit programs easier -*- nasm -*-
%imacro proc 1 ; begin a procedure definition
%push proc
global %1
%1: push ebp
mov ebp,esp
%assign %$arg 8
%define %$procname %1
%endmacro
%imacro arg 0-1 4 ; used with the argument name as a label
equ %$arg
%assign %$arg %1+%$arg
%endmacro
%imacro endproc 0
%ifnctx proc
%error Mismatched `endproc'/`proc'
%else
leave
ret
__end_%$procname: ; useful for calculating function size
%pop
%endif
%endmacro

BIN
misc/exasm.zip Normal file

Binary file not shown.

57
misc/exebin.mac Normal file
View file

@ -0,0 +1,57 @@
; -*- nasm -*-
; NASM macro file to allow the `bin' output format to generate
; simple .EXE files by constructing the EXE header by hand.
; Adapted from a contribution by Yann Guidon <whygee_corp@hol.fr>
%define EXE_stack_size EXE_realstacksize
%macro EXE_begin 0
ORG 0E0h
section .text
header_start:
db 4Dh,5Ah ; EXE file signature
dw EXE_allocsize % 512
dw (EXE_allocsize + 511) / 512
dw 0 ; relocation information: none
dw (header_end-header_start)/16 ; header size in paragraphs
dw (EXE_absssize + EXE_realstacksize) / 16 ; min extra mem
dw (EXE_absssize + EXE_realstacksize) / 16 ; max extra mem
dw -10h ; Initial SS (before fixup)
dw EXE_endbss + EXE_realstacksize ; Initial SP (1K DPMI+1K STACK)
dw 0 ; (no) Checksum
dw 100h ; Initial IP - start just after the header
dw -10h ; Initial CS (before fixup)
dw 0 ; file offset to relocation table: none
dw 0 ; (no overlay)
align 16,db 0
header_end:
EXE_startcode:
section .data
EXE_startdata:
section .bss
EXE_startbss:
%endmacro
%macro EXE_stack 1
EXE_realstacksize equ %1
%define EXE_stack_size EXE_bogusstacksize ; defeat EQU in EXE_end
%endmacro
%macro EXE_end 0
section .text
EXE_endcode:
section .data
EXE_enddata:
section .bss
alignb 4
EXE_endbss:
EXE_acodesize equ (EXE_endcode-EXE_startcode+3) & (~3)
EXE_datasize equ EXE_enddata-EXE_startdata
EXE_absssize equ (EXE_endbss-EXE_startbss+3) & (~3)
EXE_allocsize equ EXE_acodesize + EXE_datasize
EXE_stack_size equ 0x800 ; default if nothing else was used
%endmacro

View file

@ -294,6 +294,14 @@ define_keywords_n($1, nasm_kw_8, 8, 0);
define_keywords_n($1, nasm_kw_9, 9, 0);
define_keywords_n($1, nasm_kw_10, 10, 0);
define_keywords_n($1, "org", 3, 1);
define_keywords_n($1, "bitsiend", 4, 1);
define_keywords_n($1, "aligngroupstruc", 5, 1);
define_keywords_n($1, "alignbcommonexternglobalistruc", 6, 1);
define_keywords_n($1, "sectionsegmentlibrary", 7, 1);
define_keywords_n($1, "absoluteendstruc", 8, 1);
define_keywords_n($1, "uppercase", 9, 1);
!if (keymap_p ($1)) make_keymap ($1);
definekey("nasm_bol_self_ins", ";", $1);
definekey("nasm_bol_self_ins", "#", $1);

68
names.c
View file

@ -8,7 +8,7 @@
*/
static char *reg_names[] = { /* register names, as strings */
"\0", "ah", "al", "ax", "bh", "bl", "bp", "bx", "ch", "cl",
"ah", "al", "ax", "bh", "bl", "bp", "bx", "ch", "cl",
"cr0", "cr2", "cr3", "cr4", "cs", "cx", "dh", "di", "dl", "dr0",
"dr1", "dr2", "dr3", "dr6", "dr7", "ds", "dx", "eax", "ebp",
"ebx", "ecx", "edi", "edx", "es", "esi", "esp", "fs", "gs",
@ -32,37 +32,41 @@ static char *insn_names[] = { /* instruction names, as strings */
"fidivr", "fild", "fimul", "fincstp", "finit", "fist", "fistp",
"fisub", "fisubr", "fld", "fld1", "fldcw", "fldenv", "fldl2e",
"fldl2t", "fldlg2", "fldln2", "fldpi", "fldz", "fmul", "fmulp",
"fnop", "fpatan", "fprem", "fprem1", "fptan", "frndint",
"frstor", "fsave", "fscale", "fsetpm", "fsin", "fsincos",
"fsqrt", "fst", "fstcw", "fstenv", "fstp", "fstsw", "fsub",
"fsubp", "fsubr", "fsubrp", "ftst", "fucom", "fucomi",
"fucomip", "fucomp", "fucompp", "fxam", "fxch", "fxtract",
"fyl2x", "fyl2xp1", "hlt", "ibts", "icebp", "idiv", "imul",
"in", "inc", "incbin", "insb", "insd", "insw", "int", "int1",
"int01", "int3", "into", "invd", "invlpg", "iret", "iretd",
"iretw", "jcxz", "jecxz", "jmp", "lahf", "lar", "lds", "lea",
"leave", "les", "lfs", "lgdt", "lgs", "lidt", "lldt", "lmsw",
"loadall", "loadall286", "lodsb", "lodsd", "lodsw", "loop",
"loope", "loopne", "loopnz", "loopz", "lsl", "lss", "ltr",
"mov", "movd", "movq", "movsb", "movsd", "movsw", "movsx",
"movzx", "mul", "neg", "nop", "not", "or", "out", "outsb",
"outsd", "outsw", "packssdw", "packsswb", "packuswb", "paddb",
"paddd", "paddsb", "paddsw", "paddusb", "paddusw", "paddw",
"pand", "pandn", "pcmpeqb", "pcmpeqd", "pcmpeqw", "pcmpgtb",
"pcmpgtd", "pcmpgtw", "pmaddwd", "pmulhw", "pmullw", "pop",
"popa", "popad", "popaw", "popf", "popfd", "popfw", "por",
"pslld", "psllq", "psllw", "psrad", "psraw", "psrld", "psrlq",
"psrlw", "psubb", "psubd", "psubsb", "psubsw", "psubusb",
"psubusw", "psubw", "punpckhbw", "punpckhdq", "punpckhwd",
"punpcklbw", "punpckldq", "punpcklwd", "push", "pusha",
"pushad", "pushaw", "pushf", "pushfd", "pushfw", "pxor", "rcl",
"rcr", "rdmsr", "rdpmc", "rdtsc", "resb", "resd", "resq",
"rest", "resw", "ret", "retf", "retn", "rol", "ror", "rsm",
"sahf", "sal", "salc", "sar", "sbb", "scasb", "scasd", "scasw",
"sgdt", "shl", "shld", "shr", "shrd", "sidt", "sldt", "smi",
"smsw", "stc", "std", "sti", "stosb", "stosd", "stosw", "str",
"sub", "test", "umov", "verr", "verw", "wait", "wbinvd",
"wrmsr", "xadd", "xbts", "xchg", "xlatb", "xor"
"fnclex", "fndisi", "fneni", "fninit", "fnop", "fnsave",
"fnstcw", "fnstenv", "fnstsw", "fpatan", "fprem", "fprem1",
"fptan", "frndint", "frstor", "fsave", "fscale", "fsetpm",
"fsin", "fsincos", "fsqrt", "fst", "fstcw", "fstenv", "fstp",
"fstsw", "fsub", "fsubp", "fsubr", "fsubrp", "ftst", "fucom",
"fucomi", "fucomip", "fucomp", "fucompp", "fxam", "fxch",
"fxtract", "fyl2x", "fyl2xp1", "hlt", "ibts", "icebp", "idiv",
"imul", "in", "inc", "incbin", "insb", "insd", "insw", "int",
"int1", "int01", "int3", "into", "invd", "invlpg", "iret",
"iretd", "iretw", "jcxz", "jecxz", "jmp", "lahf", "lar", "lds",
"lea", "leave", "les", "lfs", "lgdt", "lgs", "lidt", "lldt",
"lmsw", "loadall", "loadall286", "lodsb", "lodsd", "lodsw",
"loop", "loope", "loopne", "loopnz", "loopz", "lsl", "lss",
"ltr", "mov", "movd", "movq", "movsb", "movsd", "movsw",
"movsx", "movzx", "mul", "neg", "nop", "not", "or", "out",
"outsb", "outsd", "outsw", "packssdw", "packsswb", "packuswb",
"paddb", "paddd", "paddsb", "paddsiw", "paddsw", "paddusb",
"paddusw", "paddw", "pand", "pandn", "paveb", "pcmpeqb",
"pcmpeqd", "pcmpeqw", "pcmpgtb", "pcmpgtd", "pcmpgtw",
"pdistib", "pmachriw", "pmaddwd", "pmagw", "pmulhrw",
"pmulhriw", "pmulhw", "pmullw", "pmvgezb", "pmvlzb", "pmvnzb",
"pmvzb", "pop", "popa", "popad", "popaw", "popf", "popfd",
"popfw", "por", "pslld", "psllq", "psllw", "psrad", "psraw",
"psrld", "psrlq", "psrlw", "psubb", "psubd", "psubsb",
"psubsiw", "psubsw", "psubusb", "psubusw", "psubw", "punpckhbw",
"punpckhdq", "punpckhwd", "punpcklbw", "punpckldq", "punpcklwd",
"push", "pusha", "pushad", "pushaw", "pushf", "pushfd",
"pushfw", "pxor", "rcl", "rcr", "rdmsr", "rdpmc", "rdtsc",
"resb", "resd", "resq", "rest", "resw", "ret", "retf", "retn",
"rol", "ror", "rsm", "sahf", "sal", "salc", "sar", "sbb",
"scasb", "scasd", "scasw", "sgdt", "shl", "shld", "shr", "shrd",
"sidt", "sldt", "smi", "smsw", "stc", "std", "sti", "stosb",
"stosd", "stosw", "str", "sub", "test", "umov", "verr", "verw",
"wait", "wbinvd", "wrmsr", "xadd", "xbts", "xchg", "xlatb",
"xor"
};
static char *icn[] = { /* conditional instructions */

423
nasm.1 Normal file
View file

@ -0,0 +1,423 @@
.TH NASM 1 "The Netwide Assembler Project"
.SH NAME
nasm \- the Netwide Assembler \- portable 80x86 assembler
.SH SYNOPSIS
.B nasm
[
.B \-f
format
] [
.B \-o
outfile
] [
.IR options ...
] infile
.br
.B nasm \-h
.br
.B nasm \-r
.SH DESCRIPTION
The
.B nasm
command assembles the file
.I infile
and directs output to the file
.I outfile
if specified. If
.I outfile
is not specified,
.B nasm
will derive a default output file name from the name of its input
file, usually by appending `.o' or `.obj', or by removing all
extensions for a raw binary file. Failing that, the output file name
will be `nasm.out'.
.SS OPTIONS
.TP
.B \-h
Causes
.B nasm
to exit immediately, after giving a summary of its invocation
options, and listing all its supported output file formats.
.TP
.B \-a
Causes
.B nasm
to assemble the given input file without first applying the macro
preprocessor.
.TP
.B \-e
Causes
.B nasm
to preprocess the given input file, and write the output to
.I stdout
(or the specified output file name), and not actually assemble
anything.
.TP
.BI \-r
Causes
.B nasm
to exit immediately, after displaying its version number.
.TP
.BI \-f " format"
Specifies the output file format. Formats include
.IR bin ,
to produce flat-form binary files, and
.I aout
and
.I elf
to produce Linux a.out and ELF object files, respectively.
.TP
.BI \-o " outfile"
Specifies a precise name for the output file, overriding
.BR nasm 's
default means of determining it.
.TP
.BI \-l " listfile"
Causes an assembly listing to be directed to the given file, in
which the original source is displayed on the right hand side (plus
the source for included files and the expansions of multi-line
macros) and the generated code is shown in hex on the left.
.TP
.B \-s
Causes
.B nasm
to send its error messages and/or help text to
.I stdout
instead of
.IR stderr .
.TP
.BI \-w [+-]foo
Causes
.B nasm
to enable or disable certain classes of warning messages, for
example
.B \-w+orphan-labels
or
.B \-w-macro-params
to, respectively, enable warnings about labels alone on lines or
disable warnings about incorrect numbers of parameters in macro
calls.
.TP
.BI \-i " directory"
Adds a directory to the search path for include files. The directory
specification must include the trailing slash, as it will be
directly prepended to the name of the include file.
.TP
.BI \-p " file"
Specifies a file to be pre-included, before the main source file
starts to be processed.
.TP
.BI \-d " macro[=value]"
Pre-defines a single-line macro.
.PP
.RE
.SS SYNTAX
This man page does not fully describe the syntax of
.BR nasm 's
assembly language, but does give a summary of the differences from
other assemblers.
.PP
.I Registers
have no leading `%' sign, unlike
.BR gas ,
and floating-point stack registers are referred to as
.IR st0 ,
.IR st1 ,
and so on.
.PP
.I Floating-point instructions
may use either the single-operand form or the double. A
.I TO
keyword is provided; thus, one could either write
.PP
.ti +15n
fadd st0,st1
.br
.ti +15n
fadd st1,st0
.PP
or one could use the alternative single-operand forms
.PP
.ti +15n
fadd st1
.br
.ti +15n
fadd to st1
.PP
.I Uninitialised storage
is reserved using the
.IR RESB ,
.IR RESW ,
.IR RESD ,
.I RESQ
and
.I REST
pseudo-opcodes, each taking one parameter which gives the number of
bytes, words, doublewords, quadwords or ten-byte words to reserve.
.PP
.I Repetition
of data items is not done by the
.I DUP
keyword as seen in DOS assemblers, but by the use of the
.I TIMES
prefix, like this:
.PP
.ti +6n
.ta 9n
message: times 3 db 'abc'
.br
.ti +15n
times 64-$+message db 0
.PP
which defines the string `abcabcabc', followed by the right number
of zero bytes to make the total length up to 64 bytes.
.PP
.I Symbol references
are always understood to be immediate (i.e. the address of the
symbol), unless square brackets are used, in which case the contents
of the memory location are used. Thus:
.PP
.ti +15n
mov ax,wordvar
.PP
loads AX with the address of the variable `wordvar', whereas
.PP
.ti +15n
mov ax,[wordvar]
.br
.ti +15n
mov ax,[wordvar+1]
.br
.ti +15n
mov ax,[es:wordvar+bx]
.PP
all refer to the
.I contents
of memory locations. The syntaxes
.PP
.ti +15n
mov ax,es:wordvar[bx]
.br
.ti +15n
es mov ax,wordvar[1]
.PP
are not legal at all, although the use of a segment register name as
an instruction prefix is valid, and can be used with instructions
such as
.I LODSB
which can't be overridden any other way.
.PP
.I Constants
may be expressed numerically in most formats: a trailing H, Q or B
denotes hex, octal or binary respectively, and a leading `0x' or `$'
denotes hex as well. Leading zeros are not treated specially at all.
Character constants may be enclosed in single or double quotes;
there is no escape character. The ordering is little-endian
(reversed), so that the character constant
.I 'abcd'
denotes 0x64636261 and not 0x61626364.
.PP
.I Local labels
begin with a period, and their `locality' is granted by the
assembler prepending the name of the previous non-local symbol. Thus
declaring a label `.loop' after a label `label' has actually defined
a symbol called `label.loop'.
.SS DIRECTIVES
.I SECTION name
or
.I SEGMENT name
causes
.B nasm
to direct all following code to the named section. Section names
vary with output file format, although most formats support the
names
.IR .text ,
.I .data
and
.IR .bss .
(The exception is the
.I obj
format, in which all segments are user-definable.)
.PP
.I ABSOLUTE address
causes
.B nasm
to position its notional assembly point at an absolute address: so
no code or data may be generated, but you can use
.IR RESB ,
.I RESW
and
.I RESD
to move the assembly point further on, and you can define labels. So
this directive may be used to define data structures. When you have
finished doing absolute assembly, you must issue another
.I SECTION
directive to return to normal assembly.
.PP
.I BITS 16
or
.I BITS 32
switches the default processor mode for which
.B nasm
is generating code: it is equivalent to
.I USE16
or
.I USE32
in DOS assemblers.
.PP
.I EXTERN symbol
and
.I GLOBAL symbol
import and export symbol definitions, respectively, from and to
other modules. Note that the
.I GLOBAL
directive must appear before the definition of the symbol it refers
to.
.PP
.I STRUC strucname
and
.IR ENDSTRUC ,
when used to bracket a number of
.IR RESB ,
.I RESW
or similar instructions, define a data structure. In addition to
defining the offsets of the structure members, the construct also
defines a symbol for the size of the structure, which is simply the
structure name with
.I _size
tacked on to the end.
.SS FORMAT-SPECIFIC DIRECTIVES
.I ORG address
is used by the
.I bin
flat-form binary output format, and specifies the address at which
the output code will eventually be loaded.
.PP
.I GROUP grpname seg1 seg2...
is used by the
.I obj
(Microsoft 16-bit) output format, and defines segment groups. This
format also uses
.IR UPPERCASE ,
which directs that all segment, group and symbol names output to the
object file should be in uppercase. Note that the actual assembly is
still case sensitive.
.PP
.I LIBRARY libname
is used by the
.I rdf
output format, and causes a dependency record to be written to the
output file which indicates that the program requires a certain
library in order to run.
.SS MACRO PREPROCESSOR
Single-line macros are defined using the
.I %define
or
.I %idefine
commands, in a similar fashion to the C preprocessor. They can be
overloaded with respect to number of parameters, although defining a
macro with no parameters prevents the definition of any macro with
the same name taking parameters, and vice versa.
.I %define
defines macros whose names match case-sensitively, whereas
.I %idefine
defines case-insensitive macros.
.PP
Multi-line macros are defined using
.I %macro
and
.I %imacro
(the distinction is the same as that between
.I %define
and
.IR %idefine ),
whose syntax is as follows:
.PP
.ti +6n
%macro
.I name
.IR minprm [- maxprm "][+][.nolist] [" defaults ]
.br
.ti +15n
<some lines of macro expansion text>
.br
.ti +6n
%endmacro
.PP
Again, these macros may be overloaded. The trailing plus sign
indicates that any parameters after the last one get subsumed, with
their separating commas, into the last parameter. The
.I defaults
part can be used to specify defaults for unspecified macro
parameters after
.IR minparam .
.I %endm
is a valid synonym for
.IR %endmacro .
.PP
To refer to the macro parameters within a macro expansion, you use
.IR %1 ,
.I %2
and so on. You can also enforce that a macro parameter should
contain a condition code by using
.IR %+1 ,
and you can invert the condition code by using
.IR %-1 .
You can also define a label specific to a macro invocation by
prefixing it with a double % sign.
.PP
Files can be included using the
.I %include
directive, which works like C.
.PP
The preprocessor has a `context stack', which may be used by one
macro to store information that a later one will retrieve. You can
push a context on the stack using
.IR %push ,
remove one using
.IR %pop ,
and change the name of the top context (without disturbing any
associated definitions) using
.IR %repl .
Labels and
.I %define
macros specific to the top context may be defined by prefixing their
names with %$, and things specific to the next context down with
%$$, and so on.
.PP
Conditional assembly is done by means of
.IR %ifdef ,
.IR %ifndef ,
.I %else
and
.I %endif
as in C. (Except that
.I %ifdef
can accept several putative macro names, and will evaluate TRUE if
any of them is defined.) In addition, the directives
.I %ifctx
and
.I %ifnctx
can be used to condition on the name of the top context on the
context stack. The obvious set of `else-if' directives,
.IR %elifdef ,
.IR %elifndef ,
.IR %elifctx
and
.IR %elifnctx
are also supported.
.SH BUGS
There is a reported seg-fault on some (Linux) systems with some
large source files. This appears to be very hard to reproduce. All
other
.I known
bugs have been fixed...
.SH RESTRICTIONS
There is no support for listing files, symbol maps, or debugging
object-file records. The advanced features of the ELF and Win32
object file formats are not supported, and there is no means for
warning the programmer against using an instruction beyond the
capability of the target processor.
.SH SEE ALSO
.BR as "(" 1 "),"
.BR ld "(" 1 ")."

251
nasm.c
View file

@ -16,6 +16,7 @@
#include "nasmlib.h"
#include "preproc.h"
#include "parser.h"
#include "eval.h"
#include "assemble.h"
#include "labels.h"
#include "outform.h"
@ -32,7 +33,6 @@ static char *obuf;
static char inname[FILENAME_MAX];
static char outname[FILENAME_MAX];
static char listname[FILENAME_MAX];
static char realout[FILENAME_MAX];
static int lineno; /* for error reporting */
static int lineinc; /* set by [LINE] or [ONELINE] */
static int globallineno; /* for forward-reference tracking */
@ -44,7 +44,7 @@ static int sb = 16; /* by default */
static int use_stdout = FALSE; /* by default, errors to stderr */
static long current_seg;
static long current_seg, abs_seg;
static struct RAA *offsets;
static long abs_offset;
@ -62,7 +62,7 @@ static char currentfile[FILENAME_MAX];
* doesn't do anything. Initial defaults are given here.
*/
static char suppressed[1+ERR_WARN_MAX] = {
0, FALSE, TRUE
0, FALSE, TRUE, FALSE
};
/*
@ -70,7 +70,7 @@ static char suppressed[1+ERR_WARN_MAX] = {
* zero does nothing.
*/
static char *suppressed_names[1+ERR_WARN_MAX] = {
NULL, "macro-params", "orphan-labels"
NULL, "macro-params", "orphan-labels", "number-overflow"
};
/*
@ -79,7 +79,8 @@ static char *suppressed_names[1+ERR_WARN_MAX] = {
*/
static char *suppressed_what[1+ERR_WARN_MAX] = {
NULL, "macro calls with wrong no. of params",
"labels alone on lines without trailing `:'"
"labels alone on lines without trailing `:'",
"numeric constants greater than 0xFFFFFFFF"
};
/*
@ -88,7 +89,7 @@ static char *suppressed_what[1+ERR_WARN_MAX] = {
* not preprocess their source file.
*/
static void no_pp_reset (char *, efunc, ListGen *);
static void no_pp_reset (char *, int, efunc, evalfunc, ListGen *);
static char *no_pp_getline (void);
static void no_pp_cleanup (void);
static Preproc no_pp = {
@ -130,6 +131,10 @@ int main(int argc, char **argv) {
return 1;
}
if (ofmt->stdmac)
pp_extra_stdmac (ofmt->stdmac);
eval_global_info (ofmt, lookup_label);
if (preprocess_only) {
char *line;
@ -140,12 +145,29 @@ int main(int argc, char **argv) {
"unable to open output file `%s'", outname);
} else
ofile = NULL;
preproc->reset (inname, report_error, &nasmlist);
eval_info ("%", 0L, 0L); /* disallow labels, $ or $$ in exprs */
preproc->reset (inname, 2, report_error, evaluate, &nasmlist);
strcpy(currentfile,inname);
lineno = 0;
lineinc = 1;
while ( (line = preproc->getline()) ) {
int ln, li;
char buf[FILENAME_MAX];
lineno += lineinc;
/*
* We must still check for %line directives, so that we
* can report errors accurately.
*/
if (!strncmp(line, "%line", 5) &&
sscanf(line, "%%line %d+%d %s", &ln, &li, buf) == 3) {
lineno = ln - li;
lineinc = li;
strncpy (currentfile, buf, FILENAME_MAX-1);
currentfile[FILENAME_MAX-1] = '\0';
}
if (ofile) {
fputs(line, ofile);
fputc('\n', ofile);
@ -166,10 +188,7 @@ int main(int argc, char **argv) {
* the name of the input file and then put that inside the
* file.
*/
ofmt->filename (inname, realout, report_error);
if (!*outname) {
strcpy(outname, realout);
}
ofmt->filename (inname, outname, report_error);
ofile = fopen(outname, "wb");
if (!ofile) {
@ -182,7 +201,7 @@ int main(int argc, char **argv) {
* init routines. (eg OS/2 defines the FLAT group)
*/
init_labels ();
ofmt->init (ofile, report_error, define_label);
ofmt->init (ofile, report_error, define_label, evaluate);
assemble_file (inname);
if (!terminate_after_phase) {
ofmt->cleanup ();
@ -377,19 +396,23 @@ static void parse_cmdline(int argc, char **argv) {
}
static void assemble_file (char *fname) {
char *value, *p, *line;
char *value, *p, *q, *special, *line;
insn output_ins;
int i, rn_error;
long seg;
int i, rn_error, validid;
long seg, offs;
struct tokenval tokval;
expr *e;
/* pass one */
pass = 1;
current_seg = ofmt->section(NULL, pass, &sb);
preproc->reset(fname, report_error, &nasmlist);
preproc->reset(fname, 1, report_error, evaluate, &nasmlist);
strcpy(currentfile,fname);
lineno = 0;
lineinc = 1;
globallineno = 0;
offs = get_curr_ofs;
eval_info (NULL, current_seg, offs); /* set $ */
while ( (line = preproc->getline()) ) {
lineno += lineinc;
globallineno++;
@ -433,11 +456,33 @@ static void assemble_file (char *fname) {
current_seg = seg;
}
break;
case 2: /* [EXTERN label] */
case 2: /* [EXTERN label:special] */
if (*value == '$')
value++; /* skip initial $ if present */
declare_as_global (value, report_error);
define_label (value, seg_alloc(), 0L, ofmt, report_error);
q = value;
validid = TRUE;
if (!isidstart(*q))
validid = FALSE;
while (*q && *q != ':') {
if (!isidchar(*q))
validid = FALSE;
q++;
}
if (!validid) {
report_error (ERR_NONFATAL,
"identifier expected after EXTERN");
break;
}
if (*q == ':') {
*q++ = '\0';
special = q;
} else
special = NULL;
if (!is_extern(value)) { /* allow re-EXTERN to be ignored */
declare_as_global (value, special, report_error);
define_label (value, seg_alloc(), 0L, NULL, FALSE, TRUE,
ofmt, report_error);
}
break;
case 3: /* [BITS bits] */
switch (atoi(value)) {
@ -452,39 +497,87 @@ static void assemble_file (char *fname) {
break;
}
break;
case 4: /* [GLOBAL symbol] */
case 4: /* [GLOBAL symbol:special] */
if (*value == '$')
value++; /* skip initial $ if present */
declare_as_global (value, report_error);
q = value;
validid = TRUE;
if (!isidstart(*q))
validid = FALSE;
while (*q && *q != ':') {
if (!isidchar(*q))
validid = FALSE;
q++;
}
if (!validid) {
report_error (ERR_NONFATAL,
"identifier expected after GLOBAL");
break;
}
if (*q == ':') {
*q++ = '\0';
special = q;
} else
special = NULL;
declare_as_global (value, special, report_error);
break;
case 5: /* [COMMON symbol size] */
case 5: /* [COMMON symbol size:special] */
p = value;
while (*p && !isspace(*p))
validid = TRUE;
if (!isidstart(*p))
validid = FALSE;
while (*p && !isspace(*p)) {
if (!isidchar(*p))
validid = FALSE;
p++;
}
if (!validid) {
report_error (ERR_NONFATAL,
"identifier expected after COMMON");
break;
}
if (*p) {
long size;
while (*p && isspace(*p))
*p++ = '\0';
q = p;
while (*q && *q != ':')
q++;
if (*q == ':') {
*q++ = '\0';
special = q;
} else
special = NULL;
size = readnum (p, &rn_error);
if (rn_error)
report_error (ERR_NONFATAL, "invalid size specified"
" in COMMON declaration");
else
define_common (value, seg_alloc(), size,
ofmt, report_error);
special, ofmt, report_error);
} else
report_error (ERR_NONFATAL, "no size specified in"
" COMMON declaration");
break;
case 6: /* [ABSOLUTE address] */
current_seg = NO_SEG;
abs_offset = readnum(value, &rn_error);
if (rn_error) {
report_error (ERR_NONFATAL, "invalid address specified"
" for ABSOLUTE directive");
stdscan_reset();
stdscan_bufptr = value;
tokval.t_type = TOKEN_INVALID;
e = evaluate(stdscan, NULL, &tokval, NULL, 1, report_error,
NULL);
if (e) {
if (!is_reloc(e))
report_error (ERR_NONFATAL, "cannot use non-"
"relocatable expression as ABSOLUTE"
" address");
else {
abs_seg = reloc_seg(e);
abs_offset = reloc_value(e);
}
} else
abs_offset = 0x100;/* don't go near zero in case of / */
}
break;
default:
if (!ofmt->directive (line+1, value, 1))
@ -493,9 +586,8 @@ static void assemble_file (char *fname) {
break;
}
} else {
long offs = get_curr_ofs;
parse_line (current_seg, offs, lookup_label,
1, line, &output_ins, ofmt, report_error);
parse_line (1, line, &output_ins,
report_error, evaluate, eval_info);
if (output_ins.forw_ref)
*(int *)saa_wstruct(forwrefs) = globallineno;
@ -535,7 +627,7 @@ static void assemble_file (char *fname) {
define_label (output_ins.label,
output_ins.oprs[0].segment,
output_ins.oprs[0].offset,
ofmt, report_error);
NULL, FALSE, FALSE, ofmt, report_error);
} else if (output_ins.operands == 2 &&
(output_ins.oprs[0].type & IMMEDIATE) &&
(output_ins.oprs[0].type & COLON) &&
@ -547,15 +639,15 @@ static void assemble_file (char *fname) {
define_label (output_ins.label,
output_ins.oprs[0].offset | SEG_ABS,
output_ins.oprs[1].offset,
ofmt, report_error);
NULL, FALSE, FALSE, ofmt, report_error);
} else
report_error(ERR_NONFATAL, "bad syntax for EQU");
}
} else {
if (output_ins.label)
define_label (output_ins.label,
current_seg, offs,
ofmt, report_error);
current_seg==NO_SEG ? abs_seg : current_seg,
offs, NULL, TRUE, FALSE, ofmt, report_error);
offs += insn_size (current_seg, offs, sb,
&output_ins, report_error);
set_curr_ofs (offs);
@ -563,6 +655,8 @@ static void assemble_file (char *fname) {
cleanup_insn (&output_ins);
}
nasm_free (line);
offs = get_curr_ofs;
eval_info (NULL, current_seg, offs); /* set $ */
}
preproc->cleanup();
@ -589,11 +683,13 @@ static void assemble_file (char *fname) {
current_seg = ofmt->section(NULL, pass, &sb);
raa_free (offsets);
offsets = raa_init();
preproc->reset(fname, report_error, &nasmlist);
preproc->reset(fname, 2, report_error, evaluate, &nasmlist);
strcpy(currentfile,fname);
lineno = 0;
lineinc = 1;
globallineno = 0;
offs = get_curr_ofs;
eval_info (NULL, current_seg, offs); /* set $ */
while ( (line = preproc->getline()) ) {
lineno += lineinc;
globallineno++;
@ -619,7 +715,6 @@ static void assemble_file (char *fname) {
/* here we parse our directives; this is not handled by
* the 'real' parser. */
if ( (i = getkw (line, &value)) ) {
switch (i) {
case 1: /* [SEGMENT n] */
@ -631,6 +726,13 @@ static void assemble_file (char *fname) {
current_seg = seg;
break;
case 2: /* [EXTERN label] */
q = value;
while (*q && *q != ':')
q++;
if (*q == ':') {
*q++ = '\0';
ofmt->symdef(value, 0L, 0L, 3, q);
}
break;
case 3: /* [BITS bits] */
switch (atoi(value)) {
@ -646,13 +748,42 @@ static void assemble_file (char *fname) {
}
break;
case 4: /* [GLOBAL symbol] */
q = value;
while (*q && *q != ':')
q++;
if (*q == ':') {
*q++ = '\0';
ofmt->symdef(value, 0L, 0L, 3, q);
}
break;
case 5: /* [COMMON symbol size] */
q = value;
while (*q && *q != ':') {
if (isspace(*q))
*q = '\0';
q++;
}
if (*q == ':') {
*q++ = '\0';
ofmt->symdef(value, 0L, 0L, 3, q);
}
break;
case 6: /* [ABSOLUTE addr] */
current_seg = NO_SEG;
abs_offset = readnum(value, &rn_error);
if (rn_error)
stdscan_reset();
stdscan_bufptr = value;
tokval.t_type = TOKEN_INVALID;
e = evaluate(stdscan, NULL, &tokval, NULL, 2, report_error,
NULL);
if (e) {
if (!is_reloc(e))
report_error (ERR_PANIC, "non-reloc ABSOLUTE address"
" in pass two");
else {
abs_seg = reloc_seg(e);
abs_offset = reloc_value(e);
}
} else
report_error (ERR_PANIC, "invalid ABSOLUTE address "
"in pass two");
break;
@ -662,9 +793,8 @@ static void assemble_file (char *fname) {
break;
}
} else {
long offs = get_curr_ofs;
parse_line (current_seg, offs, lookup_label, 2,
line, &output_ins, ofmt, report_error);
parse_line (2, line, &output_ins,
report_error, evaluate, eval_info);
if (globallineno == forwline) {
int *p = saa_rstruct (forwrefs);
if (p)
@ -702,7 +832,7 @@ static void assemble_file (char *fname) {
define_label (output_ins.label,
output_ins.oprs[0].segment,
output_ins.oprs[0].offset,
ofmt, report_error);
NULL, FALSE, FALSE, ofmt, report_error);
} else if (output_ins.operands == 2 &&
(output_ins.oprs[0].type & IMMEDIATE) &&
(output_ins.oprs[0].type & COLON) &&
@ -712,7 +842,7 @@ static void assemble_file (char *fname) {
define_label (output_ins.label,
output_ins.oprs[0].offset | SEG_ABS,
output_ins.oprs[1].offset,
ofmt, report_error);
NULL, FALSE, FALSE, ofmt, report_error);
} else
report_error(ERR_NONFATAL, "bad syntax for EQU");
}
@ -723,6 +853,9 @@ static void assemble_file (char *fname) {
set_curr_ofs (offs);
}
nasm_free (line);
offs = get_curr_ofs;
eval_info (NULL, current_seg, offs); /* set $ */
}
preproc->cleanup();
nasmlist.cleanup();
@ -786,6 +919,12 @@ static void report_error (int severity, char *fmt, ...) {
suppressed[ (severity & ERR_WARN_MASK) >> ERR_WARN_SHR ])
return; /* and bail out if so */
/*
* See if it's a pass-one only warning and we're not in pass one.
*/
if ((severity & ERR_PASS1) && pass != 1)
return;
if (severity & ERR_NOFILE)
fputs ("nasm: ", use_stdout ? stdout : stderr);
else
@ -839,6 +978,9 @@ static void register_output_formats(void) {
#ifdef OF_AOUT
extern struct ofmt of_aout;
#endif
#ifdef OF_AOUTB
extern struct ofmt of_aoutb;
#endif
#ifdef OF_COFF
extern struct ofmt of_coff;
#endif
@ -856,9 +998,6 @@ static void register_output_formats(void) {
#ifdef OF_WIN32
extern struct ofmt of_win32;
#endif
#ifdef OF_OS2
extern struct ofmt of_os2;
#endif
#ifdef OF_RDF
extern struct ofmt of_rdf;
#endif
@ -872,6 +1011,9 @@ static void register_output_formats(void) {
#ifdef OF_AOUT
ofmt_register (&of_aout);
#endif
#ifdef OF_AOUTB
ofmt_register (&of_aoutb);
#endif
#ifdef OF_COFF
ofmt_register (&of_coff);
#endif
@ -887,9 +1029,6 @@ static void register_output_formats(void) {
#ifdef OF_WIN32
ofmt_register (&of_win32);
#endif
#ifdef OF_OS2
ofmt_register (&of_os2);
#endif
#ifdef OF_RDF
ofmt_register (&of_rdf);
#endif
@ -906,14 +1045,18 @@ static void register_output_formats(void) {
static FILE *no_pp_fp;
static efunc no_pp_err;
static ListGen *no_pp_list;
static void no_pp_reset (char *file, efunc error, ListGen *listgen) {
static void no_pp_reset (char *file, int pass, efunc error, evalfunc eval,
ListGen *listgen) {
no_pp_err = error;
no_pp_fp = fopen(file, "r");
if (!no_pp_fp)
no_pp_err (ERR_FATAL | ERR_NOFILE,
"unable to open input file `%s'", file);
(void) listgen; /* placate compilers */
no_pp_list = listgen;
(void) pass; /* placate compilers */
(void) eval; /* placate compilers */
}
static char *no_pp_getline (void) {
@ -954,6 +1097,8 @@ static char *no_pp_getline (void) {
*/
buffer[strcspn(buffer, "\032")] = '\0';
no_pp_list->line (LIST_READ, buffer);
return buffer;
}

1769
nasm.doc

File diff suppressed because it is too large Load diff

277
nasm.h
View file

@ -12,8 +12,8 @@
#define NASM_NASM_H
#define NASM_MAJOR_VER 0
#define NASM_MINOR_VER 95
#define NASM_VER "0.95"
#define NASM_MINOR_VER 96
#define NASM_VER "0.96"
#ifndef NULL
#define NULL 0
@ -33,6 +33,15 @@
#define FILENAME_MAX 256
#endif
/*
* Name pollution problems: <time.h> on Digital UNIX pulls in some
* strange hardware header file which sees fit to define R_SP. We
* undefine it here so as not to break the enum below.
*/
#ifdef R_SP
#undef R_SP
#endif
/*
* We must declare the existence of this structure type up here,
* since we have to reference it before we define it...
@ -66,15 +75,18 @@ typedef void (*efunc) (int severity, char *fmt, ...);
#define ERR_OFFBY1 0x40 /* report error as being on the line
* we're just _about_ to read, not
* the one we've just read */
#define ERR_PASS1 0x80 /* only print this error on pass one */
/*
* These codes define specific types of suppressible warning.
*/
#define ERR_WARN_MNP 0x0100 /* macro-num-parameters warning */
#define ERR_WARN_OL 0x0200 /* orphan label (no colon, and
* alone on line) */
#define ERR_WARN_NOV 0x0300 /* numeric overflow */
#define ERR_WARN_MASK 0xFF00 /* the mask for this feature */
#define ERR_WARN_SHR 8 /* how far to shift right */
#define ERR_WARN_MAX 2 /* the highest numbered one */
#define ERR_WARN_MAX 3 /* the highest numbered one */
/*
* -----------------------
@ -88,10 +100,14 @@ typedef void (*efunc) (int severity, char *fmt, ...);
typedef int (*lfunc) (char *label, long *segment, long *offset);
/*
* And a label-definition function like this.
* And a label-definition function like this. The boolean parameter
* `is_norm' states whether the label is a `normal' label (which
* should affect the local-label system), or something odder like
* an EQU or a segment-base symbol, which shouldn't.
*/
typedef void (*ldfunc) (char *label, long segment, long offset,
struct ofmt *ofmt, efunc error);
typedef void (*ldfunc) (char *label, long segment, long offset, char *special,
int is_norm, int isextrn, struct ofmt *ofmt,
efunc error);
/*
* List-file generators should look like this:
@ -152,15 +168,128 @@ typedef struct {
void (*downlevel) (int);
} ListGen;
/*
* The expression evaluator must be passed a scanner function; a
* standard scanner is provided as part of nasmlib.c. The
* preprocessor will use a different one. Scanners, and the
* token-value structures they return, look like this.
*
* The return value from the scanner is always a copy of the
* `t_type' field in the structure.
*/
struct tokenval {
int t_type;
long t_integer, t_inttwo;
char *t_charptr;
};
typedef int (*scanner) (void *private_data, struct tokenval *tv);
/*
* Token types returned by the scanner, in addition to ordinary
* ASCII character values, and zero for end-of-string.
*/
enum { /* token types, other than chars */
TOKEN_INVALID = -1, /* a placeholder value */
TOKEN_EOS = 0, /* end of string */
TOKEN_EQ = '=', TOKEN_GT = '>', TOKEN_LT = '<', /* aliases */
TOKEN_ID = 256, TOKEN_NUM, TOKEN_REG, TOKEN_INSN, /* major token types */
TOKEN_ERRNUM, /* numeric constant with error in */
TOKEN_HERE, TOKEN_BASE, /* $ and $$ */
TOKEN_SPECIAL, /* BYTE, WORD, DWORD, FAR, NEAR, etc */
TOKEN_PREFIX, /* A32, O16, LOCK, REPNZ, TIMES, etc */
TOKEN_SHL, TOKEN_SHR, /* << and >> */
TOKEN_SDIV, TOKEN_SMOD, /* // and %% */
TOKEN_GE, TOKEN_LE, TOKEN_NE, /* >=, <= and <> (!= is same as <>) */
TOKEN_DBL_AND, TOKEN_DBL_OR, TOKEN_DBL_XOR, /* &&, || and ^^ */
TOKEN_SEG, TOKEN_WRT, /* SEG and WRT */
TOKEN_FLOAT /* floating-point constant */
};
/*
* Expression-evaluator datatype. Expressions, within the
* evaluator, are stored as an array of these beasts, terminated by
* a record with type==0. Mostly, it's a vector type: each type
* denotes some kind of a component, and the value denotes the
* multiple of that component present in the expression. The
* exception is the WRT type, whose `value' field denotes the
* segment to which the expression is relative. These segments will
* be segment-base types, i.e. either odd segment values or SEG_ABS
* types. So it is still valid to assume that anything with a
* `value' field of zero is insignificant.
*/
typedef struct {
long type; /* a register, or EXPR_xxx */
long value; /* must be >= 32 bits */
} expr;
/*
* The evaluator can also return hints about which of two registers
* used in an expression should be the base register. See also the
* `operand' structure.
*/
struct eval_hints {
int base;
int type;
};
/*
* The actual expression evaluator function looks like this. When
* called, it expects the first token of its expression to already
* be in `*tv'; if it is not, set tv->t_type to TOKEN_INVALID and
* it will start by calling the scanner.
*
* If a forward reference happens during evaluation, the evaluator
* must set `*fwref' to TRUE if `fwref' is non-NULL.
*
* `critical' is non-zero if the expression may not contain forward
* references. The evaluator will report its own error if this
* occurs; if `critical' is 1, the error will be "symbol not
* defined before use", whereas if `critical' is 2, the error will
* be "symbol undefined".
*
* If `critical' has bit 4 set (in addition to its main value: 0x11
* and 0x12 correspond to 1 and 2) then an extended expression
* syntax is recognised, in which relational operators such as =, <
* and >= are accepted, as well as low-precedence logical operators
* &&, ^^ and ||.
*
* If `hints' is non-NULL, it gets filled in with some hints as to
* the base register in complex effective addresses.
*/
typedef expr *(*evalfunc) (scanner sc, void *scprivate, struct tokenval *tv,
int *fwref, int critical, efunc error,
struct eval_hints *hints);
/*
* There's also an auxiliary routine through which the evaluator
* needs to hear about the value of $ and the label (if any)
* defined on the current line.
*/
typedef void (*evalinfofunc) (char *labelname, long segment, long offset);
/*
* Special values for expr->type. ASSUMPTION MADE HERE: the number
* of distinct register names (i.e. possible "type" fields for an
* expr structure) does not exceed 124 (EXPR_REG_START through
* EXPR_REG_END).
*/
#define EXPR_REG_START 1
#define EXPR_REG_END 124
#define EXPR_UNKNOWN 125L /* for forward references */
#define EXPR_SIMPLE 126L
#define EXPR_WRT 127L
#define EXPR_SEGBASE 128L
/*
* Preprocessors ought to look like this:
*/
typedef struct {
/*
* Called at the start of a pass; given a file name, an error
* reporting function and a listing generator to talk to.
* Called at the start of a pass; given a file name, the number
* of the pass, an error reporting function, an evaluator
* function, and a listing generator to talk to.
*/
void (*reset) (char *, efunc, ListGen *);
void (*reset) (char *, int, efunc, evalfunc, ListGen *);
/*
* Called to fetch a line of preprocessed source. The line
@ -252,9 +381,9 @@ enum {
#define REG8 0x00201001L
#define REG16 0x00201002L
#define REG32 0x00201004L
#define MMXREG 0x00201008L /* MMX registers */
#define FPUREG 0x01000000L /* floating point stack registers */
#define FPU0 0x01000800L /* FPU stack register zero */
#define MMXREG 0x00001008L /* MMX registers */
/* special register operands: these may be treated differently */
#define REG_SMASK 0x00070000L /* a mask for the following */
@ -290,13 +419,13 @@ enum {
*/
enum { /* register names */
R_AH = 1, R_AL, R_AX, R_BH, R_BL, R_BP, R_BX, R_CH, R_CL, R_CR0,
R_CR2, R_CR3, R_CR4, R_CS, R_CX, R_DH, R_DI, R_DL, R_DR0, R_DR1,
R_DR2, R_DR3, R_DR6, R_DR7, R_DS, R_DX, R_EAX, R_EBP, R_EBX,
R_ECX, R_EDI, R_EDX, R_ES, R_ESI, R_ESP, R_FS, R_GS, R_MM0,
R_MM1, R_MM2, R_MM3, R_MM4, R_MM5, R_MM6, R_MM7, R_SI, R_SP,
R_SS, R_ST0, R_ST1, R_ST2, R_ST3, R_ST4, R_ST5, R_ST6, R_ST7,
R_TR3, R_TR4, R_TR5, R_TR6, R_TR7, REG_ENUM_LIMIT
R_AH = EXPR_REG_START, R_AL, R_AX, R_BH, R_BL, R_BP, R_BX, R_CH,
R_CL, R_CR0, R_CR2, R_CR3, R_CR4, R_CS, R_CX, R_DH, R_DI, R_DL,
R_DR0, R_DR1, R_DR2, R_DR3, R_DR6, R_DR7, R_DS, R_DX, R_EAX,
R_EBP, R_EBX, R_ECX, R_EDI, R_EDX, R_ES, R_ESI, R_ESP, R_FS,
R_GS, R_MM0, R_MM1, R_MM2, R_MM3, R_MM4, R_MM5, R_MM6, R_MM7,
R_SI, R_SP, R_SS, R_ST0, R_ST1, R_ST2, R_ST3, R_ST4, R_ST5,
R_ST6, R_ST7, R_TR3, R_TR4, R_TR5, R_TR6, R_TR7, REG_ENUM_LIMIT
};
enum { /* instruction names */
@ -314,38 +443,41 @@ enum { /* instruction names */
I_FIDIVR, I_FILD, I_FIMUL, I_FINCSTP, I_FINIT, I_FIST, I_FISTP,
I_FISUB, I_FISUBR, I_FLD, I_FLD1, I_FLDCW, I_FLDENV, I_FLDL2E,
I_FLDL2T, I_FLDLG2, I_FLDLN2, I_FLDPI, I_FLDZ, I_FMUL, I_FMULP,
I_FNOP, I_FPATAN, I_FPREM, I_FPREM1, I_FPTAN, I_FRNDINT,
I_FRSTOR, I_FSAVE, I_FSCALE, I_FSETPM, I_FSIN, I_FSINCOS,
I_FSQRT, I_FST, I_FSTCW, I_FSTENV, I_FSTP, I_FSTSW, I_FSUB,
I_FSUBP, I_FSUBR, I_FSUBRP, I_FTST, I_FUCOM, I_FUCOMI,
I_FUCOMIP, I_FUCOMP, I_FUCOMPP, I_FXAM, I_FXCH, I_FXTRACT,
I_FYL2X, I_FYL2XP1, I_HLT, I_IBTS, I_ICEBP, I_IDIV, I_IMUL,
I_IN, I_INC, I_INCBIN, I_INSB, I_INSD, I_INSW, I_INT, I_INT1,
I_INT01, I_INT3, I_INTO, I_INVD, I_INVLPG, I_IRET, I_IRETD,
I_IRETW, I_JCXZ, I_JECXZ, I_JMP, I_LAHF, I_LAR, I_LDS, I_LEA,
I_LEAVE, I_LES, I_LFS, I_LGDT, I_LGS, I_LIDT, I_LLDT, I_LMSW,
I_LOADALL, I_LOADALL286, I_LODSB, I_LODSD, I_LODSW, I_LOOP,
I_LOOPE, I_LOOPNE, I_LOOPNZ, I_LOOPZ, I_LSL, I_LSS, I_LTR,
I_MOV, I_MOVD, I_MOVQ, I_MOVSB, I_MOVSD, I_MOVSW, I_MOVSX,
I_MOVZX, I_MUL, I_NEG, I_NOP, I_NOT, I_OR, I_OUT, I_OUTSB,
I_OUTSD, I_OUTSW, I_PACKSSDW, I_PACKSSWB, I_PACKUSWB, I_PADDB,
I_PADDD, I_PADDSB, I_PADDSW, I_PADDUSB, I_PADDUSW, I_PADDW,
I_PAND, I_PANDN, I_PCMPEQB, I_PCMPEQD, I_PCMPEQW, I_PCMPGTB,
I_PCMPGTD, I_PCMPGTW, I_PMADDWD, I_PMULHW, I_PMULLW, I_POP,
I_POPA, I_POPAD, I_POPAW, I_POPF, I_POPFD, I_POPFW, I_POR,
I_PSLLD, I_PSLLQ, I_PSLLW, I_PSRAD, I_PSRAW, I_PSRLD, I_PSRLQ,
I_PSRLW, I_PSUBB, I_PSUBD, I_PSUBSB, I_PSUBSW, I_PSUBUSB,
I_PSUBUSW, I_PSUBW, I_PUNPCKHBW, I_PUNPCKHDQ, I_PUNPCKHWD,
I_PUNPCKLBW, I_PUNPCKLDQ, I_PUNPCKLWD, I_PUSH, I_PUSHA,
I_PUSHAD, I_PUSHAW, I_PUSHF, I_PUSHFD, I_PUSHFW, I_PXOR, I_RCL,
I_RCR, I_RDMSR, I_RDPMC, I_RDTSC, I_RESB, I_RESD, I_RESQ,
I_REST, I_RESW, I_RET, I_RETF, I_RETN, I_ROL, I_ROR, I_RSM,
I_SAHF, I_SAL, I_SALC, I_SAR, I_SBB, I_SCASB, I_SCASD, I_SCASW,
I_SGDT, I_SHL, I_SHLD, I_SHR, I_SHRD, I_SIDT, I_SLDT, I_SMI,
I_SMSW, I_STC, I_STD, I_STI, I_STOSB, I_STOSD, I_STOSW, I_STR,
I_SUB, I_TEST, I_UMOV, I_VERR, I_VERW, I_WAIT, I_WBINVD,
I_WRMSR, I_XADD, I_XBTS, I_XCHG, I_XLATB, I_XOR, I_CMOVcc,
I_Jcc, I_SETcc
I_FNCLEX, I_FNDISI, I_FNENI, I_FNINIT, I_FNOP, I_FNSAVE,
I_FNSTCW, I_FNSTENV, I_FNSTSW, I_FPATAN, I_FPREM, I_FPREM1,
I_FPTAN, I_FRNDINT, I_FRSTOR, I_FSAVE, I_FSCALE, I_FSETPM,
I_FSIN, I_FSINCOS, I_FSQRT, I_FST, I_FSTCW, I_FSTENV, I_FSTP,
I_FSTSW, I_FSUB, I_FSUBP, I_FSUBR, I_FSUBRP, I_FTST, I_FUCOM,
I_FUCOMI, I_FUCOMIP, I_FUCOMP, I_FUCOMPP, I_FXAM, I_FXCH,
I_FXTRACT, I_FYL2X, I_FYL2XP1, I_HLT, I_IBTS, I_ICEBP, I_IDIV,
I_IMUL, I_IN, I_INC, I_INCBIN, I_INSB, I_INSD, I_INSW, I_INT,
I_INT1, I_INT01, I_INT3, I_INTO, I_INVD, I_INVLPG, I_IRET,
I_IRETD, I_IRETW, I_JCXZ, I_JECXZ, I_JMP, I_LAHF, I_LAR, I_LDS,
I_LEA, I_LEAVE, I_LES, I_LFS, I_LGDT, I_LGS, I_LIDT, I_LLDT,
I_LMSW, I_LOADALL, I_LOADALL286, I_LODSB, I_LODSD, I_LODSW,
I_LOOP, I_LOOPE, I_LOOPNE, I_LOOPNZ, I_LOOPZ, I_LSL, I_LSS,
I_LTR, I_MOV, I_MOVD, I_MOVQ, I_MOVSB, I_MOVSD, I_MOVSW,
I_MOVSX, I_MOVZX, I_MUL, I_NEG, I_NOP, I_NOT, I_OR, I_OUT,
I_OUTSB, I_OUTSD, I_OUTSW, I_PACKSSDW, I_PACKSSWB, I_PACKUSWB,
I_PADDB, I_PADDD, I_PADDSB, I_PADDSIW, I_PADDSW, I_PADDUSB,
I_PADDUSW, I_PADDW, I_PAND, I_PANDN, I_PAVEB, I_PCMPEQB,
I_PCMPEQD, I_PCMPEQW, I_PCMPGTB, I_PCMPGTD, I_PCMPGTW,
I_PDISTIB, I_PMACHRIW, I_PMADDWD, I_PMAGW, I_PMULHRW,
I_PMULHRIW, I_PMULHW, I_PMULLW, I_PMVGEZB, I_PMVLZB, I_PMVNZB,
I_PMVZB, I_POP, I_POPA, I_POPAD, I_POPAW, I_POPF, I_POPFD,
I_POPFW, I_POR, I_PSLLD, I_PSLLQ, I_PSLLW, I_PSRAD, I_PSRAW,
I_PSRLD, I_PSRLQ, I_PSRLW, I_PSUBB, I_PSUBD, I_PSUBSB,
I_PSUBSIW, I_PSUBSW, I_PSUBUSB, I_PSUBUSW, I_PSUBW, I_PUNPCKHBW,
I_PUNPCKHDQ, I_PUNPCKHWD, I_PUNPCKLBW, I_PUNPCKLDQ, I_PUNPCKLWD,
I_PUSH, I_PUSHA, I_PUSHAD, I_PUSHAW, I_PUSHF, I_PUSHFD,
I_PUSHFW, I_PXOR, I_RCL, I_RCR, I_RDMSR, I_RDPMC, I_RDTSC,
I_RESB, I_RESD, I_RESQ, I_REST, I_RESW, I_RET, I_RETF, I_RETN,
I_ROL, I_ROR, I_RSM, I_SAHF, I_SAL, I_SALC, I_SAR, I_SBB,
I_SCASB, I_SCASD, I_SCASW, I_SGDT, I_SHL, I_SHLD, I_SHR, I_SHRD,
I_SIDT, I_SLDT, I_SMI, I_SMSW, I_STC, I_STD, I_STI, I_STOSB,
I_STOSD, I_STOSW, I_STR, I_SUB, I_TEST, I_UMOV, I_VERR, I_VERW,
I_WAIT, I_WBINVD, I_WRMSR, I_XADD, I_XBTS, I_XCHG, I_XLATB,
I_XOR, I_CMOVcc, I_Jcc, I_SETcc
};
enum { /* condition code names */
@ -369,13 +501,27 @@ enum { /* extended operand types */
EOT_NOTHING, EOT_DB_STRING, EOT_DB_NUMBER
};
enum { /* special EA flags */
EAF_BYTEOFFS = 1, /* force offset part to byte size */
EAF_WORDOFFS = 2, /* force offset part to [d]word size */
EAF_TIMESTWO = 4 /* really do EAX*2 not EAX+EAX */
};
enum { /* values for `hinttype' */
EAH_NOHINT = 0, /* no hint at all - our discretion */
EAH_MAKEBASE = 1, /* try to make given reg the base */
EAH_NOTBASE = 2 /* try _not_ to make reg the base */
};
typedef struct { /* operand to an instruction */
long type; /* type of operand */
int addr_size; /* 0 means default; 16; 32 */
int basereg, indexreg, scale; /* registers and scale involved */
int hintbase, hinttype; /* hint as to real base register */
long segment; /* immediate segment, if needed */
long offset; /* any immediate number */
long wrt; /* segment base it's relative to */
int eaflags; /* special EA flags */
} operand;
typedef struct extop { /* extended operand */
@ -422,14 +568,23 @@ struct ofmt {
*/
char *shortname;
/*
* This, if non-NULL, is a NULL-terminated list of `char *'s
* pointing to extra standard macros supplied by the object
* format (e.g. a sensible initial default value of __SECT__,
* and user-level equivalents for any format-specific
* directives).
*/
char **stdmac;
/*
* This procedure is called at the start of an output session.
* It tells the output format what file it will be writing to,
* what routine to report errors through, and how to interface
* to the label manager if necessary. It also gives it a chance
* to do other initialisation.
* to the label manager and expression evaluator if necessary.
* It also gives it a chance to do other initialisation.
*/
void (*init) (FILE *fp, efunc error, ldfunc ldef);
void (*init) (FILE *fp, efunc error, ldfunc ldef, evalfunc eval);
/*
* This procedure is called by assemble() to write actual
@ -465,8 +620,14 @@ struct ofmt {
* re-entrancy is guaranteed in the label manager. However, the
* label manager will in turn call this routine, so it should
* be prepared to be re-entrant itself.
*
* The `special' parameter contains special information passed
* through from the command that defined the label: it may have
* been an EXTERN, a COMMON or a GLOBAL. The distinction should
* be obvious to the output format from the other parameters.
*/
void (*symdef) (char *name, long segment, long offset, int is_global);
void (*symdef) (char *name, long segment, long offset, int is_global,
char *special);
/*
* This procedure is called when the source code requests a
@ -492,6 +653,11 @@ struct ofmt {
* required to produce in return a segment value which may be
* different. It can map segment bases to absolute numbers by
* means of returning SEG_ABS types.
*
* It should return NO_SEG if the segment base cannot be
* determined; the evaluator (which calls this routine) is
* responsible for throwing an error condition if that occurs
* in pass two or in a critical expression.
*/
long (*segbase) (long segment);
@ -516,8 +682,9 @@ struct ofmt {
* the "init" routine - and is passed the name of the input
* file from which this output file is being generated. It
* should return its preferred name for the output file in
* `outfunc'. Since it is called before the driver is properly
* initialised, it has to be passed its error handler
* `outname', if outname[0] is not '\0', and do nothing to
* `outname' otherwise. Since it is called before the driver is
* properly initialised, it has to be passed its error handler
* separately.
*
* This procedure may also take its own copy of the input file

422
nasmlib.c
View file

@ -102,6 +102,28 @@ char *nasm_strdup (char *s)
return p;
}
#ifdef LOGALLOC
char *nasm_strndup_log (char *file, int line, char *s, size_t len)
#else
char *nasm_strndup (char *s, size_t len)
#endif
{
char *p;
int size = len+1;
p = malloc(size);
if (!p)
nasm_malloc_error (ERR_FATAL | ERR_NOFILE, "out of memory");
#ifdef LOGALLOC
else
fprintf(logfp, "%s %d strndup(%ld) returns %p\n",
file, line, (long)size, p);
#endif
strncpy (p, s, len);
p[len] = '\0';
return p;
}
int nasm_stricmp (char *s1, char *s2) {
while (*s1 && toupper(*s1) == toupper(*s2))
s1++, s2++;
@ -130,7 +152,8 @@ int nasm_strnicmp (char *s1, char *s2, int n) {
long readnum (char *str, int *error) {
char *r = str, *q;
long radix;
long result;
unsigned long result, checklimit;
int warn = FALSE;
*error = FALSE;
@ -157,15 +180,42 @@ long readnum (char *str, int *error) {
else
radix = 10;
/*
* If this number has been found for us by something other than
* the ordinary scanners, then it might be malformed by having
* nothing between the prefix and the suffix. Check this case
* now.
*/
if (r >= q) {
*error = TRUE;
return 0;
}
/*
* `checklimit' must be 2**32 / radix. We can't do that in
* 32-bit arithmetic, which we're (probably) using, so we
* cheat: since we know that all radices we use are even, we
* can divide 2**31 by radix/2 instead.
*/
checklimit = 0x80000000UL / (radix>>1);
result = 0;
while (*r && r < q) {
if (*r<'0' || (*r>'9' && *r<'A') || numvalue(*r)>=radix) {
*error = TRUE;
return 0;
}
if (result >= checklimit)
warn = TRUE;
result = radix * result + numvalue(*r);
r++;
}
if (warn)
nasm_malloc_error (ERR_WARNING | ERR_PASS1 | ERR_WARN_NOV,
"numeric constant %s does not fit in 32 bits",
str);
return result;
}
@ -195,6 +245,8 @@ void standard_extension (char *inname, char *outname, char *extension,
efunc error) {
char *p, *q;
if (*outname) /* file name already exists, */
return; /* so do nothing */
q = inname;
p = outname;
while (*q) *p++ = *q++; /* copy, and find end of string */
@ -225,7 +277,21 @@ typedef struct RAA_LEAF RAA_LEAF;
typedef struct RAA_BRANCH RAA_BRANCH;
struct RAA {
/*
* Number of layers below this one to get to the real data. 0
* means this structure is a leaf, holding RAA_BLKSIZE real
* data items; 1 and above mean it's a branch, holding
* RAA_LAYERSIZE pointers to the next level branch or leaf
* structures.
*/
int layers;
/*
* Number of real data items spanned by one position in the
* `data' array at this level. This number is 1, trivially, for
* a leaf (level 0): for a level 1 branch it should be
* RAA_BLKSIZE, and for a level 2 branch it's
* RAA_LAYERSIZE*RAA_BLKSIZE.
*/
long stepsize;
union RAA_UNION {
struct RAA_LEAF {
@ -254,8 +320,8 @@ static struct RAA *real_raa_init (int layers) {
r = nasm_malloc (BRANCHSIZ);
memset (r->u.b.data, 0, sizeof(r->u.b.data));
r->layers = layers;
r->stepsize = 1L;
while (layers--)
r->stepsize = RAA_BLKSIZE;
while (--layers)
r->stepsize *= RAA_LAYERSIZE;
}
return r;
@ -541,3 +607,353 @@ void saa_fpwrite (struct SAA *s, FILE *fp) {
while ( (data = saa_rbytes (s, &len)) )
fwrite (data, 1, len, fp);
}
/*
* Register, instruction, condition-code and prefix keywords used
* by the scanner.
*/
#include "names.c"
static char *special_names[] = {
"byte", "dword", "far", "long", "near", "nosplit", "qword",
"short", "to", "tword", "word"
};
static char *prefix_names[] = {
"a16", "a32", "lock", "o16", "o32", "rep", "repe", "repne",
"repnz", "repz", "times"
};
/*
* Standard scanner routine used by parser.c and some output
* formats. It keeps a succession of temporary-storage strings in
* stdscan_tempstorage, which can be cleared using stdscan_reset.
*/
static char **stdscan_tempstorage = NULL;
static int stdscan_tempsize = 0, stdscan_templen = 0;
#define STDSCAN_TEMP_DELTA 256
static void stdscan_pop(void) {
nasm_free (stdscan_tempstorage[--stdscan_templen]);
}
void stdscan_reset(void) {
while (stdscan_templen > 0)
stdscan_pop();
}
static char *stdscan_copy(char *p, int len) {
char *text;
text = nasm_malloc(len+1);
strncpy (text, p, len);
text[len] = '\0';
if (stdscan_templen >= stdscan_tempsize) {
stdscan_tempsize += STDSCAN_TEMP_DELTA;
stdscan_tempstorage = nasm_realloc(stdscan_tempstorage,
stdscan_tempsize*sizeof(char *));
}
stdscan_tempstorage[stdscan_templen++] = text;
return text;
}
char *stdscan_bufptr = NULL;
int stdscan (void *private_data, struct tokenval *tv) {
char ourcopy[256], *r, *s;
while (isspace(*stdscan_bufptr)) stdscan_bufptr++;
if (!*stdscan_bufptr)
return tv->t_type = 0;
/* we have a token; either an id, a number or a char */
if (isidstart(*stdscan_bufptr) ||
(*stdscan_bufptr == '$' && isidstart(stdscan_bufptr[1]))) {
/* now we've got an identifier */
int i;
int is_sym = FALSE;
if (*stdscan_bufptr == '$') {
is_sym = TRUE;
stdscan_bufptr++;
}
r = stdscan_bufptr++;
while (isidchar(*stdscan_bufptr)) stdscan_bufptr++;
tv->t_charptr = stdscan_copy(r, stdscan_bufptr - r);
for (s=tv->t_charptr, r=ourcopy; *s; s++)
*r++ = tolower (*s);
*r = '\0';
if (is_sym)
return tv->t_type = TOKEN_ID;/* bypass all other checks */
/* right, so we have an identifier sitting in temp storage. now,
* is it actually a register or instruction name, or what? */
if ((tv->t_integer=bsi(ourcopy, reg_names,
elements(reg_names)))>=0) {
tv->t_integer += EXPR_REG_START;
return tv->t_type = TOKEN_REG;
} else if ((tv->t_integer=bsi(ourcopy, insn_names,
elements(insn_names)))>=0) {
return tv->t_type = TOKEN_INSN;
}
for (i=0; i<elements(icn); i++)
if (!strncmp(ourcopy, icn[i], strlen(icn[i]))) {
char *p = ourcopy + strlen(icn[i]);
tv->t_integer = ico[i];
if ((tv->t_inttwo=bsi(p, conditions,
elements(conditions)))>=0)
return tv->t_type = TOKEN_INSN;
}
if ((tv->t_integer=bsi(ourcopy, prefix_names,
elements(prefix_names)))>=0) {
tv->t_integer += PREFIX_ENUM_START;
return tv->t_type = TOKEN_PREFIX;
}
if ((tv->t_integer=bsi(ourcopy, special_names,
elements(special_names)))>=0)
return tv->t_type = TOKEN_SPECIAL;
if (!strcmp(ourcopy, "seg"))
return tv->t_type = TOKEN_SEG;
if (!strcmp(ourcopy, "wrt"))
return tv->t_type = TOKEN_WRT;
return tv->t_type = TOKEN_ID;
} else if (*stdscan_bufptr == '$' && !isnumchar(stdscan_bufptr[1])) {
/*
* It's a $ sign with no following hex number; this must
* mean it's a Here token ($), evaluating to the current
* assembly location, or a Base token ($$), evaluating to
* the base of the current segment.
*/
stdscan_bufptr++;
if (*stdscan_bufptr == '$') {
stdscan_bufptr++;
return tv->t_type = TOKEN_BASE;
}
return tv->t_type = TOKEN_HERE;
} else if (isnumstart(*stdscan_bufptr)) { /* now we've got a number */
int rn_error;
r = stdscan_bufptr++;
while (isnumchar(*stdscan_bufptr))
stdscan_bufptr++;
if (*stdscan_bufptr == '.') {
/*
* a floating point constant
*/
stdscan_bufptr++;
while (isnumchar(*stdscan_bufptr)) {
stdscan_bufptr++;
}
tv->t_charptr = stdscan_copy(r, stdscan_bufptr - r);
return tv->t_type = TOKEN_FLOAT;
}
r = stdscan_copy(r, stdscan_bufptr - r);
tv->t_integer = readnum(r, &rn_error);
stdscan_pop();
if (rn_error)
return tv->t_type = TOKEN_ERRNUM;/* some malformation occurred */
tv->t_charptr = NULL;
return tv->t_type = TOKEN_NUM;
} else if (*stdscan_bufptr == '\'' ||
*stdscan_bufptr == '"') {/* a char constant */
char quote = *stdscan_bufptr++, *r;
r = tv->t_charptr = stdscan_bufptr;
while (*stdscan_bufptr && *stdscan_bufptr != quote) stdscan_bufptr++;
tv->t_inttwo = stdscan_bufptr - r; /* store full version */
if (!*stdscan_bufptr)
return tv->t_type = TOKEN_ERRNUM; /* unmatched quotes */
tv->t_integer = 0;
r = stdscan_bufptr++; /* skip over final quote */
while (quote != *--r) {
tv->t_integer = (tv->t_integer<<8) + (unsigned char) *r;
}
return tv->t_type = TOKEN_NUM;
} else if (*stdscan_bufptr == ';') { /* a comment has happened - stay */
return tv->t_type = 0;
} else if (stdscan_bufptr[0] == '>' && stdscan_bufptr[1] == '>') {
stdscan_bufptr += 2;
return tv->t_type = TOKEN_SHR;
} else if (stdscan_bufptr[0] == '<' && stdscan_bufptr[1] == '<') {
stdscan_bufptr += 2;
return tv->t_type = TOKEN_SHL;
} else if (stdscan_bufptr[0] == '/' && stdscan_bufptr[1] == '/') {
stdscan_bufptr += 2;
return tv->t_type = TOKEN_SDIV;
} else if (stdscan_bufptr[0] == '%' && stdscan_bufptr[1] == '%') {
stdscan_bufptr += 2;
return tv->t_type = TOKEN_SMOD;
} else if (stdscan_bufptr[0] == '=' && stdscan_bufptr[1] == '=') {
stdscan_bufptr += 2;
return tv->t_type = TOKEN_EQ;
} else if (stdscan_bufptr[0] == '<' && stdscan_bufptr[1] == '>') {
stdscan_bufptr += 2;
return tv->t_type = TOKEN_NE;
} else if (stdscan_bufptr[0] == '!' && stdscan_bufptr[1] == '=') {
stdscan_bufptr += 2;
return tv->t_type = TOKEN_NE;
} else if (stdscan_bufptr[0] == '<' && stdscan_bufptr[1] == '=') {
stdscan_bufptr += 2;
return tv->t_type = TOKEN_LE;
} else if (stdscan_bufptr[0] == '>' && stdscan_bufptr[1] == '=') {
stdscan_bufptr += 2;
return tv->t_type = TOKEN_GE;
} else if (stdscan_bufptr[0] == '&' && stdscan_bufptr[1] == '&') {
stdscan_bufptr += 2;
return tv->t_type = TOKEN_DBL_AND;
} else if (stdscan_bufptr[0] == '^' && stdscan_bufptr[1] == '^') {
stdscan_bufptr += 2;
return tv->t_type = TOKEN_DBL_XOR;
} else if (stdscan_bufptr[0] == '|' && stdscan_bufptr[1] == '|') {
stdscan_bufptr += 2;
return tv->t_type = TOKEN_DBL_OR;
} else /* just an ordinary char */
return tv->t_type = (unsigned char) (*stdscan_bufptr++);
}
/*
* Return TRUE if the argument is a simple scalar. (Or a far-
* absolute, which counts.)
*/
int is_simple (expr *vect) {
while (vect->type && !vect->value)
vect++;
if (!vect->type)
return 1;
if (vect->type != EXPR_SIMPLE)
return 0;
do {
vect++;
} while (vect->type && !vect->value);
if (vect->type && vect->type < EXPR_SEGBASE+SEG_ABS) return 0;
return 1;
}
/*
* Return TRUE if the argument is a simple scalar, _NOT_ a far-
* absolute.
*/
int is_really_simple (expr *vect) {
while (vect->type && !vect->value)
vect++;
if (!vect->type)
return 1;
if (vect->type != EXPR_SIMPLE)
return 0;
do {
vect++;
} while (vect->type && !vect->value);
if (vect->type) return 0;
return 1;
}
/*
* Return TRUE if the argument is relocatable (i.e. a simple
* scalar, plus at most one segment-base, plus possibly a WRT).
*/
int is_reloc (expr *vect) {
while (vect->type && !vect->value)
vect++;
if (!vect->type)
return 1;
if (vect->type < EXPR_SIMPLE)
return 0;
if (vect->type == EXPR_SIMPLE) {
do {
vect++;
} while (vect->type && !vect->value);
if (!vect->type)
return 1;
}
if (vect->type != EXPR_WRT && vect->value != 0 && vect->value != 1)
return 0; /* segment base multiplier non-unity */
do {
vect++;
} while (vect->type && (vect->type == EXPR_WRT || !vect->value));
if (!vect->type)
return 1;
return 0;
}
/*
* Return TRUE if the argument contains an `unknown' part.
*/
int is_unknown(expr *vect) {
while (vect->type && vect->type < EXPR_UNKNOWN)
vect++;
return (vect->type == EXPR_UNKNOWN);
}
/*
* Return TRUE if the argument contains nothing but an `unknown'
* part.
*/
int is_just_unknown(expr *vect) {
while (vect->type && !vect->value)
vect++;
return (vect->type == EXPR_UNKNOWN);
}
/*
* Return the scalar part of a relocatable vector. (Including
* simple scalar vectors - those qualify as relocatable.)
*/
long reloc_value (expr *vect) {
while (vect->type && !vect->value)
vect++;
if (!vect->type) return 0;
if (vect->type == EXPR_SIMPLE)
return vect->value;
else
return 0;
}
/*
* Return the segment number of a relocatable vector, or NO_SEG for
* simple scalars.
*/
long reloc_seg (expr *vect) {
while (vect->type && (vect->type == EXPR_WRT || !vect->value))
vect++;
if (vect->type == EXPR_SIMPLE) {
do {
vect++;
} while (vect->type && (vect->type == EXPR_WRT || !vect->value));
}
if (!vect->type)
return NO_SEG;
else
return vect->type - EXPR_SEGBASE;
}
/*
* Return the WRT segment number of a relocatable vector, or NO_SEG
* if no WRT part is present.
*/
long reloc_wrt (expr *vect) {
while (vect->type && vect->type < EXPR_WRT)
vect++;
if (vect->type == EXPR_WRT) {
return vect->value;
} else
return NO_SEG;
}
/*
* Binary search.
*/
int bsi (char *string, char **array, int size) {
int i = -1, j = size; /* always, i < index < j */
while (j-i >= 2) {
int k = (i+j)/2;
int l = strcmp(string, array[k]);
if (l<0) /* it's in the first half */
j = k;
else if (l>0) /* it's in the second half */
i = k;
else /* we've got it :) */
return k;
}
return -1; /* we haven't got it :( */
}

View file

@ -32,15 +32,18 @@ void *nasm_malloc (size_t);
void *nasm_realloc (void *, size_t);
void nasm_free (void *);
char *nasm_strdup (char *);
char *nasm_strndup (char *, size_t);
#else
void *nasm_malloc_log (char *, int, size_t);
void *nasm_realloc_log (char *, int, void *, size_t);
void nasm_free_log (char *, int, void *);
char *nasm_strdup_log (char *, int, char *);
char *nasm_strndup_log (char *, int, char *, size_t);
#define nasm_malloc(x) nasm_malloc_log(__FILE__,__LINE__,x)
#define nasm_realloc(x,y) nasm_realloc_log(__FILE__,__LINE__,x,y)
#define nasm_free(x) nasm_free_log(__FILE__,__LINE__,x)
#define nasm_strdup(x) nasm_strdup_log(__FILE__,__LINE__,x)
#define nasm_strndup(x,y) nasm_strndup_log(__FILE__,__LINE__,x,y)
#endif
#endif
@ -136,4 +139,34 @@ void saa_fread (struct SAA *s, long posn, void *p, long len); /* fixup */
void saa_fwrite (struct SAA *s, long posn, void *p, long len); /* fixup */
void saa_fpwrite (struct SAA *, FILE *);
#ifdef NASM_NASM_H
/*
* Standard scanner.
*/
extern char *stdscan_bufptr;
void stdscan_reset(void);
int stdscan (void *private_data, struct tokenval *tv);
#endif
#ifdef NASM_NASM_H
/*
* Library routines to manipulate expression data types.
*/
int is_reloc(expr *);
int is_simple(expr *);
int is_really_simple (expr *);
int is_unknown(expr *);
int is_just_unknown(expr *);
long reloc_value(expr *);
long reloc_seg(expr *);
long reloc_wrt(expr *);
#endif
/*
* Binary search routine. Returns index into `array' of an entry
* matching `string', or <0 if no match. `array' is taken to
* contain `size' elements.
*/
int bsi (char *string, char **array, int size);
#endif

117
ndisasm.1 Normal file
View file

@ -0,0 +1,117 @@
.TH NDISASM 1 "The Netwide Assembler Project"
.SH NAME
ndisasm \- the Netwide Disassembler \- 80x86 binary file disassembler
.SH SYNOPSIS
.B ndisasm
[
.B \-o
origin
] [
.B \-s
sync-point [...]]
[
.B \-a
|
.B \-i
] [
.B \-b
bits
] [
.B -u
] [
.B \-e
hdrlen
] [
.B \-k
offset,length [...]]
infile
.br
.B ndisasm \-h
.br
.B ndisasm \-r
.SH DESCRIPTION
The
.B ndisasm
command generates a disassembly listing of the binary file
.I infile
and directs it to stdout.
.SS OPTIONS
.TP
.B \-h
Causes
.B ndisasm
to exit immediately, after giving a summary of its invocation
options.
.TP
.BI \-r
Causes
.B ndisasm
to exit immediately, after displaying its version number.
.TP
.BI \-o " origin"
Specifies the notional load address for the file. This option causes
.B ndisasm
to get the addresses it lists down the left hand margin, and the
target addresses of PC-relative jumps and calls, right.
.TP
.BI \-s " sync-point"
Manually specifies a synchronisation address, such that
.B ndisasm
will not output any machine instruction which encompasses bytes on
both sides of the address. Hence the instruction which
.I starts
at that address will be correctly disassembled.
.TP
.BI \-e " hdrlen"
Specifies a number of bytes to discard from the beginning of the
file before starting disassembly. This does not count towards the
calculation of the disassembly offset: the first
.I disassembled
instruction will be shown starting at the given load address.
.TP
.BI \-k " offset,length"
Specifies that
.I length
bytes, starting from disassembly offset
.IR offset ,
should be skipped over without generating any output. The skipped
bytes still count towards the calculation of the disassembly offset.
.TP
.BR \-a " or " \-i
Enables automatic (or intelligent) sync mode, in which
.B ndisasm
will attempt to guess where synchronisation should be performed, by
means of examining the target addresses of the relative jumps and
calls it disassembles.
.TP
.BI \-b " bits"
Specifies either 16-bit or 32-bit mode. The default is 16-bit mode.
.TP
.B \-u
Specifies 32-bit mode, more compactly than using `-b 32'.
.PP
.RE
.SH RESTRICTIONS
.B ndisasm
only disassembles binary files: it has no understanding of the
header information present in object or executable files. If you
want to disassemble an object file, you should probably be using
.BR objdump "(" 1 ")."
.PP
Auto-sync mode won't necessarily cure all your synchronisation
problems: a sync marker can only be placed automatically if a jump
or call instruction is found to refer to it
.I before
.B ndisasm
actually disassembles that part of the code. Also, if spurious jumps
or calls result from disassembling non-machine-code data, sync
markers may get placed in strange places. Feel free to turn
auto-sync off and go back to doing it manually if necessary.
.PP
.B ndisasm
can only keep track of 8192 sync markers internally at once: this is
to do with portability, since DOS machines don't take kindly to more
than 64K being allocated at a time.
.PP
.SH SEE ALSO
.BR objdump "(" 1 ")."

View file

@ -10,6 +10,7 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "nasm.h"
#include "nasmlib.h"
@ -169,6 +170,11 @@ int main(int argc, char **argv) {
}
fp = fopen(filename, "rb");
if (!fp) {
fprintf(stderr, "%s: unable to open `%s': %s\n",
pname, filename, strerror(errno));
return 1;
}
if (initskip > 0)
skip (initskip, fp);

535
outaout.c
View file

@ -16,20 +16,34 @@
#include "nasmlib.h"
#include "outform.h"
#ifdef OF_AOUT
#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 relative; /* TRUE or FALSE */
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 */
};
/*
@ -43,9 +57,12 @@ struct Symbol {
#define SECT_MASK 0xE /* mask out any of the above */
/*
* Another flag used in Symbol.type.
* 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
@ -61,11 +78,10 @@ struct Section {
unsigned long len, size, nrelocs;
long index;
struct Reloc *head, **tail;
struct Symbol *gsyms, *asym;
};
static struct Section stext, sdata;
static unsigned long bsslen;
static long bssindex;
static struct Section stext, sdata, sbss;
static struct SAA *syms;
static unsigned long nsyms;
@ -75,8 +91,14 @@ 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 *);
@ -85,24 +107,73 @@ static void aout_sect_write(struct Section *, unsigned char *, unsigned long);
static void aout_pad_sections(void);
static void aout_fixup_relocs(struct Section *);
static void aout_init(FILE *fp, efunc errfunc, ldfunc ldef) {
/*
* 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 = bsslen = 0;
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();
bssindex = 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(void) {
struct Reloc *r;
@ -143,21 +214,66 @@ static long aout_section_names (char *name, int pass, int *bits) {
else if (!strcmp(name, ".data"))
return sdata.index;
else if (!strcmp(name, ".bss"))
return bssindex;
return sbss.index;
else
return NO_SEG;
}
static void aout_deflabel (char *name, long segment, long offset,
int is_global) {
int is_global, char *special) {
int pos = strslen+4;
struct Symbol *sym;
int special_used = FALSE;
if (name[0] == '.' && name[1] == '.' && name[2] != '@') {
error (ERR_NONFATAL, "unrecognised special symbol `%s'", name);
/*
* 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);
@ -165,34 +281,110 @@ static void aout_deflabel (char *name, long segment, long offset,
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)
else if (segment == stext.index) {
sym->type |= SECT_TEXT;
else if (segment == sdata.index)
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;
else if (segment == bssindex)
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;
else
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;
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);
}
}
}
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 != bssindex)
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 relative, int bytes) {
int reltype, int bytes) {
struct Reloc *r;
r = *sect->tail = nasm_malloc(sizeof(struct Reloc));
@ -203,25 +395,151 @@ static void aout_add_reloc (struct Section *sect, long segment,
r->symbol = (segment == NO_SEG ? -SECT_ABS :
segment == stext.index ? -SECT_TEXT :
segment == sdata.index ? -SECT_DATA :
segment == bssindex ? -SECT_BSS :
segment == sbss.index ? -SECT_BSS :
raa_read(bsym, segment));
r->relative = relative;
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;
}
if (exact) {
/*
* Find a symbol pointing _exactly_ at this one.
*/
for (sym = shead; sym; sym = sym->next)
if (sym->value == offset)
break;
} else {
/*
* Find the nearest symbol below this one.
*/
sym = NULL;
for (sm = shead; sm; sm = sm->next)
if (sm->value <= offset && (!sym || sm->value > sym->value))
sym = sm;
}
if (!sym && exact) {
error (ERR_NONFATAL, "unable to find a suitable global symbol"
" for this reference");
return 0;
}
r = *sect->tail = nasm_malloc(sizeof(struct Reloc));
sect->tail = &r->next;
r->next = NULL;
r->address = sect->len;
r->symbol = sym->symnum;
r->reltype = type | RELTYPE_SYMFLAG;
r->bytes = bytes;
sect->nrelocs++;
return offset - sym->value;
}
/*
* This routine deals with ..gotoff relocations. These _must_ refer
* to a symbol, due to a perversity of *BSD's PIC implementation,
* and it must be a non-global one as well; so we store `asym', the
* first nonglobal symbol defined in each section, and always work
* from that. Relocation type is always RELTYPE_GOTOFF.
*
* Return value is the adjusted value of `addr', having become an
* offset from the `asym' symbol rather than the section.
*/
static long aout_add_gotoff_reloc (struct Section *sect, long segment,
long offset, int bytes) {
struct Reloc *r;
struct Symbol *asym;
/*
* First look up the segment to find whether it's text, data,
* bss or an external symbol.
*/
asym = NULL;
if (segment == stext.index)
asym = stext.asym;
else if (segment == sdata.index)
asym = sdata.asym;
else if (segment == sbss.index)
asym = sbss.asym;
if (!asym)
error (ERR_NONFATAL, "`..gotoff' relocations require a non-global"
" symbol in the section");
r = *sect->tail = nasm_malloc(sizeof(struct Reloc));
sect->tail = &r->next;
r->next = NULL;
r->address = sect->len;
r->symbol = asym->symnum;
r->reltype = RELTYPE_GOTOFF;
r->bytes = bytes;
sect->nrelocs++;
return offset - asym->value;
}
static void aout_out (long segto, void *data, unsigned long type,
long segment, long wrt) {
struct Section *s;
long realbytes = type & OUT_SIZMASK;
long addr;
unsigned char mydata[4], *p;
if (wrt != NO_SEG) {
wrt = NO_SEG; /* continue to do _something_ */
error (ERR_NONFATAL, "WRT not supported by a.out output format");
}
type &= OUT_TYPMASK;
/*
@ -238,7 +556,7 @@ static void aout_out (long segto, void *data, unsigned long type,
s = &stext;
else if (segto == sdata.index)
s = &sdata;
else if (segto == bssindex)
else if (segto == sbss.index)
s = NULL;
else {
error(ERR_WARNING, "attempt to assemble code in"
@ -253,7 +571,7 @@ static void aout_out (long segto, void *data, unsigned long type,
realbytes = 2;
else if (type == OUT_REL4ADR)
realbytes = 4;
bsslen += realbytes;
sbss.len += realbytes;
return;
}
@ -264,24 +582,55 @@ static void aout_out (long segto, void *data, unsigned long type,
(segto == stext.index ? "code" : "data"));
aout_sect_write (s, NULL, realbytes);
} else
bsslen += realbytes;
sbss.len += realbytes;
} else if (type == OUT_RAWDATA) {
if (segment != NO_SEG)
error(ERR_PANIC, "OUT_RAWDATA with other than NO_SEG");
aout_sect_write (s, data, realbytes);
} else if (type == OUT_ADDRESS) {
addr = *(long *)data;
if (segment != NO_SEG) {
if (segment % 2) {
error(ERR_NONFATAL, "a.out format does not support"
" segment base references");
} else
aout_add_reloc (s, segment, FALSE, realbytes);
} else {
if (wrt == NO_SEG) {
aout_add_reloc (s, segment, RELTYPE_ABSOLUTE, realbytes);
} else if (!bsd) {
error (ERR_NONFATAL, "Linux a.out format does not support"
" any use of WRT");
wrt = NO_SEG; /* we can at least _try_ to continue */
} else if (wrt == aout_gotpc_sect+1) {
is_pic = 0x40;
aout_add_reloc (s, segment, RELTYPE_GOTPC, realbytes);
} else if (wrt == aout_gotoff_sect+1) {
is_pic = 0x40;
addr = aout_add_gotoff_reloc (s, segment,
addr, realbytes);
} else if (wrt == aout_got_sect+1) {
is_pic = 0x40;
addr = aout_add_gsym_reloc (s, segment, addr, RELTYPE_GOT,
realbytes, TRUE);
} else if (wrt == aout_sym_sect+1) {
addr = aout_add_gsym_reloc (s, segment, addr,
RELTYPE_ABSOLUTE, realbytes,
FALSE);
} else if (wrt == aout_plt_sect+1) {
is_pic = 0x40;
error(ERR_NONFATAL, "a.out format cannot produce non-PC-"
"relative PLT references");
} else {
error (ERR_NONFATAL, "a.out format does not support this"
" use of WRT");
wrt = NO_SEG; /* we can at least _try_ to continue */
}
}
}
p = mydata;
if (realbytes == 2)
WRITESHORT (p, *(long *)data);
WRITESHORT (p, addr);
else
WRITELONG (p, *(long *)data);
WRITELONG (p, addr);
aout_sect_write (s, mydata, realbytes);
} else if (type == OUT_REL2ADR) {
if (segment == segto)
@ -289,8 +638,27 @@ static void aout_out (long segto, void *data, unsigned long type,
if (segment != NO_SEG && segment % 2) {
error(ERR_NONFATAL, "a.out format does not support"
" segment base references");
} else
aout_add_reloc (s, segment, TRUE, 2);
} else {
if (wrt == NO_SEG) {
aout_add_reloc (s, segment, RELTYPE_RELATIVE, 2);
} else if (!bsd) {
error (ERR_NONFATAL, "Linux a.out format does not support"
" any use of WRT");
wrt = NO_SEG; /* we can at least _try_ to continue */
} else if (wrt == aout_plt_sect+1) {
is_pic = 0x40;
aout_add_reloc (s, segment, RELTYPE_PLT, 2);
} else if (wrt == aout_gotpc_sect+1 ||
wrt == aout_gotoff_sect+1 ||
wrt == aout_got_sect+1) {
error(ERR_NONFATAL, "a.out format cannot produce PC-"
"relative GOT references");
} else {
error (ERR_NONFATAL, "a.out format does not support this"
" use of WRT");
wrt = NO_SEG; /* we can at least _try_ to continue */
}
}
p = mydata;
WRITESHORT (p, *(long*)data-(realbytes + s->len));
aout_sect_write (s, mydata, 2L);
@ -300,8 +668,27 @@ static void aout_out (long segto, void *data, unsigned long type,
if (segment != NO_SEG && segment % 2) {
error(ERR_NONFATAL, "a.out format does not support"
" segment base references");
} else
aout_add_reloc (s, segment, TRUE, 4);
} else {
if (wrt == NO_SEG) {
aout_add_reloc (s, segment, RELTYPE_RELATIVE, 4);
} else if (!bsd) {
error (ERR_NONFATAL, "Linux a.out format does not support"
" any use of WRT");
wrt = NO_SEG; /* we can at least _try_ to continue */
} else if (wrt == aout_plt_sect+1) {
is_pic = 0x40;
aout_add_reloc (s, segment, RELTYPE_PLT, 4);
} else if (wrt == aout_gotpc_sect+1 ||
wrt == aout_gotoff_sect+1 ||
wrt == aout_got_sect+1) {
error(ERR_NONFATAL, "a.out format cannot produce PC-"
"relative GOT references");
} else {
error (ERR_NONFATAL, "a.out format does not support this"
" use of WRT");
wrt = NO_SEG; /* we can at least _try_ to continue */
}
}
p = mydata;
WRITELONG (p, *(long*)data-(realbytes + s->len));
aout_sect_write (s, mydata, 4L);
@ -315,9 +702,9 @@ static void aout_pad_sections(void) {
* length is a multiple of four. (NOP == 0x90.) Also increase
* the length of the BSS section similarly.
*/
aout_sect_write (&stext, pad, (-stext.len) & 3);
aout_sect_write (&sdata, pad, (-sdata.len) & 3);
bsslen = (bsslen + 3) & ~3;
aout_sect_write (&stext, pad, (-(long)stext.len) & 3);
aout_sect_write (&sdata, pad, (-(long)sdata.len) & 3);
sbss.len = (sbss.len + 3) & ~3;
}
/*
@ -340,10 +727,12 @@ static void aout_fixup_relocs(struct Section *sect) {
saa_fread (sect->data, r->address, blk, (long)r->bytes);
p = q = blk;
l = *p++;
l += ((long)*p++) << 8;
if (r->bytes == 4) {
l += ((long)*p++) << 16;
l += ((long)*p++) << 24;
if (r->bytes > 1) {
l += ((long)*p++) << 8;
if (r->bytes == 4) {
l += ((long)*p++) << 16;
l += ((long)*p++) << 24;
}
}
if (r->symbol == -SECT_DATA)
l += stext.len;
@ -351,8 +740,10 @@ static void aout_fixup_relocs(struct Section *sect) {
l += stext.len + sdata.len;
if (r->bytes == 4)
WRITELONG(q, l);
else
else if (r->bytes == 2)
WRITESHORT(q, l);
else
*q++ = l & 0xFF;
saa_fwrite (sect->data, r->address, blk, (long)r->bytes);
}
}
@ -361,10 +752,11 @@ static void aout_write(void) {
/*
* Emit the a.out header.
*/
fwritelong (0x640107L, aoutfp); /* OMAGIC, M_386, no flags */
/* OMAGIC, M_386 or MID_I386, no flags */
fwritelong (bsd ? 0x07018600 | is_pic : 0x640107L, aoutfp);
fwritelong (stext.len, aoutfp);
fwritelong (sdata.len, aoutfp);
fwritelong (bsslen, aoutfp);
fwritelong (sbss.len, aoutfp);
fwritelong (nsyms * 12, aoutfp); /* length of symbol table */
fwritelong (0L, aoutfp); /* object files have no entry point */
fwritelong (stext.nrelocs * 8, aoutfp); /* size of text relocs */
@ -401,12 +793,12 @@ static void aout_write_relocs (struct Reloc *r) {
fwritelong (r->address, aoutfp);
if (r->symbol >= 0)
word2 = r->symbol | 0x8000000L;
word2 = r->symbol;
else
word2 = -r->symbol;
if (r->relative)
word2 |= 0x1000000L;
word2 |= (r->bytes == 2 ? 0x2000000L : 0x4000000L);
word2 |= r->reltype << 24;
word2 |= (r->bytes == 1 ? 0 :
r->bytes == 2 ? 0x2000000L : 0x4000000L);
fwritelong (word2, aoutfp);
r = r->next;
@ -420,7 +812,7 @@ static void aout_write_syms (void) {
for (i=0; i<nsyms; i++) {
struct Symbol *sym = saa_rstruct(syms);
fwritelong (sym->strpos, aoutfp);
fwritelong ((long)sym->type, aoutfp);
fwritelong ((long)sym->type & ~SYM_WITH_SIZE, aoutfp);
/*
* Fix up the symbol value now we know the final section
* sizes.
@ -430,6 +822,15 @@ static void aout_write_syms (void) {
if ((sym->type & SECT_MASK) == SECT_BSS)
sym->value += stext.len + sdata.len;
fwritelong (sym->value, aoutfp);
/*
* Output a size record if necessary.
*/
if (sym->type & SYM_WITH_SIZE) {
fwritelong(sym->strpos, aoutfp);
fwritelong(0x0DL, aoutfp); /* special value: means size */
fwritelong(sym->size, aoutfp);
i++; /* use up another of `nsyms' */
}
}
}
@ -451,9 +852,19 @@ static void aout_filename (char *inname, char *outname, efunc error) {
standard_extension (inname, outname, ".o", error);
}
static char *aout_stdmac[] = {
"%define __SECT__ [section .text]",
NULL
};
#endif /* OF_AOUT || OF_AOUTB */
#ifdef OF_AOUT
struct ofmt of_aout = {
"GNU a.out (i386) object files (e.g. Linux)",
"Linux a.out object files",
"aout",
aout_stdmac,
aout_init,
aout_out,
aout_deflabel,
@ -464,4 +875,22 @@ struct ofmt of_aout = {
aout_cleanup
};
#endif /* OF_AOUT */
#endif
#ifdef OF_AOUTB
struct ofmt of_aoutb = {
"NetBSD/FreeBSD a.out object files",
"aoutb",
aout_stdmac,
aoutb_init,
aout_out,
aout_deflabel,
aout_section_names,
aout_segbase,
aout_directive,
aout_filename,
aout_cleanup
};
#endif

View file

@ -80,7 +80,7 @@ static void as86_write_section (struct Section *, int);
static int as86_add_string (char *name);
static void as86_sect_write(struct Section *, unsigned char *, unsigned long);
static void as86_init(FILE *fp, efunc errfunc, ldfunc ldef) {
static void as86_init(FILE *fp, efunc errfunc, ldfunc ldef, evalfunc eval) {
as86fp = fp;
error = errfunc;
(void) ldef; /* placate optimisers */
@ -158,9 +158,13 @@ static int as86_add_string (char *name) {
}
static void as86_deflabel (char *name, long segment, long offset,
int is_global) {
int is_global, char *special) {
struct Symbol *sym;
if (special)
error (ERR_NONFATAL, "as86 format does not support any"
" special symbol types");
if (name[0] == '.' && name[1] == '.' && name[2] != '@') {
error (ERR_NONFATAL, "unrecognised special symbol `%s'", name);
return;
@ -429,7 +433,7 @@ static void as86_write(void) {
static void as86_set_rsize (int size) {
if (as86_reloc_size != size) {
switch (as86_reloc_size = size) {
case 1: fputc (0x01, as86fp); break; /* shouldn't happen */
case 1: fputc (0x01, as86fp); break;
case 2: fputc (0x02, as86fp); break;
case 4: fputc (0x03, as86fp); break;
default: error (ERR_PANIC, "bizarre relocation size %d", size);
@ -533,9 +537,15 @@ static void as86_filename (char *inname, char *outname, efunc error) {
standard_extension (inname, outname, ".o", error);
}
static char *as86_stdmac[] = {
"%define __SECT__ [section .text]",
NULL
};
struct ofmt of_as86 = {
"Linux as86 (bin86 version 0.3) object files",
"as86",
as86_stdmac,
as86_init,
as86_out,
as86_deflabel,

View file

@ -10,6 +10,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "nasm.h"
#include "nasmlib.h"
#include "outform.h"
@ -35,6 +37,8 @@ static struct Reloc {
struct Section *target;
} *relocs, **reloctail;
static long data_align, bss_align;
static long start_point;
static void add_reloc (struct Section *s, long bytes, long secref,
@ -51,7 +55,7 @@ static void add_reloc (struct Section *s, long bytes, long secref,
r->target = s;
}
static void bin_init (FILE *afp, efunc errfunc, ldfunc ldef) {
static void bin_init (FILE *afp, efunc errfunc, ldfunc ldef, evalfunc eval) {
fp = afp;
error = errfunc;
@ -67,20 +71,22 @@ static void bin_init (FILE *afp, efunc errfunc, ldfunc ldef) {
bssindex = seg_alloc();
relocs = NULL;
reloctail = &relocs;
data_align = bss_align = 4;
}
static void bin_cleanup (void) {
struct Reloc *r;
long datapos, dataalign, bsspos;
long datapos, datagap, bsspos;
datapos = (start_point + textsect.length + 3) & ~3;/* align on 4 bytes */
dataalign = datapos - (start_point + textsect.length);
datapos = start_point + textsect.length;
datapos = (datapos + data_align-1) & ~(data_align-1);
datagap = datapos - (start_point + textsect.length);
bsspos = datapos + datasect.length;
bsspos = (bsspos + bss_align-1) & ~(bss_align-1);
saa_rewind (textsect.contents);
saa_rewind (datasect.contents);
bsspos = (datapos + datasect.length + 3) & ~3;
for (r = relocs; r; r = r->next) {
unsigned char *p, *q, mydata[4];
long l;
@ -88,10 +94,12 @@ static void bin_cleanup (void) {
saa_fread (r->target->contents, r->posn, mydata, r->bytes);
p = q = mydata;
l = *p++;
l += ((long)*p++) << 8;
if (r->bytes == 4) {
l += ((long)*p++) << 16;
l += ((long)*p++) << 24;
if (r->bytes > 1) {
l += ((long)*p++) << 8;
if (r->bytes == 4) {
l += ((long)*p++) << 16;
l += ((long)*p++) << 24;
}
}
if (r->secref == textsect.index)
@ -110,13 +118,16 @@ static void bin_cleanup (void) {
if (r->bytes == 4)
WRITELONG(q, l);
else
else if (r->bytes == 2)
WRITESHORT(q, l);
else
*q++ = l & 0xFF;
saa_fwrite (r->target->contents, r->posn, mydata, r->bytes);
}
saa_fpwrite (textsect.contents, fp);
if (datasect.length > 0) {
fwrite ("\0\0\0\0", dataalign, 1, fp);
while (datagap--)
fputc('\0', fp);
saa_fpwrite (datasect.contents, fp);
}
fclose (fp);
@ -240,7 +251,12 @@ static void bin_out (long segto, void *data, unsigned long type,
}
static void bin_deflabel (char *name, long segment, long offset,
int is_global) {
int is_global, char *special) {
if (special)
error (ERR_NONFATAL, "binary format does not support any"
" special symbol types");
if (name[0] == '.' && name[1] == '.' && name[2] != '@') {
error (ERR_NONFATAL, "unrecognised special symbol `%s'", name);
return;
@ -253,6 +269,10 @@ static void bin_deflabel (char *name, long segment, long offset,
}
static long bin_secname (char *name, int pass, int *bits) {
int sec_index;
long *sec_align;
char *p;
/*
* Default is 16 bits.
*/
@ -262,14 +282,40 @@ static long bin_secname (char *name, int pass, int *bits) {
if (!name)
return textsect.index;
if (!strcmp(name, ".text"))
return textsect.index;
else if (!strcmp(name, ".data"))
return datasect.index;
else if (!strcmp(name, ".bss"))
return bssindex;
else
p = name;
while (*p && !isspace(*p)) p++;
if (*p) *p++ = '\0';
if (!strcmp(name, ".text")) {
sec_index = textsect.index;
sec_align = NULL;
} else if (!strcmp(name, ".data")) {
sec_index = datasect.index;
sec_align = &data_align;
} else if (!strcmp(name, ".bss")) {
sec_index = bssindex;
sec_align = &bss_align;
} else
return NO_SEG;
if (*p) {
if (!nasm_strnicmp(p,"align=",6)) {
if (sec_align == NULL)
error(ERR_NONFATAL, "cannot specify an alignment to"
" the `.text' section");
else if (p[6+strspn(p+6,"0123456789")])
error(ERR_NONFATAL, "argument to `align' is not numeric");
else {
unsigned int align = atoi(p+6);
if (!align || ((align-1) & align))
error(ERR_NONFATAL, "argument to `align' is not a"
" power of two");
else
*sec_align = align;
}
}
}
return sec_index;
}
static long bin_segbase (long segment) {
@ -292,9 +338,18 @@ static void bin_filename (char *inname, char *outname, efunc error) {
standard_extension (inname, outname, "", error);
}
static char *bin_stdmac[] = {
"%define __SECT__ [section .text]",
"%imacro org 1+.nolist",
"[org %1]",
"%endmacro",
NULL
};
struct ofmt of_bin = {
"flat-form binary files (e.g. DOS .COM, .SYS)",
"bin",
bin_stdmac,
bin_init,
bin_out,
bin_deflabel,

View file

@ -37,14 +37,11 @@
* (2) Win32 doesn't bother putting any flags in the header flags
* field (at offset 0x12 into the file).
*
* (3) Win32 puts some weird flags into the section header table.
* It uses flags 0x80000000 (writable), 0x40000000 (readable) and
* 0x20000000 (executable) in the expected combinations, which
* standard COFF doesn't seem to bother with, but it also does
* something else strange: it also flags code sections as
* 0x00500000 and data/bss as 0x00300000. Even Microsoft's
* documentation doesn't explain what these things mean. I just go
* ahead and use them anyway - it seems to work.
* (3) Win32 uses some extra flags into the section header table:
* it defines flags 0x80000000 (writable), 0x40000000 (readable)
* and 0x20000000 (executable), and uses them in the expected
* combinations. It also defines 0x00100000 through 0x00700000 for
* section alignments of 1 through 64 bytes.
*
* (4) Both standard COFF and Win32 COFF seem to use the DWORD
* field directly after the section name in the section header
@ -53,8 +50,7 @@
* to end starting at zero. Dunno why. Microsoft's documentation
* lists this field as "Virtual Size of Section", which doesn't
* seem to fit at all. In fact, Win32 even includes non-linked
* sections such as .drectve in this calculation. Not that I can be
* bothered with those things anyway.
* sections such as .drectve in this calculation.
*
* (5) Standard COFF does something very strange to common
* variables: the relocation point for a common variable is as far
@ -131,13 +127,15 @@ static void coff_section_header (char *, long, long, long, long, int, long);
static void coff_write_relocs (struct Section *);
static void coff_write_symbols (void);
static void coff_win32_init(FILE *fp, efunc errfunc, ldfunc ldef) {
static void coff_win32_init(FILE *fp, efunc errfunc,
ldfunc ldef, evalfunc eval) {
win32 = TRUE;
(void) ldef; /* placate optimisers */
coff_gen_init(fp, errfunc);
}
static void coff_std_init(FILE *fp, efunc errfunc, ldfunc ldef) {
static void coff_std_init(FILE *fp, efunc errfunc,
ldfunc ldef, evalfunc eval) {
win32 = FALSE;
(void) ldef; /* placate optimisers */
coff_gen_init(fp, errfunc);
@ -209,7 +207,7 @@ static int coff_make_section (char *name, unsigned long flags) {
static long coff_section_names (char *name, int pass, int *bits) {
char *p;
unsigned long flags;
unsigned long flags, align_and = ~0L, align_or = 0L;
int i;
/*
@ -224,7 +222,7 @@ static long coff_section_names (char *name, int pass, int *bits) {
p = name;
while (*p && !isspace(*p)) p++;
if (*p) *p++ = '\0';
if (strlen(p) > 8) {
if (strlen(name) > 8) {
error (ERR_WARNING, "COFF section names limited to 8 characters:"
" truncating");
p[8] = '\0';
@ -237,7 +235,7 @@ static long coff_section_names (char *name, int pass, int *bits) {
while (*p && !isspace(*p)) p++;
if (*p) *p++ = '\0';
while (*p && isspace(*p)) p++;
if (!nasm_stricmp(q, "code") || !nasm_stricmp(q, "text")) {
flags = TEXT_FLAGS;
} else if (!nasm_stricmp(q, "data")) {
@ -252,6 +250,32 @@ static long coff_section_names (char *name, int pass, int *bits) {
error (ERR_NONFATAL, "standard COFF does not support"
" informational sections");
}
} else if (!nasm_strnicmp(q,"align=",6)) {
if (!win32)
error (ERR_NONFATAL, "standard COFF does not support"
" section alignment specification");
else {
if (q[6+strspn(q+6,"0123456789")])
error(ERR_NONFATAL, "argument to `align' is not numeric");
else {
unsigned int align = atoi(q+6);
if (!align || ((align-1) & align))
error(ERR_NONFATAL, "argument to `align' is not a"
" power of two");
else if (align > 64)
error(ERR_NONFATAL, "Win32 cannot align sections"
" to better than 64-byte boundaries");
else {
align_and = ~0x00F00000L;
align_or = (align == 1 ? 0x00100000L :
align == 2 ? 0x00200000L :
align == 4 ? 0x00300000L :
align == 8 ? 0x00400000L :
align == 16 ? 0x00500000L :
align == 32 ? 0x00600000L : 0x00700000L);
}
}
}
}
}
@ -259,18 +283,19 @@ static long coff_section_names (char *name, int pass, int *bits) {
if (!strcmp(name, sects[i]->name))
break;
if (i == nsects) {
if (!strcmp(name, ".text") && !flags)
i = coff_make_section (name, TEXT_FLAGS);
else if (!strcmp(name, ".data") && !flags)
i = coff_make_section (name, DATA_FLAGS);
else if (!strcmp(name, ".bss") && !flags)
i = coff_make_section (name, BSS_FLAGS);
else if (flags)
i = coff_make_section (name, flags);
else
i = coff_make_section (name, TEXT_FLAGS);
if (!flags) {
if (!strcmp(name, ".data"))
flags = DATA_FLAGS;
else if (!strcmp(name, ".bss"))
flags = BSS_FLAGS;
else
flags = TEXT_FLAGS;
}
i = coff_make_section (name, flags);
if (flags)
sects[i]->flags = flags;
sects[i]->flags &= align_and;
sects[i]->flags |= align_or;
} else if (pass == 1) {
if (flags)
error (ERR_WARNING, "section attributes ignored on"
@ -281,10 +306,14 @@ static long coff_section_names (char *name, int pass, int *bits) {
}
static void coff_deflabel (char *name, long segment, long offset,
int is_global) {
int is_global, char *special) {
int pos = strslen+4;
struct Symbol *sym;
if (special)
error (ERR_NONFATAL, "binary format does not support any"
" special symbol types");
if (name[0] == '.' && name[1] == '.' && name[2] != '@') {
error (ERR_NONFATAL, "unrecognised special symbol `%s'", name);
return;
@ -430,8 +459,8 @@ static void coff_out (long segto, void *data, unsigned long type,
error(ERR_PANIC, "OUT_RAWDATA with other than NO_SEG");
coff_sect_write (s, data, realbytes);
} else if (type == OUT_ADDRESS) {
if (realbytes == 2 && (segment != NO_SEG || wrt != NO_SEG))
error(ERR_NONFATAL, "COFF format does not support 16-bit"
if (realbytes != 4 && (segment != NO_SEG || wrt != NO_SEG))
error(ERR_NONFATAL, "COFF format does not support non-32-bit"
" relocations");
else {
long fix = 0;
@ -661,11 +690,17 @@ static void coff_win32_filename (char *inname, char *outname, efunc error) {
#endif /* defined(OF_COFF) || defined(OF_WIN32) */
static char *coff_stdmac[] = {
"%define __SECT__ [section .text]",
NULL
};
#ifdef OF_COFF
struct ofmt of_coff = {
"COFF (i386) object files (e.g. DJGPP for DOS)",
"coff",
coff_stdmac,
coff_std_init,
coff_out,
coff_deflabel,
@ -683,6 +718,7 @@ struct ofmt of_coff = {
struct ofmt of_win32 = {
"Microsoft Win32 (i386) object files",
"win32",
coff_stdmac,
coff_win32_init,
coff_out,
coff_deflabel,

152
outdbg.c
View file

@ -18,30 +18,39 @@
#ifdef OF_DBG
struct Section {
struct Section *next;
long number;
char *name;
} *dbgsect;
FILE *dbgf;
efunc dbgef;
int segcode,segdata,segbss;
static void dbg_init(FILE *fp, efunc errfunc, ldfunc ldef)
static void dbg_init(FILE *fp, efunc errfunc, ldfunc ldef, evalfunc eval)
{
dbgf = fp;
dbgef = errfunc;
(void) ldef;
segcode = seg_alloc();
segdata = seg_alloc();
segbss = seg_alloc();
fprintf(fp,"NASM Output format debug dump - code=%d,data=%d,bss=%d\n",
segcode,segdata,segbss);
dbgf = fp;
dbgef = errfunc;
dbgsect = NULL;
(void) ldef;
fprintf(fp,"NASM Output format debug dump\n");
}
static void dbg_cleanup(void)
{
fclose(dbgf);
while (dbgsect) {
struct Section *tmp = dbgsect;
dbgsect = dbgsect->next;
nasm_free (tmp->name);
nasm_free (tmp);
}
fclose(dbgf);
}
static long dbg_section_names (char *name, int pass, int *bits)
{
int seg;
/*
* We must have an initial default: let's make it 16.
*/
@ -49,73 +58,91 @@ static long dbg_section_names (char *name, int pass, int *bits)
*bits = 16;
if (!name)
return 0;
fprintf(dbgf, "section_name on init: returning %d\n",
seg = seg_alloc());
else {
int n = strcspn(name, " \t");
char *sname = nasm_strndup(name, n);
struct Section *s;
if (!strcmp(name, ".text"))
return segcode;
else if (!strcmp(name, ".data"))
return segdata;
else if (!strcmp(name, ".bss"))
return segbss;
else
return NO_SEG;
seg = NO_SEG;
for (s = dbgsect; s; s = s->next)
if (!strcmp(s->name, sname))
seg = s->number;
if (seg == NO_SEG) {
s = nasm_malloc(sizeof(*s));
s->name = sname;
s->number = seg = seg_alloc();
s->next = dbgsect;
dbgsect = s;
fprintf(dbgf, "section_name %s (pass %d): returning %d\n",
name, pass, seg);
}
}
return seg;
}
static void dbg_deflabel (char *name, long segment, long offset,
int is_global) {
fprintf(dbgf,"deflabel %s := %08lx:%08lx %s (%d)\n",name,segment,offset,
is_global ? "global" : "local", is_global);
int is_global, char *special) {
fprintf(dbgf,"deflabel %s := %08lx:%08lx %s (%d)%s%s\n",
name, segment, offset,
is_global == 2 ? "common" : is_global ? "global" : "local",
is_global,
special ? ": " : "", special);
}
static void dbg_out (long segto, void *data, unsigned long type,
long segment, long wrt) {
long realbytes = type & OUT_SIZMASK;
long ldata;
int id;
long segment, long wrt) {
long realbytes = type & OUT_SIZMASK;
long ldata;
int id;
type &= OUT_TYPMASK;
type &= OUT_TYPMASK;
fprintf(dbgf,"out to %lx, len = %ld: ",segto,realbytes);
fprintf(dbgf,"out to %lx, len = %ld: ",segto,realbytes);
switch(type) {
case OUT_RESERVE:
fprintf(dbgf,"reserved.\n"); break;
case OUT_RAWDATA:
fprintf(dbgf,"raw data = ");
while (realbytes--) {
id = *(unsigned char *)data;
data = (char *)data + 1;
fprintf(dbgf,"%02x ",id);
switch(type) {
case OUT_RESERVE:
fprintf(dbgf,"reserved.\n"); break;
case OUT_RAWDATA:
fprintf(dbgf,"raw data = ");
while (realbytes--) {
id = *(unsigned char *)data;
data = (char *)data + 1;
fprintf(dbgf,"%02x ",id);
}
fprintf(dbgf,"\n"); break;
case OUT_ADDRESS:
ldata = 0; /* placate gcc */
if (realbytes == 1)
ldata = *((char *)data);
else if (realbytes == 2)
ldata = *((short *)data);
else if (realbytes == 4)
ldata = *((long *)data);
fprintf(dbgf,"addr %08lx (seg %08lx, wrt %08lx)\n",ldata,
segment,wrt);break;
case OUT_REL2ADR:
fprintf(dbgf,"rel2adr %04x (seg %08lx)\n",(int)*(short *)data,segment);
break;
case OUT_REL4ADR:
fprintf(dbgf,"rel4adr %08lx (seg %08lx)\n",*(long *)data,segment);
break;
default:
fprintf(dbgf,"unknown\n");
break;
}
fprintf(dbgf,"\n"); break;
case OUT_ADDRESS:
ldata = 0; /* placate gcc */
if (realbytes == 1)
ldata = *((char *)data);
else if (realbytes == 2)
ldata = *((short *)data);
else if (realbytes == 4)
ldata = *((long *)data);
fprintf(dbgf,"addr %08lx (seg %08lx, wrt %08lx)\n",ldata,
segment,wrt);break;
case OUT_REL2ADR:
fprintf(dbgf,"rel2adr %04x (seg %08lx)\n",(int)*(short *)data,segment);
break;
case OUT_REL4ADR:
fprintf(dbgf,"rel4adr %08lx (seg %08lx)\n",*(long *)data,segment);
break;
default:
fprintf(dbgf,"unknown\n");
break;
}
}
static long dbg_segbase(long segment) {
return segment;
return segment;
}
static int dbg_directive (char *directive, char *value, int pass) {
return 0;
fprintf(dbgf, "directive [%s] value [%s] (pass %d)\n",
directive, value, pass);
return 1;
}
static void dbg_filename (char *inname, char *outname, efunc error) {
@ -125,6 +152,7 @@ static void dbg_filename (char *inname, char *outname, efunc error) {
struct ofmt of_dbg = {
"Trace of all info passed to output stage",
"dbg",
NULL,
dbg_init,
dbg_out,
dbg_deflabel,

363
outelf.c
View file

@ -18,18 +18,33 @@
#ifdef OF_ELF
/*
* Relocation types.
*/
#define R_386_32 1 /* ordinary absolute relocation */
#define R_386_PC32 2 /* PC-relative relocation */
#define R_386_GOT32 3 /* an offset into GOT */
#define R_386_PLT32 4 /* a PC-relative offset into PLT */
#define R_386_GOTOFF 9 /* an offset from GOT base */
#define R_386_GOTPC 10 /* a PC-relative offset _to_ GOT */
struct Reloc {
struct Reloc *next;
long address; /* relative to _start_ of section */
long symbol; /* ELF symbol info thingy */
int relative; /* TRUE or FALSE */
int type; /* type of relocation */
};
struct Symbol {
long strpos; /* string table position of name */
long section; /* section ID of the symbol */
int type; /* TRUE or FALSE */
long value; /* address, or COMMON variable size */
int type; /* symbol type */
long value; /* address, or COMMON variable align */
long size; /* size of symbol */
long globnum; /* symbol table offset if global */
struct Symbol *next; /* list of globals in each section */
struct Symbol *nextfwd; /* list of unresolved-size symbols */
char *name; /* used temporarily if in above list */
};
#define SHT_PROGBITS 1
@ -50,6 +65,7 @@ struct Section {
struct SAA *rel;
long rellen;
struct Reloc *head, **tail;
struct Symbol *gsyms; /* global symbols in section */
};
#define SECT_DELTA 32
@ -72,15 +88,22 @@ static unsigned long strslen;
static FILE *elffp;
static efunc error;
static evalfunc evaluate;
static struct Symbol *fwds;
static char elf_module[FILENAME_MAX];
extern struct ofmt of_elf;
#define SHN_ABS 0xFFF1
#define SHN_COMMON 0xFFF2
#define SHN_UNDEF 0
#define SYM_SECTION 0x04
#define SYM_GLOBAL 0x10
#define SYM_DATA 0x01
#define SYM_FUNCTION 0x02
#define GLOBAL_TEMP_BASE 6 /* bigger than any constant sym id */
@ -107,9 +130,19 @@ static struct SAA *elf_build_symtab (long *, long *);
static struct SAA *elf_build_reltab (long *, struct Reloc *);
static void add_sectname (char *, char *);
static void elf_init(FILE *fp, efunc errfunc, ldfunc ldef) {
/*
* Special section numbers which are used to define ELF special
* symbols, which can be used with WRT to provide PIC relocation
* types.
*/
static long elf_gotpc_sect, elf_gotoff_sect;
static long elf_got_sect, elf_plt_sect;
static long elf_sym_sect;
static void elf_init(FILE *fp, efunc errfunc, ldfunc ldef, evalfunc eval) {
elffp = fp;
error = errfunc;
evaluate = eval;
(void) ldef; /* placate optimisers */
sects = NULL;
nsects = sectlen = 0;
@ -123,6 +156,20 @@ static void elf_init(FILE *fp, efunc errfunc, ldfunc ldef) {
shstrtab = NULL;
shstrtablen = shstrtabsize = 0;;
add_sectname ("", "");
fwds = NULL;
elf_gotpc_sect = seg_alloc();
ldef("..gotpc", elf_gotpc_sect+1, 0L, NULL, FALSE, FALSE, &of_elf, error);
elf_gotoff_sect = seg_alloc();
ldef("..gotoff", elf_gotoff_sect+1, 0L, NULL, FALSE, FALSE,&of_elf,error);
elf_got_sect = seg_alloc();
ldef("..got", elf_got_sect+1, 0L, NULL, FALSE, FALSE, &of_elf, error);
elf_plt_sect = seg_alloc();
ldef("..plt", elf_plt_sect+1, 0L, NULL, FALSE, FALSE, &of_elf, error);
elf_sym_sect = seg_alloc();
ldef("..sym", elf_sym_sect+1, 0L, NULL, FALSE, FALSE, &of_elf, error);
def_seg = seg_alloc();
}
@ -179,6 +226,7 @@ static int elf_make_section (char *name, int type, int flags, int align) {
s->type = type;
s->flags = flags;
s->align = align;
s->gsyms = NULL;
if (nsects >= sectlen)
sects = nasm_realloc (sects, (sectlen += SECT_DELTA)*sizeof(*sects));
@ -286,15 +334,60 @@ static long elf_section_names (char *name, int pass, int *bits) {
}
static void elf_deflabel (char *name, long segment, long offset,
int is_global) {
int is_global, char *special) {
int pos = strslen;
struct Symbol *sym;
int special_used = FALSE;
if (name[0] == '.' && name[1] == '.' && name[2] != '@') {
error (ERR_NONFATAL, "unrecognised special symbol `%s'", name);
/*
* This is a NASM special symbol. We never allow it into
* the ELF 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);
@ -302,6 +395,7 @@ static void elf_deflabel (char *name, long segment, long offset,
sym->strpos = pos;
sym->type = is_global ? SYM_GLOBAL : 0;
sym->size = 0;
if (segment == NO_SEG)
sym->section = SHN_ABS;
else {
@ -322,21 +416,96 @@ static void elf_deflabel (char *name, long segment, long offset,
}
if (is_global == 2) {
sym->value = offset;
sym->size = offset;
sym->value = 0;
sym->section = SHN_COMMON;
/*
* We have a common variable. Check the special text to see
* if it's a valid number and power of two; if so, store it
* as the alignment for the common variable.
*/
if (special) {
int err;
sym->value = readnum (special, &err);
if (err)
error(ERR_NONFATAL, "alignment constraint `%s' is not a"
" valid number", special);
else if ( (sym->value | (sym->value-1)) != 2*sym->value - 1)
error(ERR_NONFATAL, "alignment constraint `%s' is not a"
" power of two", special);
}
special_used = TRUE;
} else
sym->value = (sym->section == SHN_UNDEF ? 0 : offset);
if (sym->type == SYM_GLOBAL) {
if (sym->section == SHN_UNDEF || sym->section == SHN_COMMON)
bsym = raa_write (bsym, segment, nglobs);
else {
/*
* This is a global symbol; so we must add it to the linked
* list of global symbols in its section. We'll push it on
* the beginning of the list, because it doesn't matter
* much which end we put it on and it's easier like this.
*
* In addition, we check the special text for symbol
* type and size information.
*/
sym->next = sects[sym->section-1]->gsyms;
sects[sym->section-1]->gsyms = sym;
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;
while (special[n] && isspace(special[n]))
n++;
/*
* We have a size expression; attempt to
* evaluate it.
*/
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);
}
}
special_used = TRUE;
}
}
sym->globnum = nglobs;
nglobs++;
} else
nlocals++;
if (special && !special_used)
error(ERR_NONFATAL, "no special symbol features supported here");
}
static void elf_add_reloc (struct Section *sect, long segment,
int relative) {
int type) {
struct Reloc *r;
r = *sect->tail = nasm_malloc(sizeof(struct Reloc));
@ -355,23 +524,106 @@ static void elf_add_reloc (struct Section *sect, long segment,
if (!r->symbol)
r->symbol = GLOBAL_TEMP_BASE + raa_read(bsym, segment);
}
r->relative = relative;
r->type = type;
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.
*
* R_386_GOT32 references require the _exact_ symbol address to be
* used; R_386_32 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 elf_add_gsym_reloc (struct Section *sect,
long segment, long offset,
int type, int exact) {
struct Reloc *r;
struct Section *s;
struct Symbol *sym, *sm;
int i;
/*
* First look up the segment/offset pair and find a global
* symbol corresponding to it. If it's not one of our segments,
* then it must be an external symbol, in which case we're fine
* doing a normal elf_add_reloc after first sanity-checking
* that the offset from the symbol is zero.
*/
s = NULL;
for (i=0; i<nsects; i++)
if (segment == sects[i]->index) {
s = sects[i];
break;
}
if (!s) {
if (exact && offset != 0)
error (ERR_NONFATAL, "unable to find a suitable global symbol"
" for this reference");
else
elf_add_reloc (sect, segment, type);
return offset;
}
if (exact) {
/*
* Find a symbol pointing _exactly_ at this one.
*/
for (sym = s->gsyms; sym; sym = sym->next)
if (sym->value == offset)
break;
} else {
/*
* Find the nearest symbol below this one.
*/
sym = NULL;
for (sm = s->gsyms; sm; sm = sm->next)
if (sm->value <= offset && (!sym || sm->value > sym->value))
sym = sm;
}
if (!sym && exact) {
error (ERR_NONFATAL, "unable to find a suitable global symbol"
" for this reference");
return 0;
}
r = *sect->tail = nasm_malloc(sizeof(struct Reloc));
sect->tail = &r->next;
r->next = NULL;
r->address = sect->len;
r->symbol = GLOBAL_TEMP_BASE + sym->globnum;
r->type = type;
sect->nrelocs++;
return offset - sym->value;
}
static void elf_out (long segto, void *data, unsigned long type,
long segment, long wrt) {
struct Section *s;
long realbytes = type & OUT_SIZMASK;
long addr;
unsigned char mydata[4], *p;
int i;
if (wrt != NO_SEG) {
wrt = NO_SEG; /* continue to do _something_ */
error (ERR_NONFATAL, "WRT not supported by ELF output format");
}
type &= OUT_TYPMASK;
/*
@ -421,20 +673,45 @@ static void elf_out (long segto, void *data, unsigned long type,
error(ERR_PANIC, "OUT_RAWDATA with other than NO_SEG");
elf_sect_write (s, data, realbytes);
} else if (type == OUT_ADDRESS) {
if (wrt != NO_SEG)
error(ERR_NONFATAL, "ELF format does not support WRT types");
addr = *(long *)data;
if (segment != NO_SEG) {
if (segment % 2) {
error(ERR_NONFATAL, "ELF format does not support"
" segment base references");
} else
elf_add_reloc (s, segment, FALSE);
} else {
if (wrt == NO_SEG) {
elf_add_reloc (s, segment, R_386_32);
} else if (wrt == elf_gotpc_sect+1) {
/*
* The user will supply GOT relative to $$. ELF
* will let us have GOT relative to $. So we
* need to fix up the data item by $-$$.
*/
addr += s->len;
elf_add_reloc (s, segment, R_386_GOTPC);
} else if (wrt == elf_gotoff_sect+1) {
elf_add_reloc (s, segment, R_386_GOTOFF);
} else if (wrt == elf_got_sect+1) {
addr = elf_add_gsym_reloc (s, segment, addr,
R_386_GOT32, TRUE);
} else if (wrt == elf_sym_sect+1) {
addr = elf_add_gsym_reloc (s, segment, addr,
R_386_32, FALSE);
} else if (wrt == elf_plt_sect+1) {
error(ERR_NONFATAL, "ELF format cannot produce non-PC-"
"relative PLT references");
} else {
error (ERR_NONFATAL, "ELF format does not support this"
" use of WRT");
wrt = NO_SEG; /* we can at least _try_ to continue */
}
}
}
p = mydata;
if (realbytes == 2 && segment != NO_SEG)
error (ERR_NONFATAL, "ELF format does not support 16-bit"
if (realbytes != 4 && segment != NO_SEG)
error (ERR_NONFATAL, "ELF format does not support non-32-bit"
" relocations");
WRITELONG (p, *(long *)data);
WRITELONG (p, addr);
elf_sect_write (s, mydata, realbytes);
} else if (type == OUT_REL2ADR) {
error (ERR_NONFATAL, "ELF format does not support 16-bit"
@ -445,8 +722,22 @@ static void elf_out (long segto, void *data, unsigned long type,
if (segment != NO_SEG && segment % 2) {
error(ERR_NONFATAL, "ELF format does not support"
" segment base references");
} else
elf_add_reloc (s, segment, TRUE);
} else {
if (wrt == NO_SEG) {
elf_add_reloc (s, segment, R_386_PC32);
} else if (wrt == elf_plt_sect+1) {
elf_add_reloc (s, segment, R_386_PLT32);
} else if (wrt == elf_gotpc_sect+1 ||
wrt == elf_gotoff_sect+1 ||
wrt == elf_got_sect+1) {
error(ERR_NONFATAL, "ELF format cannot produce PC-"
"relative GOT references");
} else {
error (ERR_NONFATAL, "ELF format does not support this"
" use of WRT");
wrt = NO_SEG; /* we can at least _try_ to continue */
}
}
p = mydata;
WRITELONG (p, *(long*)data - realbytes);
elf_sect_write (s, mydata, 4L);
@ -614,16 +905,13 @@ static struct SAA *elf_build_symtab (long *len, long *local) {
*/
saa_rewind (syms);
while ( (sym = saa_rstruct (syms)) ) {
if (sym->type == SYM_GLOBAL)
if (sym->type & SYM_GLOBAL)
continue;
p = entry;
WRITELONG (p, sym->strpos);
WRITELONG (p, sym->value);
if (sym->section == SHN_COMMON)
WRITELONG (p, sym->value);
else
WRITELONG (p, 0);
WRITESHORT (p, 0); /* local non-typed thing */
WRITELONG (p, sym->size);
WRITESHORT (p, sym->type); /* local non-typed thing */
WRITESHORT (p, sym->section);
saa_wbytes (s, entry, 16L);
*len += 16;
@ -635,16 +923,13 @@ static struct SAA *elf_build_symtab (long *len, long *local) {
*/
saa_rewind (syms);
while ( (sym = saa_rstruct (syms)) ) {
if (sym->type != SYM_GLOBAL)
if (!(sym->type & SYM_GLOBAL))
continue;
p = entry;
WRITELONG (p, sym->strpos);
WRITELONG (p, sym->value);
if (sym->section == SHN_COMMON)
WRITELONG (p, sym->value);
else
WRITELONG (p, 0);
WRITESHORT (p, SYM_GLOBAL); /* global non-typed thing */
WRITELONG (p, sym->size);
WRITESHORT (p, sym->type); /* global non-typed thing */
WRITESHORT (p, sym->section);
saa_wbytes (s, entry, 16L);
*len += 16;
@ -671,7 +956,7 @@ static struct SAA *elf_build_reltab (long *len, struct Reloc *r) {
p = entry;
WRITELONG (p, r->address);
WRITELONG (p, (sym << 8) + (r->relative ? 2 : 1));
WRITELONG (p, (sym << 8) + r->type);
saa_wbytes (s, entry, 8L);
*len += 8;
@ -737,9 +1022,15 @@ static void elf_filename (char *inname, char *outname, efunc error) {
standard_extension (inname, outname, ".o", error);
}
static char *elf_stdmac[] = {
"%define __SECT__ [section .text]",
NULL
};
struct ofmt of_elf = {
"ELF32 (i386) object files (e.g. Linux)",
"elf",
elf_stdmac,
elf_init,
elf_out,
elf_deflabel,

View file

@ -17,8 +17,8 @@
* OF_name -- ensure that output format 'name' is included
* OF_NO_name -- remove output format 'name'
* OF_DOS -- ensure that 'obj', 'bin' & 'win32' are included.
* OF_UNIX -- ensure that 'aout', 'coff' and 'elf' are in.
* OF_OTHERS -- ensure that 'bin', 'as86', 'os2' & 'rdf' are in.
* OF_UNIX -- ensure that 'aout', 'aoutb', 'coff', 'elf' are in.
* OF_OTHERS -- ensure that 'bin', 'as86' & 'rdf' are in.
* OF_ALL -- ensure that all formats are included.
*
* OF_DEFAULT=of_name -- ensure that 'name' is the default format.
@ -60,7 +60,7 @@ void ofmt_register (struct ofmt *);
/* ====configurable info begins here==== */
/* formats configurable:
* bin,obj,elf,aout,coff,win32,as86,rdf */
* bin,obj,elf,aout,aoutb,coff,win32,as86,rdf */
/* process options... */
@ -77,9 +77,6 @@ void ofmt_register (struct ofmt *);
#ifndef OF_OBJ
#define OF_OBJ
#endif
#ifndef OF_OS2
#define OF_OS2
#endif
#ifndef OF_ELF
#define OF_ELF
#endif
@ -89,6 +86,9 @@ void ofmt_register (struct ofmt *);
#ifndef OF_AOUT
#define OF_AOUT
#endif
#ifndef OF_AOUTB
#define OF_AOUTB
#endif
#ifndef OF_WIN32
#define OF_WIN32
#endif
@ -117,6 +117,9 @@ void ofmt_register (struct ofmt *);
#ifndef OF_AOUT
#define OF_AOUT
#endif
#ifndef OF_AOUTB
#define OF_AOUTB
#endif
#ifndef OF_COFF
#define OF_COFF
#endif
@ -135,9 +138,6 @@ void ofmt_register (struct ofmt *);
#ifndef OF_RDF
#define OF_RDF
#endif
#ifndef OF_OS2
#define OF_OS2
#endif
#endif
/* finally... override any format specifically specifed to be off */
@ -153,6 +153,9 @@ void ofmt_register (struct ofmt *);
#ifdef OF_NO_AOUT
#undef OF_AOUT
#endif
#ifdef OF_NO_AOUTB
#undef OF_AOUTB
#endif
#ifdef OF_NO_COFF
#undef OF_COFF
#endif
@ -165,9 +168,6 @@ void ofmt_register (struct ofmt *);
#ifdef OF_NO_RDF
#undef OF_RDF
#endif
#ifdef OF_NO_OS2
#undef OF_OS2
#endif
#ifndef OF_DEFAULT
#define OF_DEFAULT of_bin

648
outobj.c
View file

@ -22,6 +22,7 @@ static char obj_infile[FILENAME_MAX];
static int obj_uppercase;
static efunc error;
static evalfunc evaluate;
static ldfunc deflabel;
static FILE *ofp;
static long first_seg;
@ -35,6 +36,9 @@ static int any_segs;
static unsigned char record[RECORD_MAX], *recptr;
struct Segment; /* need to know these structs exist */
struct Group;
static struct Public {
struct Public *next;
char *name;
@ -46,13 +50,27 @@ static struct External {
struct External *next;
char *name;
long commonsize;
} *exthead, **exttail;
long commonelem; /* element size if FAR, else zero */
int index; /* OBJ-file external index */
enum {
DEFWRT_NONE, /* no unusual default-WRT */
DEFWRT_STRING, /* a string we don't yet understand */
DEFWRT_SEGMENT, /* a segment */
DEFWRT_GROUP /* a group */
} defwrt_type;
union {
char *string;
struct Segment *seg;
struct Group *grp;
} defwrt_ptr;
struct External *next_dws; /* next with DEFWRT_STRING */
} *exthead, **exttail, *dws;
static int externals;
static struct ExtBack {
struct ExtBack *next;
int index[EXT_BLKSIZ];
struct External *exts[EXT_BLKSIZ];
} *ebhead, **ebtail;
static struct Segment {
@ -85,7 +103,7 @@ static struct Group {
long index;
char *name;
} segs[GROUP_MAX]; /* ...in this */
} *grphead, **grptail, *obj_grp_needs_update, *defgrp;
} *grphead, **grptail, *obj_grp_needs_update;
static struct ObjData {
struct ObjData *next;
@ -97,9 +115,28 @@ static struct ObjData {
unsigned char fixupp[RECORD_MAX], *fptr;
} *datahead, *datacurr, **datatail;
static long obj_entry_seg, obj_entry_ofs;
static struct ImpDef {
struct ImpDef *next;
char *extname;
char *libname;
unsigned int impindex;
char *impname;
} *imphead, **imptail;
static int os2;
static struct ExpDef {
struct ExpDef *next;
char *intname;
char *extname;
unsigned int ordinal;
int flags;
} *exphead, **exptail;
#define EXPDEF_FLAG_ORDINAL 0x80
#define EXPDEF_FLAG_RESIDENT 0x40
#define EXPDEF_FLAG_NODATA 0x20
#define EXPDEF_MASK_PARMCNT 0x1F
static long obj_entry_seg, obj_entry_ofs;
enum RecordID { /* record ID codes */
@ -140,9 +177,10 @@ static unsigned char *obj_write_value(unsigned char *, unsigned long);
static void obj_record(int, unsigned char *, unsigned char *);
static int obj_directive (char *, char *, int);
static void obj_init (FILE *fp, efunc errfunc, ldfunc ldef) {
static void obj_init (FILE *fp, efunc errfunc, ldfunc ldef, evalfunc eval) {
ofp = fp;
error = errfunc;
evaluate = eval;
deflabel = ldef;
first_seg = seg_alloc();
any_segs = FALSE;
@ -150,6 +188,11 @@ static void obj_init (FILE *fp, efunc errfunc, ldfunc ldef) {
fpubtail = &fpubhead;
exthead = NULL;
exttail = &exthead;
imphead = NULL;
imptail = &imphead;
exphead = NULL;
exptail = &exphead;
dws = NULL;
externals = 0;
ebhead = NULL;
ebtail = &ebhead;
@ -161,22 +204,6 @@ static void obj_init (FILE *fp, efunc errfunc, ldfunc ldef) {
datatail = &datahead;
obj_entry_seg = NO_SEG;
obj_uppercase = FALSE;
if (os2) {
obj_directive ("group", "FLAT", 1);
defgrp = grphead;
} else
defgrp = NULL;
}
static void dos_init (FILE *fp, efunc errfunc, ldfunc ldef) {
os2 = FALSE;
obj_init (fp, errfunc, ldef);
}
static void os2_init (FILE *fp, efunc errfunc, ldfunc ldef) {
os2 = TRUE;
obj_init (fp, errfunc, ldef);
}
static void obj_cleanup (void) {
@ -188,6 +215,7 @@ static void obj_cleanup (void) {
while (segtmp->pubhead) {
struct Public *pubtmp = segtmp->pubhead;
segtmp->pubhead = pubtmp->next;
nasm_free (pubtmp->name);
nasm_free (pubtmp);
}
nasm_free (segtmp);
@ -195,6 +223,7 @@ static void obj_cleanup (void) {
while (fpubhead) {
struct Public *pubtmp = fpubhead;
fpubhead = fpubhead->next;
nasm_free (pubtmp->name);
nasm_free (pubtmp);
}
while (exthead) {
@ -202,6 +231,21 @@ static void obj_cleanup (void) {
exthead = exthead->next;
nasm_free (exttmp);
}
while (imphead) {
struct ImpDef *imptmp = imphead;
imphead = imphead->next;
nasm_free (imptmp->extname);
nasm_free (imptmp->libname);
nasm_free (imptmp->impname); /* nasm_free won't mind if it's NULL */
nasm_free (imptmp);
}
while (exphead) {
struct ExpDef *exptmp = exphead;
exphead = exphead->next;
nasm_free (exptmp->extname);
nasm_free (exptmp->intname);
nasm_free (exptmp);
}
while (ebhead) {
struct ExtBack *ebtmp = ebhead;
ebhead = ebhead->next;
@ -219,8 +263,34 @@ static void obj_cleanup (void) {
}
}
static void obj_ext_set_defwrt (struct External *ext, char *id) {
struct Segment *seg;
struct Group *grp;
for (seg = seghead; seg; seg = seg->next)
if (!strcmp(seg->name, id)) {
ext->defwrt_type = DEFWRT_SEGMENT;
ext->defwrt_ptr.seg = seg;
nasm_free (id);
return;
}
for (grp = grphead; grp; grp = grp->next)
if (!strcmp(grp->name, id)) {
ext->defwrt_type = DEFWRT_GROUP;
ext->defwrt_ptr.grp = grp;
nasm_free (id);
return;
}
ext->defwrt_type = DEFWRT_STRING;
ext->defwrt_ptr.string = id;
ext->next_dws = dws;
dws = ext;
}
static void obj_deflabel (char *name, long segment,
long offset, int is_global) {
long offset, int is_global, char *special) {
/*
* We have three cases:
*
@ -241,6 +311,13 @@ static void obj_deflabel (char *name, long segment,
struct ExtBack *eb;
struct Segment *seg;
int i;
int used_special = FALSE; /* have we used the special text? */
/*
* If it's a special-retry from pass two, discard it.
*/
if (is_global == 3)
return;
/*
* First check for the double-period, signifying something
@ -278,10 +355,13 @@ static void obj_deflabel (char *name, long segment,
pub = *fpubtail = nasm_malloc(sizeof(*pub));
fpubtail = &pub->next;
pub->next = NULL;
pub->name = name;
pub->name = nasm_strdup(name);
pub->offset = offset;
pub->segment = (segment == NO_SEG ? 0 : segment & ~SEG_ABS);
}
if (special)
error(ERR_NONFATAL, "OBJ supports no special symbol features"
" for this symbol type");
return;
}
@ -306,9 +386,12 @@ static void obj_deflabel (char *name, long segment,
pub = *seg->pubtail = nasm_malloc(sizeof(*pub));
seg->pubtail = &pub->next;
pub->next = NULL;
pub->name = name;
pub->name = nasm_strdup(name);
pub->offset = offset;
}
if (special)
error(ERR_NONFATAL, "OBJ supports no special symbol features"
" for this symbol type");
return;
}
@ -319,11 +402,97 @@ static void obj_deflabel (char *name, long segment,
ext->next = NULL;
exttail = &ext->next;
ext->name = name;
if (is_global == 2)
ext->defwrt_type = DEFWRT_NONE;
if (is_global == 2) {
ext->commonsize = offset;
else
ext->commonelem = 1; /* default FAR */
} else
ext->commonsize = 0;
/*
* Now process the special text, if any, to find default-WRT
* specifications and common-variable element-size and near/far
* specifications.
*/
while (special && *special) {
used_special = TRUE;
/*
* We might have a default-WRT specification.
*/
if (!nasm_strnicmp(special, "wrt", 3)) {
char *p;
int len;
special += 3;
special += strspn(special, " \t");
p = nasm_strndup(special, len = strcspn(special, ":"));
obj_ext_set_defwrt (ext, p);
special += len;
if (*special && *special != ':')
error(ERR_NONFATAL, "`:' expected in special symbol"
" text for `%s'", ext->name);
else if (*special == ':')
special++;
}
/*
* The NEAR or FAR keywords specify nearness or
* farness. FAR gives default element size 1.
*/
if (!nasm_strnicmp(special, "far", 3)) {
if (ext->commonsize)
ext->commonelem = 1;
else
error(ERR_NONFATAL, "`%s': `far' keyword may only be applied"
" to common variables\n", ext->name);
special += 3;
special += strspn(special, " \t");
} else if (!nasm_strnicmp(special, "near", 4)) {
if (ext->commonsize)
ext->commonelem = 0;
else
error(ERR_NONFATAL, "`%s': `far' keyword may only be applied"
" to common variables\n", ext->name);
special += 4;
special += strspn(special, " \t");
}
/*
* If it's a common, and anything else remains on the line
* before a further colon, evaluate it as an expression and
* use that as the element size. Forward references aren't
* allowed.
*/
if (*special == ':')
special++;
else if (*special) {
if (ext->commonsize) {
expr *e;
struct tokenval tokval;
stdscan_reset();
stdscan_bufptr = special;
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 common-variable element size");
else
ext->commonelem = reloc_value(e);
}
special = stdscan_bufptr;
} else {
error (ERR_NONFATAL, "`%s': element-size specifications only"
" apply to common variables", ext->name);
while (*special && *special != ':')
special++;
if (*special == ':')
special++;
}
}
}
i = segment/2;
eb = ebhead;
if (!eb) {
@ -341,7 +510,12 @@ static void obj_deflabel (char *name, long segment,
}
i -= EXT_BLKSIZ;
}
eb->index[i] = ++externals;
eb->exts[i] = ext;
ext->index = ++externals;
if (special && !used_special)
error(ERR_NONFATAL, "OBJ supports no special symbol features"
" for this symbol type");
}
static void obj_out (long segto, void *data, unsigned long type,
@ -400,6 +574,8 @@ static void obj_out (long segto, void *data, unsigned long type,
}
} else if (realtype == OUT_ADDRESS || realtype == OUT_REL2ADR ||
realtype == OUT_REL4ADR) {
int rsize;
if (segment == NO_SEG && realtype != OUT_ADDRESS)
error(ERR_NONFATAL, "relative call to absolute address not"
" supported by OBJ format");
@ -407,10 +583,14 @@ static void obj_out (long segto, void *data, unsigned long type,
error(ERR_NONFATAL, "far-absolute relocations not supported"
" by OBJ format");
ldata = *(long *)data;
if (realtype == OUT_REL2ADR)
if (realtype == OUT_REL2ADR) {
ldata += (size-2);
if (realtype == OUT_REL4ADR)
size = 2;
}
if (realtype == OUT_REL4ADR) {
ldata += (size-4);
size = 4;
}
if (obj_ledata_space(seg) < 4 || !obj_fixup_free(seg))
obj_ledata_new(seg);
if (size == 2)
@ -418,8 +598,22 @@ static void obj_out (long segto, void *data, unsigned long type,
else
datacurr->lptr = obj_write_dword (datacurr->lptr, ldata);
datacurr->nonempty = TRUE;
rsize = size;
if (segment < SEG_ABS && segment % 2 && size == 4) {
/*
* This is a 4-byte segment-base relocation such as
* `MOV EAX,SEG foo'. OBJ format can't actually handle
* these, but if the constant term has the 16 low bits
* zero, we can just apply a 2-byte segment-base
* relocation to the low word instead.
*/
rsize = 2;
if (ldata & 0xFFFF)
error(ERR_NONFATAL, "OBJ format cannot handle complex"
" dword-size segment base references");
}
if (segment != NO_SEG)
obj_write_fixup (datacurr, size,
obj_write_fixup (datacurr, rsize,
(realtype == OUT_REL2ADR ||
realtype == OUT_REL4ADR ? 0 : 0x4000),
segment, wrt,
@ -479,6 +673,13 @@ static void obj_write_fixup (struct ObjData *data, int bytes,
long tidx, fidx;
struct Segment *s = NULL;
struct Group *g = NULL;
struct External *e = NULL;
if (bytes == 1) {
error(ERR_NONFATAL, "`obj' output driver does not support"
" one-byte relocations");
return;
}
locat = 0x8000 | segrel | offset;
if (seg % 2) {
@ -486,8 +687,8 @@ static void obj_write_fixup (struct ObjData *data, int bytes,
locat |= 0x800;
seg--;
if (bytes != 2)
error(ERR_NONFATAL, "OBJ format can only handle 2-byte"
" segment base references");
error(ERR_PANIC, "OBJ: 4-byte segment base fixup got"
" through sanity check");
} else {
base = FALSE;
if (bytes == 2)
@ -527,7 +728,7 @@ static void obj_write_fixup (struct ObjData *data, int bytes,
i -= EXT_BLKSIZ;
}
if (eb)
method = 6, tidx = eb->index[i];
method = 6, e = eb->exts[i], tidx = e->index;
else
error(ERR_PANIC,
"unrecognised segment value in obj_write_fixup");
@ -536,17 +737,28 @@ static void obj_write_fixup (struct ObjData *data, int bytes,
/*
* If no WRT given, assume the natural default, which is method
* F5 unless we are doing an OFFSET fixup for a grouped
* segment, in which case we require F1 (group). Oh, and in
* OS/2 mode we're in F1 (group) on `defgrp' _always_, by
* default.
* F5 unless:
*
* - we are doing an OFFSET fixup for a grouped segment, in
* which case we require F1 (group).
*
* - we are doing an OFFSET fixup for an external with a
* default WRT, in which case we must honour the default WRT.
*/
if (wrt == NO_SEG) {
if (os2)
method |= 0x10, fidx = defgrp->obj_index;
else if (!base && s && s->grp)
if (!base && s && s->grp)
method |= 0x10, fidx = s->grp->obj_index;
else
else if (!base && e && e->defwrt_type != DEFWRT_NONE) {
if (e->defwrt_type == DEFWRT_SEGMENT)
method |= 0x00, fidx = e->defwrt_ptr.seg->obj_index;
else if (e->defwrt_type == DEFWRT_GROUP)
method |= 0x10, fidx = e->defwrt_ptr.grp->obj_index;
else {
error(ERR_NONFATAL, "default WRT specification for"
" external `%s' unresolved", e->name);
method |= 0x50, fidx = -1; /* got to do _something_ */
}
} else
method |= 0x50, fidx = -1;
} else {
/*
@ -575,7 +787,7 @@ static void obj_write_fixup (struct ObjData *data, int bytes,
i -= EXT_BLKSIZ;
}
if (eb)
method |= 0x20, fidx = eb->index[i];
method |= 0x20, fidx = eb->exts[i]->index;
else
error(ERR_PANIC,
"unrecognised WRT value in obj_write_fixup");
@ -603,6 +815,7 @@ static long obj_segment (char *name, int pass, int *bits) {
} else {
struct Segment *seg;
struct Group *grp;
struct External **extp;
int obj_idx, i, attrs, rn_error;
char *p;
@ -686,7 +899,32 @@ static long obj_segment (char *name, int pass, int *bits) {
seg->use32 = FALSE;
else if (!nasm_stricmp(p, "use32"))
seg->use32 = TRUE;
else if (!nasm_strnicmp(p, "class=", 6))
else if (!nasm_stricmp(p, "flat")) {
/*
* This segment is an OS/2 FLAT segment. That means
* that its default group is group FLAT, even if
* the group FLAT does not explicitly _contain_ the
* segment.
*
* When we see this, we must create the group
* `FLAT', containing no segments, if it does not
* already exist; then we must set the default
* group of this segment to be the FLAT group.
*/
struct Group *grp;
for (grp = grphead; grp; grp = grp->next)
if (!strcmp(grp->name, "FLAT"))
break;
if (!grp) {
obj_directive ("group", "FLAT", 1);
for (grp = grphead; grp; grp = grp->next)
if (!strcmp(grp->name, "FLAT"))
break;
if (!grp)
error (ERR_PANIC, "failure to define FLAT?!");
}
seg->grp = grp;
} else if (!nasm_strnicmp(p, "class=", 6))
seg->segclass = nasm_strdup(p+6);
else if (!nasm_strnicmp(p, "overlay=", 8))
seg->overlay = nasm_strdup(p+8);
@ -703,6 +941,7 @@ static long obj_segment (char *name, int pass, int *bits) {
case 4: /* DWORD */
case 16: /* PARA */
case 256: /* PAGE */
case 4096: /* PharLap extension */
break;
case 8:
error(ERR_WARNING, "OBJ format does not support alignment"
@ -716,6 +955,13 @@ static long obj_segment (char *name, int pass, int *bits) {
" of %d: rounding up to 256", seg->align);
seg->align = 256;
break;
case 512:
case 1024:
case 2048:
error(ERR_WARNING, "OBJ format does not support alignment"
" of %d: rounding up to 4096", seg->align);
seg->align = 4096;
break;
default:
error(ERR_NONFATAL, "invalid alignment value %d",
seg->align);
@ -732,9 +978,11 @@ static long obj_segment (char *name, int pass, int *bits) {
obj_seg_needs_update = seg;
if (seg->align >= SEG_ABS)
deflabel (name, NO_SEG, seg->align - SEG_ABS, &of_obj, error);
deflabel (name, NO_SEG, seg->align - SEG_ABS,
NULL, FALSE, FALSE, &of_obj, error);
else
deflabel (name, seg->index+1, 0L, &of_obj, error);
deflabel (name, seg->index+1, 0L,
NULL, FALSE, FALSE, &of_obj, error);
obj_seg_needs_update = NULL;
/*
@ -756,6 +1004,22 @@ static long obj_segment (char *name, int pass, int *bits) {
}
}
/*
* Walk through the list of externals with unresolved
* default-WRT clauses, and resolve any that point at this
* segment.
*/
extp = &dws;
while (*extp) {
if ((*extp)->defwrt_type == DEFWRT_STRING &&
!strcmp((*extp)->defwrt_ptr.string, seg->name)) {
(*extp)->defwrt_type = DEFWRT_SEGMENT;
(*extp)->defwrt_ptr.seg = seg;
*extp = (*extp)->next_dws;
} else
extp = &(*extp)->next_dws;
}
if (seg->use32)
*bits = 32;
else
@ -770,6 +1034,7 @@ static int obj_directive (char *directive, char *value, int pass) {
if (pass == 1) {
struct Group *grp;
struct Segment *seg;
struct External **extp;
int obj_idx;
q = value;
@ -813,7 +1078,8 @@ static int obj_directive (char *directive, char *value, int pass) {
grp->name = NULL;
obj_grp_needs_update = grp;
deflabel (v, grp->index+1, 0L, &of_obj, error);
deflabel (v, grp->index+1, 0L,
NULL, FALSE, FALSE, &of_obj, error);
obj_grp_needs_update = NULL;
while (*q) {
@ -852,6 +1118,22 @@ static int obj_directive (char *directive, char *value, int pass) {
grp->segs[grp->nentries++].name = nasm_strdup(p);
}
}
/*
* Walk through the list of externals with unresolved
* default-WRT clauses, and resolve any that point at
* this group.
*/
extp = &dws;
while (*extp) {
if ((*extp)->defwrt_type == DEFWRT_STRING &&
!strcmp((*extp)->defwrt_ptr.string, grp->name)) {
(*extp)->defwrt_type = DEFWRT_GROUP;
(*extp)->defwrt_ptr.grp = grp;
*extp = (*extp)->next_dws;
} else
extp = &(*extp)->next_dws;
}
}
return 1;
}
@ -859,6 +1141,129 @@ static int obj_directive (char *directive, char *value, int pass) {
obj_uppercase = TRUE;
return 1;
}
if (!strcmp(directive, "import")) {
char *q, *extname, *libname, *impname;
if (pass == 2)
return 1; /* ignore in pass two */
extname = q = value;
while (*q && !isspace(*q))
q++;
if (isspace(*q)) {
*q++ = '\0';
while (*q && isspace(*q))
q++;
}
libname = q;
while (*q && !isspace(*q))
q++;
if (isspace(*q)) {
*q++ = '\0';
while (*q && isspace(*q))
q++;
}
impname = q;
if (!*extname || !*libname)
error(ERR_NONFATAL, "`import' directive requires symbol name"
" and library name");
else {
struct ImpDef *imp;
int err = FALSE;
imp = *imptail = nasm_malloc(sizeof(struct ImpDef));
imptail = &imp->next;
imp->next = NULL;
imp->extname = nasm_strdup(extname);
imp->libname = nasm_strdup(libname);
imp->impindex = readnum(impname, &err);
if (!*impname || err)
imp->impname = nasm_strdup(impname);
else
imp->impname = NULL;
}
return 1;
}
if (!strcmp(directive, "export")) {
char *q, *extname, *intname, *v;
struct ExpDef *export;
int flags = 0;
unsigned int ordinal = 0;
if (pass == 2)
return 1; /* ignore in pass two */
intname = q = value;
while (*q && !isspace(*q))
q++;
if (isspace(*q)) {
*q++ = '\0';
while (*q && isspace(*q))
q++;
}
extname = q;
while (*q && !isspace(*q))
q++;
if (isspace(*q)) {
*q++ = '\0';
while (*q && isspace(*q))
q++;
}
if (!*intname) {
error(ERR_NONFATAL, "`export' directive requires export name");
return 1;
}
if (!*extname) {
extname = intname;
intname = "";
}
while (*q) {
v = q;
while (*q && !isspace(*q))
q++;
if (isspace(*q)) {
*q++ = '\0';
while (*q && isspace(*q))
q++;
}
if (!nasm_stricmp(v, "resident"))
flags |= EXPDEF_FLAG_RESIDENT;
else if (!nasm_stricmp(v, "nodata"))
flags |= EXPDEF_FLAG_NODATA;
else if (!nasm_strnicmp(v, "parm=", 5)) {
int err = FALSE;
flags |= EXPDEF_MASK_PARMCNT & readnum(v+5, &err);
if (err) {
error(ERR_NONFATAL,
"value `%s' for `parm' is non-numeric", v+5);
return 1;
}
} else {
int err = FALSE;
ordinal = readnum(v, &err);
if (err) {
error(ERR_NONFATAL, "unrecognised export qualifier `%s'",
v);
return 1;
}
flags |= EXPDEF_FLAG_ORDINAL;
}
}
export = *exptail = nasm_malloc(sizeof(struct ExpDef));
exptail = &export->next;
export->next = NULL;
export->extname = nasm_strdup(extname);
export->intname = nasm_strdup(intname);
export->ordinal = ordinal;
export->flags = flags;
return 1;
}
return 0;
}
@ -872,8 +1277,35 @@ static long obj_segbase (long segment) {
if (seg->index == segment-1)
break;
if (!seg)
if (!seg) {
/*
* Might be an external with a default WRT.
*/
long i = segment/2;
struct ExtBack *eb = ebhead;
struct External *e;
while (i > EXT_BLKSIZ) {
if (eb)
eb = eb->next;
else
break;
i -= EXT_BLKSIZ;
}
if (eb) {
e = eb->exts[i];
if (e->defwrt_type == DEFWRT_NONE)
return segment; /* fine */
else if (e->defwrt_type == DEFWRT_SEGMENT)
return e->defwrt_ptr.seg->index+1;
else if (e->defwrt_type == DEFWRT_GROUP)
return e->defwrt_ptr.grp->index+1;
else if (e->defwrt_type == DEFWRT_STRING)
return NO_SEG; /* can't tell what it is */
}
return segment; /* not one of ours - leave it alone */
}
if (seg->align >= SEG_ABS)
return seg->align; /* absolute segment */
@ -894,6 +1326,8 @@ static void obj_write_file (void) {
struct Public *pub;
struct External *ext;
struct ObjData *data;
struct ImpDef *imp;
struct ExpDef *export;
static char boast[] = "The Netwide Assembler " NASM_VER;
int lname_idx, rectype;
@ -912,6 +1346,41 @@ static void obj_write_file (void) {
recptr = obj_write_name (recptr, boast);
obj_record (COMENT, record, recptr);
/*
* Write the IMPDEF records, if any.
*/
for (imp = imphead; imp; imp = imp->next) {
recptr = record;
recptr = obj_write_rword (recptr, 0xA0); /* comment class A0 */
recptr = obj_write_byte (recptr, 1); /* subfunction 1: IMPDEF */
if (imp->impname)
recptr = obj_write_byte (recptr, 0); /* import by name */
else
recptr = obj_write_byte (recptr, 1); /* import by ordinal */
recptr = obj_write_name (recptr, imp->extname);
recptr = obj_write_name (recptr, imp->libname);
if (imp->impname)
recptr = obj_write_name (recptr, imp->impname);
else
recptr = obj_write_word (recptr, imp->impindex);
obj_record (COMENT, record, recptr);
}
/*
* Write the EXPDEF records, if any.
*/
for (export = exphead; export; export = export->next) {
recptr = record;
recptr = obj_write_rword (recptr, 0xA0); /* comment class A0 */
recptr = obj_write_byte (recptr, 2); /* subfunction 1: EXPDEF */
recptr = obj_write_byte (recptr, export->flags);
recptr = obj_write_name (recptr, export->extname);
recptr = obj_write_name (recptr, export->intname);
if (export->flags & EXPDEF_FLAG_ORDINAL)
recptr = obj_write_word (recptr, export->ordinal);
obj_record (COMENT, record, recptr);
}
/*
* Write the first LNAMES record, containing LNAME one, which
* is null. Also initialise the LNAME counter.
@ -961,10 +1430,12 @@ static void obj_write_file (void) {
/* A field */
if (seg->align >= SEG_ABS)
acbp |= 0x00;
else if (seg->align >= 256) {
if (seg->align > 256)
else if (seg->align >= 4096) {
if (seg->align > 4096)
error(ERR_NONFATAL, "segment `%s' requires more alignment"
" than OBJ format supports", seg->name);
acbp |= 0xC0; /* PharLap extension */
} else if (seg->align >= 256) {
acbp |= 0x80;
} else if (seg->align >= 16) {
acbp |= 0x60;
@ -1000,11 +1471,11 @@ static void obj_write_file (void) {
*/
recptr = record;
for (grp = grphead; grp; grp = grp->next) {
recptr = obj_write_name (recptr, grp->name);
if (recptr - record > 1024) {
if (recptr - record + strlen(grp->name)+2 > 1024) {
obj_record (LNAMES, record, recptr);
recptr = record;
}
recptr = obj_write_name (recptr, grp->name);
}
if (recptr > record)
obj_record (LNAMES, record, recptr);
@ -1083,28 +1554,29 @@ static void obj_write_file (void) {
recptr = record;
for (ext = exthead; ext; ext = ext->next) {
if (ext->commonsize == 0) {
recptr = obj_write_name (recptr, ext->name);
recptr = obj_write_index (recptr, 0);
if (recptr - record > 1024) {
/* dj@delorie.com: check for buffer overrun before we overrun it */
if (recptr - record + strlen(ext->name)+2 > RECORD_MAX) {
obj_record (EXTDEF, record, recptr);
recptr = record;
}
recptr = obj_write_name (recptr, ext->name);
recptr = obj_write_index (recptr, 0);
} else {
if (recptr > record)
obj_record (EXTDEF, record, recptr);
recptr = record;
if (ext->commonsize > 0) {
if (ext->commonsize) {
recptr = obj_write_name (recptr, ext->name);
recptr = obj_write_index (recptr, 0);
recptr = obj_write_byte (recptr, 0x61);/* far communal */
recptr = obj_write_value (recptr, 1L);
recptr = obj_write_value (recptr, ext->commonsize);
obj_record (COMDEF, record, recptr);
} else if (ext->commonsize < 0) {
recptr = obj_write_name (recptr, ext->name);
recptr = obj_write_index (recptr, 0);
recptr = obj_write_byte (recptr, 0x62);/* near communal */
recptr = obj_write_value (recptr, ext->commonsize);
if (ext->commonelem) {
recptr = obj_write_byte (recptr, 0x61);/* far communal */
recptr = obj_write_value (recptr, (ext->commonsize /
ext->commonelem));
recptr = obj_write_value (recptr, ext->commonelem);
} else {
recptr = obj_write_byte (recptr, 0x62);/* near communal */
recptr = obj_write_value (recptr, ext->commonsize);
}
obj_record (COMDEF, record, recptr);
}
recptr = record;
@ -1115,12 +1587,12 @@ static void obj_write_file (void) {
/*
* Write a COMENT record stating that the linker's first pass
* may stop processing at this point. Exception is if we're in
* OS/2 mode and our MODEND record specifies a start point, in
* which case, according to the OS/2 documentation, this COMENT
* should be omitted.
* may stop processing at this point. Exception is if our
* MODEND record specifies a start point, in which case,
* according to some variants of the documentation, this COMENT
* should be omitted. So we'll omit it just in case.
*/
if (!os2 || obj_entry_seg == NO_SEG) {
if (obj_entry_seg == NO_SEG) {
recptr = record;
recptr = obj_write_rword (recptr, 0x40A2);
recptr = obj_write_byte (recptr, 1);
@ -1262,13 +1734,31 @@ static void obj_record(int type, unsigned char *start, unsigned char *end) {
fwrite (start, 1, end-start, ofp);
while (start < end)
cksum += *start++;
fputc ( (-cksum) & 0xFF, ofp);
fputc ( (-(long)cksum) & 0xFF, ofp);
}
static char *obj_stdmac[] = {
"%define __SECT__ [section .text]",
"%imacro group 1+.nolist",
"[group %1]",
"%endmacro",
"%imacro uppercase 1+.nolist",
"[uppercase %1]",
"%endmacro",
"%imacro export 1+.nolist",
"[export %1]",
"%endmacro",
"%imacro import 1+.nolist",
"[import %1]",
"%endmacro",
NULL
};
struct ofmt of_obj = {
"Microsoft MS-DOS 16-bit OMF object files",
"obj",
dos_init,
obj_stdmac,
obj_init,
obj_out,
obj_deflabel,
obj_segment,
@ -1277,18 +1767,4 @@ struct ofmt of_obj = {
obj_filename,
obj_cleanup
};
struct ofmt of_os2 = {
"OS/2 object files (variant of OMF)",
"os2",
os2_init,
obj_out,
obj_deflabel,
obj_segment,
obj_segbase,
obj_directive,
obj_filename,
obj_cleanup
};
#endif /* OF_OBJ */

View file

@ -187,7 +187,7 @@ static efunc error;
static int segtext,segdata,segbss;
static long bsslength;
static void rdf_init(FILE *fp, efunc errfunc, ldfunc ldef)
static void rdf_init(FILE *fp, efunc errfunc, ldfunc ldef, evalfunc eval)
{
ofile = fp;
error = errfunc;
@ -261,7 +261,8 @@ static void write_dll_rec(struct DLLRec *r)
membufwrite(header,r->libname,strlen(r->libname) + 1);
}
static void rdf_deflabel(char *name, long segment, long offset, int is_global)
static void rdf_deflabel(char *name, long segment, long offset,
int is_global, char *special)
{
struct ExportRec r;
struct ImportRec ri;
@ -269,28 +270,23 @@ static void rdf_deflabel(char *name, long segment, long offset, int is_global)
static int warned_common = 0;
#endif
if (special)
error (ERR_NONFATAL, "RDOFF format does not support any"
" special symbol types");
if (name[0] == '.' && name[1] == '.' && name[2] != '@') {
error (ERR_NONFATAL, "unrecognised special symbol `%s'", name);
return;
}
if (is_global && segment > 4) {
if (is_global == 2) {
#ifdef VERBOSE_WARNINGS
if (! warned_common) {
error(ERR_WARNING,"common declarations not supported... using extern");
if (!warned_common) {
error(ERR_WARNING,"common declarations not supported: using extern");
warned_common = 1;
}
#endif
is_global = 0;
}
if (is_global) {
r.type = 3;
r.segment = segment;
r.offset = offset;
strncpy(r.label,name,32);
r.label[32] = 0;
write_export_rec(&r);
is_global = 1;
}
if (segment > 4) { /* EXTERN declaration */
@ -299,6 +295,13 @@ static void rdf_deflabel(char *name, long segment, long offset, int is_global)
strncpy(ri.label,name,32);
ri.label[32] = 0;
write_import_rec(&ri);
} else if (is_global) {
r.type = 3;
r.segment = segment;
r.offset = offset;
strncpy(r.label,name,32);
r.label[32] = 0;
write_export_rec(&r);
}
}
@ -484,9 +487,18 @@ static void rdf_filename (char *inname, char *outname, efunc error) {
standard_extension(inname,outname,".rdf",error);
}
static char *rdf_stdmac[] = {
"%define __SECT__ [section .text]",
"%imacro library 1+.nolist",
"[library %1]",
"%endmacro",
NULL
};
struct ofmt of_rdf = {
"Relocatable Dynamic Object File Format v1.1",
"rdf",
rdf_stdmac,
rdf_init,
rdf_out,
rdf_deflabel,

1049
parser.c

File diff suppressed because it is too large Load diff

View file

@ -10,9 +10,8 @@
#ifndef NASM_PARSER_H
#define NASM_PARSER_H
insn *parse_line (long segment, long offset, lfunc lookup_label, int pass,
char *buffer, insn *result, struct ofmt *output,
efunc error);
insn *parse_line (int pass, char *buffer, insn *result,
efunc error, evalfunc evaluate, evalinfofunc einfo);
void cleanup_insn (insn *instruction);
#endif

1328
preproc.c

File diff suppressed because it is too large Load diff

View file

@ -12,6 +12,7 @@
void pp_include_path (char *);
void pp_pre_include (char *);
void pp_pre_define (char *);
void pp_extra_stdmac (char **);
extern Preproc nasmpp;

74
rdoff/Makefile.in Normal file
View file

@ -0,0 +1,74 @@
#
# Auto-configuring Makefile for RDOFF object file utils; part of the
# Netwide Assembler
#
# 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.
top_srcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
mandir = @mandir@
CC = @CC@
CFLAGS = @CFLAGS@ @GCCFLAGS@ -I$(top_srcdir)
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_DATA = @INSTALL_DATA@
LN_S = @LN_S@
LDRDFLIBS = rdoff.o nasmlib.o symtab.o collectn.o rdlib.o
RDXLIBS = rdoff.o rdfload.o symtab.o collectn.o
.c.o:
$(CC) -c $(CFLAGS) $<
all: rdfdump ldrdf rdx rdflib rdf2bin rdf2com
rdfdump: rdfdump.o
$(CC) -o rdfdump rdfdump.o
ldrdf: ldrdf.o $(LDRDFLIBS)
$(CC) -o ldrdf ldrdf.o $(LDRDFLIBS)
rdx: rdx.o $(RDXLIBS)
$(CC) -o rdx rdx.o $(RDXLIBS)
rdflib: rdflib.o
$(CC) -o rdflib rdflib.o
rdf2bin: rdf2bin.o $(RDXLIBS) nasmlib.o
$(CC) -o rdf2bin rdf2bin.o $(RDXLIBS) nasmlib.o
rdf2com:
$(LN_S) rdf2bin rdf2com
rdf2bin.o: rdf2bin.c
rdfdump.o: rdfdump.c
rdoff.o: rdoff.c rdoff.h
ldrdf.o: ldrdf.c rdoff.h $(top_srcdir)/nasmlib.h symtab.h collectn.h rdlib.h
symtab.o: symtab.c symtab.h
collectn.o: collectn.c collectn.h
rdx.o: rdx.c rdoff.h rdfload.h symtab.h
rdfload.o: rdfload.c rdfload.h rdoff.h collectn.h symtab.h
rdlib.o: rdlib.c rdlib.h
rdflib.o: rdflib.c
nasmlib.o: $(top_srcdir)/nasmlib.c
$(CC) -c $(CFLAGS) $(top_srcdir)/nasmlib.c
clean:
rm -f *.o rdfdump ldrdf rdx rdflib rdf2bin rdf2com
spotless: clean
rm -f Makefile
install: rdfdump ldrdf rdx rdflib rdf2bin rdf2com
$(INSTALL_PROGRAM) rdfdump $(bindir)/rdfdump
$(INSTALL_PROGRAM) ldrdf $(bindir)/ldrdf
$(INSTALL_PROGRAM) rdx $(bindir)/rdx
$(INSTALL_PROGRAM) rdflib $(bindir)/rdflib
$(INSTALL_PROGRAM) rdf2bin $(bindir)/rdf2bin
cd $(bindir); $(LN_S) rdf2bin rdf2com

73
rdoff/Makefile.unx Normal file
View file

@ -0,0 +1,73 @@
# Generated automatically from Makefile.in by configure.
#
# Auto-configuring Makefile for RDOFF object file utils; part of the
# Netwide Assembler
#
# 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.
# You may need to adjust these values.
prefix = /usr/local
CC = cc
CFLAGS = -O -I..
# You _shouldn't_ need to adjust anything below this line.
exec_prefix = ${prefix}
bindir = ${exec_prefix}/bin
mandir = ${prefix}/man
INSTALL = /usr/bin/install -c
INSTALL_PROGRAM = ${INSTALL}
INSTALL_DATA = ${INSTALL} -m 644
LN_S = ln -s
LDRDFLIBS = rdoff.o nasmlib.o symtab.o collectn.o rdlib.o
RDXLIBS = rdoff.o rdfload.o symtab.o collectn.o
.c.o:
$(CC) -c $(CFLAGS) $*.c
all: rdfdump ldrdf rdx rdflib rdf2bin rdf2com
rdfdump: rdfdump.o
$(CC) -o rdfdump rdfdump.o
ldrdf: ldrdf.o $(LDRDFLIBS)
$(CC) -o ldrdf ldrdf.o $(LDRDFLIBS)
rdx: rdx.o $(RDXLIBS)
$(CC) -o rdx rdx.o $(RDXLIBS)
rdflib: rdflib.o
$(CC) -o rdflib rdflib.o
rdf2bin: rdf2bin.o $(RDXLIBS) nasmlib.o
$(CC) -o rdf2bin rdf2bin.o $(RDXLIBS) nasmlib.o
rdf2com:
$(LN_S) rdf2bin rdf2com
rdf2bin.o: rdf2bin.c
rdfdump.o: rdfdump.c
rdoff.o: rdoff.c rdoff.h
ldrdf.o: ldrdf.c rdoff.h ../nasmlib.h symtab.h collectn.h rdlib.h
symtab.o: symtab.c symtab.h
collectn.o: collectn.c collectn.h
rdx.o: rdx.c rdoff.h rdfload.h symtab.h
rdfload.o: rdfload.c rdfload.h rdoff.h collectn.h symtab.h
rdlib.o: rdlib.c rdlib.h
rdflib.o: rdflib.c
nasmlib.o: ../nasmlib.c ../nasmlib.h ../names.c ../nasm.h
$(CC) -c $(CFLAGS) ../nasmlib.c
clean:
rm -f *.o rdfdump ldrdf rdx rdflib rdf2bin rdf2com
install: rdfdump ldrdf rdx rdflib rdf2bin rdf2com
$(INSTALL_PROGRAM) rdfdump $(bindir)/rdfdump
$(INSTALL_PROGRAM) ldrdf $(bindir)/ldrdf
$(INSTALL_PROGRAM) rdx $(bindir)/rdx
$(INSTALL_PROGRAM) rdflib $(bindir)/rdflib
$(INSTALL_PROGRAM) rdf2bin $(bindir)/rdf2bin
cd $(bindir); $(LN_S) rdf2bin rdf2com

View file

@ -24,7 +24,6 @@
#include <stdlib.h>
#include <string.h>
#include "nasm.h"
#include "rdoff.h"
#include "nasmlib.h"
#include "symtab.h"
@ -419,8 +418,11 @@ void link_segments(void)
relto = r->r.segment == 0 ? mod->coderel : mod->datarel;
}
else
{
bRelative = 0; /* non-relative - need to relocate
* at load time */
relto = 0; /* placate optimiser warnings */
}
/* calculate absolute offset of reference, not rel to beginning of
segment */

99
rdoff/rdf.doc Normal file
View file

@ -0,0 +1,99 @@
RDOFF: Relocatable Dynamically-linked Object File Format
========================================================
RDOFF was designed initially to test the object-file production
interface to NASM. It soon became apparent that it could be enhanced
for use in serious applications due to its simplicity; code to load
and execute an RDOFF object module is very simple. It also contains
enhancements to allow it to be linked with a dynamic link library at
either run- or load- time, depending on how complex you wish to make
your loader.
The RDOFF format (version 1.1, as produced by NASM v0.91) is defined
as follows:
The first six bytes of the file contain the string 'RDOFF1'. Other
versions of the format may contain other last characters other than
'1' - all little endian versions of the file will always contain an
ASCII character with value greater than 32. If RDOFF is used on a
big-endian machine at some point in the future, the version will be
encoded in decimal rather than ASCII, so will be below 32.
All multi-byte fields follwing this are encoded in either little- or
big-endian format depending on the system described by this version
information. Object files should be encoded in the endianness of
their target machine; files of incorrect endianness will be rejected
by the loader - this means that loaders do not need to convert
endianness, as RDOFF has been designed with simplicity of loading at
the forefront of the design requirements.
The next 4 byte field is the length of the header in bytes. The
header consists of a sequence of variable length records. Each
record's type is identified by the first byte of the record. Record
types 1-4 are currently supported. Record type 5 will be added in
the near future, when I implement BSS segments. Record type 6 may be
to do with debugging, when I get debugging implemented.
Type 1: Relocation
==================
Offset Length Description
0 1 Type (contains 1)
1 1 Segment that contains reference (0 = text, 1 = data)
Add 64 to this number to indicate a relative linkage
to an external symbol (see notes)
2 4 Offset of reference
6 1 Length of reference (1,2 or 4 bytes)
7 2 Segment to which reference is made (0 = text, 1 =
data, 2 = BSS [when implemented]) others are external
symbols.
Total length = 9 bytes
Type 2: Symbol Import
=====================
0 1 Type (2)
1 2 Segment number that will be used in references to this
symbol.
3 ? Null terminated string containing label (up to 32
chars) to match against exports in linkage.
Type 3: Symbol Export
=====================
0 1 Type (3)
1 1 Segment containing object to be exported (0/1/2)
2 4 Offset within segment
6 ? Null terminate string containing label to export (32
char maximum length)
Type 4: Dynamic Link Library
============================
0 1 Type (4)
1 ? Library name (up to 128 chars)
Type 5: Reserve BSS
===================
0 1 Type (5)
1 4 Amount of BSS space to reserve in bytes
Total length: 5 bytes
-----------------------------------------------------------------------------
Following the header is the text (code) segment. This is preceded by
a 4-byte integer, which is its length in bytes. This is followed by
the length of the data segment (also 4 bytes), and finally the data
segment.
Notes
=====
Relative linking: The number stored at the address is offset
required from the imported symbol, with the address of the end of
the instruction subtracted from it. This means that the linker can
simply add the address of the label relative to the beginning of the
current segment to it.

View file

@ -57,7 +57,7 @@ void print_header(long length) {
case 3: /* export record */
fread(&s,1,1,infile);
fread(&o,4,1,infile);
l = 0;
ll = 0;
do {
fread(&buf[ll],1,1,infile);
} while (buf[ll++]);
@ -65,7 +65,7 @@ void print_header(long length) {
length -= ll + 6;
break;
case 4: /* DLL record */
l = 0;
ll = 0;
do {
fread(&buf[ll],1,1,infile);
} while (buf[ll++]);
@ -88,6 +88,7 @@ int main(int argc,char **argv) {
char id[7];
long l;
int verbose = 0;
long offset;
puts("RDOFF Dump utility v1.1 (C) Copyright 1996 Julian R Hall");
@ -133,9 +134,15 @@ int main(int argc,char **argv) {
fread(&l,4,1,infile);
l = translatelong(l);
printf("\nText segment length = %ld bytes\n",l);
offset = 0;
while(l--) {
fread(id,1,1,infile);
if (verbose) printf(" %02x",(int) (unsigned char)id[0]);
if (verbose) {
if (offset % 16 == 0)
printf("\n%08lx ", offset);
printf(" %02x",(int) (unsigned char)id[0]);
offset++;
}
}
if (verbose) printf("\n\n");
@ -145,9 +152,13 @@ int main(int argc,char **argv) {
if (verbose)
{
offset = 0;
while (l--) {
fread(id,1,1,infile);
printf(" %02x",(int) (unsigned char) id[0]);
if (offset % 16 == 0)
printf("\n%08lx ", offset);
printf(" %02x",(int) (unsigned char) id[0]);
offset++;
}
printf("\n");
}

2
rdoff/test/Makefile Normal file
View file

@ -0,0 +1,2 @@
clean:
rm -f *.rdf *.rdx

14
rdoff/test/makelib Normal file
View file

@ -0,0 +1,14 @@
LIBNAME=$1;
shift;
if [ "$LIBNAME" = "" ]; then
echo 'Usage: makelib <library name> <module> [...]'
fi
rdflib c $LIBNAME
for FILE in $*; do
rdflib a $LIBNAME $FILE $FILE
done

54
rdoff/test/rdftest1.asm Normal file
View file

@ -0,0 +1,54 @@
;; program to test RDOFF production and linkage
;; items to test include:
;; [1] relocation within the same segment in each module
;; [2] relocation to different segments in same module
;; [3] relocation to same segment in different module
;; [4] relocation to different segment in different module
;; [5] relative relocation to same module
;; [6] relative relocation to different module
;; [7] correct generation of BSS addresses
[SECTION .text]
[BITS 32]
_main:
mov ax,localdata ; [2] (16 bit) => 66 b8 0000
mov eax,localdata2 ; [2] (32 bit) => b8 0000000a
[EXTERN _fardata]
mov eax,[_fardata] ; [4] => a1 00000000 (+20)
mov cx,next ; [1] => 66 b9 0012
next:
call localproc ; [5] => e8 00000019
[EXTERN _farproc]
mov eax,_farproc ; [3] => b8 00000000 (+40+0)
call _farproc ; [6] => e8 -$ (-0+40+0) (=1f)
mov eax,localbss ; [7] => b8 00000000
[GLOBAL _term]
_term: xor ax,ax ; => 66 31 c0
int 21h ; => cd 21
jmp _term ; => e9 -0a (=fffffff6)
localproc:
ret ; => c3
[GLOBAL _test1proc]
_test1proc:
call localproc ; [5] => e8 -$ (-0+0+?) (=-6=fffffffa)
ret ; => c3
[SECTION .data]
[GLOBAL localdata2]
localdata: db 'localdata',0
localdata2: db 'localdata2',0
farref: dd _fardata ; [3] => 0 (+20)
localref: dd _main ; [2] => 0 (+0)
[SECTION .bss]
localbss: resw 4 ; reserve 8 bytes BSS

33
rdoff/test/rdftest2.asm Normal file
View file

@ -0,0 +1,33 @@
;; rdftest2.asm - test linkage and generation of RDOFF files
[SECTION .text]
[BITS 32]
[GLOBAL _farproc]
[EXTERN _test1proc]
[EXTERN localdata2]
[EXTERN _term]
_farproc:
mov bx,localdata2 ; [4] 0 => 66 bb 000a(+0)
mov eax,_term ; [3] 5 => b8 00000000(+26+0)
call _test1proc ; [6] A => e8 fffffff2(-40+0+31)(=ffffffe3)
mov eax,_farproc ; [1] => b8 00000000(+40)
add eax,[_fardata] ; [2] => 03 05 00000000(+20)
mov ebx,mybssdata ; [7] => bb 00000000(+08)
call myproc ; [5] => e8 00000001
ret
myproc:
add eax,ebx
ret
[SECTION .data]
[GLOBAL _fardata]
_fardata: dw _term ; [4]
_localref: dd _farproc ; [2]
[SECTION .bss]
mybssdata: resw 1

48
rdoff/test/rdtlib.asm Normal file
View file

@ -0,0 +1,48 @@
;; library functions for rdtmain - test of rdx linking and execution
;; library function = _strcmp, defined as in C
[SECTION .text]
[BITS 32]
[GLOBAL _strcmp]
_strcmp:
push ebp
mov ebp,esp
;; ebp+8 = first paramater, ebp+12 = second
mov esi,[ebp+8]
mov edi,[ebp+12]
.loop:
mov cl,byte [esi]
mov dl,byte [edi]
cmp cl,dl
jb .below
ja .above
or cl,cl
jz .match
inc esi
inc edi
jmp .loop
.below:
mov eax,-1
pop ebp
ret
.above:
mov eax,1
pop ebp
ret
.match:
xor eax,eax
pop ebp
ret
[SECTION .data]
[GLOBAL _message]
_message: db 'hello',0

47
rdoff/test/rdtmain.asm Normal file
View file

@ -0,0 +1,47 @@
;; rdtmain - main part of test program for RDX execution.
;; returns true (0) if its parameter equals the phrase "hello"
;; "hello" is stored in the library part, to complicate the
;; linkage.
;; assemble and link with the following commands:
;; nasm -f rdf rdtmain.asm
;; nasm -f rdf rdtlib.asm
;; ldrdf rdtmain.rdf rdtlib.rdf -o rdxtest.rdx
;; run with 'rdx rdxtest.rdx [parameters]' on a Linux (or possibly
;; other 32 bit OS) systems (x86 architectures only!)
;; try using '&& echo Yes' afterwards to find out when it returns 0.
[EXTERN _strcmp] ; strcmp is an imported function
[EXTERN _message] ; imported data
[SECTION .text]
[BITS 32]
;; main(int argc,char **argv)
[GLOBAL _main]
_main:
push ebp
mov ebp,esp
;; ebp+8 = argc, ebp+12 = argv
cmp dword [ebp+8],2
jb error ; cause error if < 1 parameters
mov eax, [ebp+12] ; eax = argv
mov ebx, [eax+4] ; ebx = argv[1]
mov ecx, _message ; ecx = "hello"
push ecx
push ebx
call _strcmp ; compare strings
add esp,8 ; caller clears stack
pop ebp
ret ; return return value of _strcmp
error:
mov eax,2 ; return 2 on error
pop ebp
ret

18
rdoff/test/testlib.asm Normal file
View file

@ -0,0 +1,18 @@
; program to test retrieval of and linkage to modules in libraries by
; ldrdf
[SECTION .text]
[GLOBAL _main]
[EXTERN _strcmp]
_main:
push dword string1
push dword string2
call _strcmp
add esp,8 ; doh! clear up stack ;-)
ret
[SECTION .data]
string1: db 'abc',0 ; try changing these strings and see
string2: db 'abd',0 ; what happens!

View file

@ -1,7 +1,15 @@
; Standard macro set for NASM 0.95
; Standard macro set for NASM 0.96 -*- nasm -*-
; Note that although some user-level forms of directives are defined
; here, not all of them are: the user-level form of a format-specific
; directive should be defined in the module for that directive.
%define __NASM_MAJOR__ 0
%define __NASM_MINOR__ 95
%define __NASM_MINOR__ 96
; These two need to be defined, though the actual definitions will
; be constantly updated during preprocessing.
%define __FILE__
%define __LINE__
%define __SECT__ ; it ought to be defined, even if as nothing
@ -23,6 +31,7 @@
%push struc
%define %$strucname %1
[absolute 0]
%$strucname: ; allow definition of `.member' to work sanely
%endmacro
%imacro endstruc 0.nolist
%{$strucname}_size:
@ -44,34 +53,34 @@ __SECT__
%pop
%endmacro
%imacro extern 1+.nolist
%imacro align 1-2+.nolist nop
times ($$-$) & ((%1)-1) %2
%endmacro
%imacro alignb 1-2+.nolist resb 1
times ($$-$) & ((%1)-1) %2
%endmacro
%imacro extern 1-*.nolist
%rep %0
[extern %1]
%rotate 1
%endrep
%endmacro
%imacro bits 1+.nolist
[bits %1]
%endmacro
%imacro global 1+.nolist
%imacro global 1-*.nolist
%rep %0
[global %1]
%rotate 1
%endrep
%endmacro
%imacro common 1+.nolist
%imacro common 1-*.nolist
%rep %0
[common %1]
%endmacro
%imacro org 1+.nolist
[org %1]
%endmacro
%imacro group 1+.nolist
[group %1]
%endmacro
%imacro uppercase 1+.nolist
[uppercase %1]
%endmacro
%imacro library 1+.nolist
[library %1]
%rotate 1
%endrep
%endmacro

View file

@ -1,2 +1,2 @@
clean:
rm -f *.o *.obj *.com bintest inctest
rm -f *test *.com *.o *.obj *so *.exe

96
test/aoutso.asm Normal file
View file

@ -0,0 +1,96 @@
; test source file for assembling to NetBSD/FreeBSD a.out shared library
; build with:
; nasm -f aoutb aoutso.asm
; ld -Bshareable -o aoutso.so aoutso.o
; test with:
; cc -o aoutso aouttest.c aoutso.so
; ./aoutso
; This file should test the following:
; [1] Define and export a global text-section symbol
; [2] Define and export a global data-section symbol
; [3] Define and export a global BSS-section symbol
; [4] Define a non-global text-section symbol
; [5] Define a non-global data-section symbol
; [6] Define a non-global BSS-section symbol
; [7] Define a COMMON symbol
; [8] Define a NASM local label
; [9] Reference a NASM local label
; [10] Import an external symbol
; [11] Make a PC-relative call to an external symbol
; [12] Reference a text-section symbol in the text section
; [13] Reference a data-section symbol in the text section
; [14] Reference a BSS-section symbol in the text section
; [15] Reference a text-section symbol in the data section
; [16] Reference a data-section symbol in the data section
; [17] Reference a BSS-section symbol in the data section
BITS 32
EXTERN __GLOBAL_OFFSET_TABLE_
GLOBAL _lrotate:function ; [1]
GLOBAL _greet:function ; [1]
GLOBAL _asmstr:data _asmstr.end-_asmstr ; [2]
GLOBAL _textptr:data 4 ; [2]
GLOBAL _selfptr:data 4 ; [2]
GLOBAL _integer:data 4 ; [3]
EXTERN _printf ; [10]
COMMON _commvar 4 ; [7]
SECTION .text
; prototype: long lrotate(long x, int num);
_lrotate: ; [1]
push ebp
mov ebp,esp
mov eax,[ebp+8]
mov ecx,[ebp+12]
.label rol eax,1 ; [4] [8]
loop .label ; [9] [12]
mov esp,ebp
pop ebp
ret
; prototype: void greet(void);
_greet push ebx ; we'll use EBX for GOT, so save it
call .getgot
.getgot: pop ebx
add ebx,__GLOBAL_OFFSET_TABLE_ + $$ - .getgot wrt ..gotpc
mov eax,[ebx+_integer wrt ..got] ; [14]
mov eax,[eax]
inc eax
mov [ebx+localint wrt ..gotoff],eax ; [14]
mov eax,[ebx+_commvar wrt ..got]
push dword [eax]
mov eax,[ebx+localptr wrt ..gotoff] ; [13]
push dword [eax]
mov eax,[ebx+_integer wrt ..got] ; [1] [14]
push dword [eax]
lea eax,[ebx+_printfstr wrt ..gotoff]
push eax ; [13]
call _printf wrt ..plt ; [11]
add esp,16
pop ebx
ret
SECTION .data
; a string
_asmstr db 'hello, world', 0 ; [2]
.end
; a string for Printf
_printfstr db "integer==%d, localint==%d, commvar=%d"
db 10, 0
; some pointers
localptr dd localint ; [5] [17]
_textptr dd _greet wrt ..sym ; [15]
_selfptr dd _selfptr wrt ..sym ; [16]
SECTION .bss
; an integer
_integer resd 1 ; [3]
; a local integer
localint resd 1 ; [6]

32
test/binexe.asm Normal file
View file

@ -0,0 +1,32 @@
; Demonstration of how to write an entire .EXE format program by using
; the `exebin.mac' macro package.
; To build:
; nasm -fbin binexe.asm -o binexe.exe -ipath
; (where `path' is such as to allow the %include directive to find
; exebin.mac)
; To test:
; binexe
; (should print `hello, world')
%include "exebin.mac"
EXE_begin
EXE_stack 64 ; demonstrates overriding the 0x800 default
section .text
mov ax,cs
mov ds,ax
mov dx,hello
mov ah,9
int 0x21
mov ax,0x4c00
int 0x21
section .data
hello: db 'hello, world', 13, 10, '$'
EXE_end

97
test/elfso.asm Normal file
View file

@ -0,0 +1,97 @@
; test source file for assembling to ELF shared library
; build with:
; nasm -f elf elfso.asm
; ld -shared -o elfso.so elfso.o
; test with:
; gcc -o elfso elftest.c ./elfso.so
; ./elfso
; (assuming your gcc is ELF, and you're running bash)
; This file should test the following:
; [1] Define and export a global text-section symbol
; [2] Define and export a global data-section symbol
; [3] Define and export a global BSS-section symbol
; [4] Define a non-global text-section symbol
; [5] Define a non-global data-section symbol
; [6] Define a non-global BSS-section symbol
; [7] Define a COMMON symbol
; [8] Define a NASM local label
; [9] Reference a NASM local label
; [10] Import an external symbol
; [11] Make a PC-relative call to an external symbol
; [12] Reference a text-section symbol in the text section
; [13] Reference a data-section symbol in the text section
; [14] Reference a BSS-section symbol in the text section
; [15] Reference a text-section symbol in the data section
; [16] Reference a data-section symbol in the data section
; [17] Reference a BSS-section symbol in the data section
BITS 32
GLOBAL lrotate:function ; [1]
GLOBAL greet:function ; [1]
GLOBAL asmstr:data asmstr.end-asmstr ; [2]
GLOBAL textptr:data 4 ; [2]
GLOBAL selfptr:data 4 ; [2]
GLOBAL integer:data 4 ; [3]
EXTERN printf ; [10]
COMMON commvar 4:4 ; [7]
EXTERN _GLOBAL_OFFSET_TABLE_
SECTION .text
; prototype: long lrotate(long x, int num);
lrotate: ; [1]
push ebp
mov ebp,esp
mov eax,[ebp+8]
mov ecx,[ebp+12]
.label rol eax,1 ; [4] [8]
loop .label ; [9] [12]
mov esp,ebp
pop ebp
ret
; prototype: void greet(void);
greet push ebx ; we'll use EBX for GOT, so save it
call .getgot
.getgot: pop ebx
add ebx,_GLOBAL_OFFSET_TABLE_ + $$ - .getgot wrt ..gotpc
mov eax,[ebx+integer wrt ..got] ; [14]
mov eax,[eax]
inc eax
mov [ebx+localint wrt ..gotoff],eax ; [14]
mov eax,[ebx+commvar wrt ..got]
push dword [eax]
mov eax,[ebx+localptr wrt ..gotoff] ; [13]
push dword [eax]
mov eax,[ebx+integer wrt ..got] ; [1] [14]
push dword [eax]
lea eax,[ebx+printfstr wrt ..gotoff]
push eax ; [13]
call printf wrt ..plt ; [11]
add esp,16
pop ebx
ret
SECTION .data
; a string
asmstr db 'hello, world', 0 ; [2]
.end
; a string for Printf
printfstr db "integer==%d, localint==%d, commvar=%d"
db 10, 0
; some pointers
localptr dd localint ; [5] [17]
textptr dd greet wrt ..sym ; [15]
selfptr dd selfptr wrt ..sym ; [16]
SECTION .bss
; an integer
integer resd 1 ; [3]
; a local integer
localint resd 1 ; [6]

30
test/objexe.asm Normal file
View file

@ -0,0 +1,30 @@
; Demonstration of how to write an entire .EXE format program as a .OBJ
; file to be linked. Tested with the VAL free linker.
; To build:
; nasm -fobj objexe.asm
; val objexe.obj,objexe.exe;
; To test:
; objexe
; (should print `hello, world')
segment code
..start: mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,stacktop
mov dx,hello
mov ah,9
int 0x21
mov ax,0x4c00
int 0x21
segment data
hello: db 'hello, world', 13, 10, '$'
segment stack stack
resb 64
stacktop: