diff options
| author | Marek Marecki <[email protected]> | 2023-10-09 22:50:19 +0200 |
|---|---|---|
| committer | Marek Marecki <[email protected]> | 2023-10-09 23:54:11 +0200 |
| commit | 4d6823b86f793e445b6be4af561e77010ff42e0a (patch) | |
| tree | 756da09520bc56a0736b731762360e37c5ecb924 | |
| parent | 90afc70cf88a9764f94d88145e5b585219087595 (diff) | |
| download | viuavm-master.tar.gz | |
The assembly language was reworked "a wee bit". By this I mean that the
source files will have to be almost completely rewritten, as there are
new ways of:
- storing objects in .rodata
- loading data from .rodata into working memory
- declaring and defining functions
- jumps
Basically everything changed, except for the syntax of instructions.
SECTIONS
The assembler no longer switches sections automatically to put objects
and code in appropriate sections. It now requires explicitly setting the
active section with
.section ".rodata"
.section ".text"
The only two valid names are ".rodata" and ".text".
ALLOCATING OBJECTS IN .rodata
To allocate an atom or a string in .rodata use the following code:
.section ".rodata"
.label your_label
.object string "Hello, World!"
Both atoms and strings are stored the same way so allocate them as
strings. They require different ways of loading into memory, though.
Atom is one of the fundamental types, and as such has a dedicated
instruction. To load an atom from .rodata use the reference syntax:
atom $1.l, @your_label
To load an atom without an explicit address you can just put it directly
in the immediate operand:
atom $1.l, atomic_atom
Loading a string is a more involved process, and requires copying the
data manually between .rodata and working memory. For an example see one
of the memcpy(3) and memset(3) tests:
- tests/asm/memcpy.asm
- tests/asm/memcmp_{eq,gt,lt}.asm
as well as the implementations of these functions to see how the data is
actually shuffled from place to place:
- tests/std/memcpy.asm
- tests/std/memcmp.asm
There is also an implementation of memset(3) in "tests/asm/memset.asm".
The most important thing to note is that when you load the address of a
string using ARODP you get the address of the string itself. Subtract 8
bytes from that address, and you will get the address of the 64-bit
unsigned integer which stores the size of the string. Then use these two
pieces of information to access the string.
SYMBOLS, LABELS, AND VISIBILITY
All labels (the .label directives) are stored in the symbol table with a
few attributes:
- type of the symbol they are refering to (either a function STT_FUNC;
or an object STT_OBJECT)
- binding (global STB_GLOBAL, or local STB_LOCAL)
- visibility (default STV_DEFAULT, or hidden STV_HIDDEN)
Not all options provided by the ELF standard are used. See elf(5) for
more information about types, binding, and visibility.
TEXT SYMBOLS: JUMPS
A label defined without a corresponding symbol:
.label foo_jmp
will be STB_LOCAL and STV_HIDDEN. This means that it is only available
inside the current compilation unit (ie, the assembly file where it is
defined). They could have been defined using visibility STV_DEFAULT but
STV_HIDDEN is used to mark them as not callable. Such labels can only be
used as targets in the IF instruction:
if void, foo_jmp
TEXT SYMBOLS: FUNCTIONS
To make a callable label (in effect marking it as an entry point of a
function) declare a symbol for it explicitly:
.symbol foo_global_fn
.label foo_global_fn
This default combination makes the "foo_global_fn" label STB_GLOBAL and
STV_DEFAULT ie, a global function available to all modules which will
link to the module in which this symbol was defined.
To create a function visible only in the current compilation unit (which
would be equivalent to declaring it static in C, or defining it in an
anonymous namespace in C++) use:
.symbol [[local]] foo_unit_fn
.label foo_unit_fn
This makes the "foo_unit_fn" function STB_LOCAL and STV_DEFAULT ie,
available in the current compilation unit, but not outside of it.
To create a function that will be available inside a module, but not
globally use:
.symbol [[hidden]] foo_module_fn
.label foo_module_fn
The "foo_module_fn" function is STB_GLOBAL and STV_HIDDEN. What is the
purpose of this weird combination? To make the function available
outside of the current compilation module, but only until it is first
linked to. This allows creating "modules" composed of several
compilation units, which have some public and some private functions.
The linker will change STB_GLOBAL and STV_HIDDEN into STB_LOCAL and
STV_DEFAULT during first linking, making the "global but hidden" symbol
local to the resulting ELF.
This means that when creating a module from several compilation units it
is wise to postpone linking as much as possible, and the way to go is
usually to link all individual parts in one go. For example
]$ viua-ld -c -o module.o \
module.fn_a.o module.fn_b.o
will yield the expected result, but linking in two steps will probably
not.
Of course, "modules" defined this way are just a convention.
There are other shortcomins to the simplistic scheme outlined above.
Duplicate symbols will cause conflicts even if defined as private to a
module. However, this should not be problematic as the compilers for
high level languages (or assembly programmers themselves) should prefix
even private labels with the module's name. While a crude solution,
prefixing will solve the problem of name clashes.
| -rw-r--r-- | VERSION | 2 |
1 files changed, 1 insertions, 1 deletions
@@ -1 +1 @@ -0.12 +0.13 |
