Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Assembly (x86-64, AArch64, RISC-V)  Module 4 – Moving data: registers, memory and addresses

Static data and PC-relative addresses

Place data in .rodata, .data and .bss, and form its address position-independently on x86-64, AArch64 and RISC-V. Fix three classic address errors.

  • Intermediate
  • 5 minutes
  • Examples run with Python 3.14.8
  • By MySmartCoPilot
Free lesson, Pro answers: 5 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Choose .rodata, .data or .bss for a constant, a variable or a zero-filled buffer
  • Explain how RIP-relative lea, adrp/add and auipc/addi reach data from any load address
  • Diagnose and fix three classic address errors (NASM DEFAULT ABS, macOS text relocations, RISC-V gp relaxation)

Before you start

What you’ll learn · Before you start

What you will learn

  • Choose .rodata, .data or .bss for a constant, a variable or a zero-filled buffer
  • Explain how RIP-relative lea, adrp/add and auipc/addi reach data from any load address
  • Diagnose and fix three classic address errors (NASM DEFAULT ABS, macOS text relocations, RISC-V gp relaxation)

Before you start

On this page

A string literal or a global variable is not in a register; it sits in the program’s static data, and the code must form its address at run time. Modern executables are position-independent code: Code that reaches its data and other code relative to the program counter rather than by absolute address, so the loader may place it at any base address. Formed with RIP-relative operands on x86-64, adrp/add on AArch64 and auipc/addi on RISC-V., so the loader may place them anywhere, and the code can no longer hard-code where its data lives. Instead it computes each address relative to the program counter, and the three ISAs do so with lea [rip + x], adrp/add and auipc/addi.

This lesson places data in the right section with the right alignment, forms a data address position-independently, and explains three address errors that cost real time: NASM’s deprecated DEFAULT ABS, the macOS text-relocation refusal, and the RISC-V global-pointer relaxation that can crash a startup which never set gp. The AArch64 program is built and run, and returns 42; the NASM sessions show the deprecation warning and the clean, RIP-relative fix.

Where static data lives: .rodata, .data and .bss

The assembler puts your data into a named section: A named region of an object or executable file, such as .text, .rodata, .data or .bss. The linker merges matching sections across objects and the loader groups them into segments at run time. More in “From source to executable: objects and linking”, and the section decides the data’s permissions and whether it takes space in the file. A directive: A line that gives an order to the assembler instead of naming a processor instruction: switch section, export a symbol, put bytes into the data, define a constant, pad to a boundary. GNU as directives start with a dot (.data, .globl, .byte); NASM writes section, global, db and equ. More in “Assembler syntax: NASM, GNU as, Intel and AT&T” such as .section, .data or .p2align chooses the section and its alignment.

Section Holds Writable In the file?
.rodata (__TEXT,__const on Mach-O) constants, string literals no yes
.data initialised globals yes yes
.bss zero-initialised globals yes no (allocated and zeroed by the loader)

The last row is the useful surprise: .bss: The section that holds writable data initialised to zero. It records only a size in the executable and takes no file space; the loader allocates and zeroes its pages when the program starts. takes no room in the executable. A one-million-entry zeroed array adds a size field to the file, not a million zero bytes; the loader allocates and zeroes the pages when the program starts. Alignment still matters: a directive like .p2align 4 (16-byte alignment) puts data where a vector load can reach it, which the previous lesson showed some hardware requires.

Reaching data relative to the program counter

With position-independent code the address of a global is not known until the loader places the program, so the code builds it from the current PC. x86-64 writes lea rsi, [rip + msg], a single instruction. AArch64 needs two: adrp loads the base of the 4 KiB page that holds the symbol, and add with @PAGEOFF adds the offset within the page. RISC-V also uses a pair: auipc adds a 20-bit PC-relative upper immediate, and addi adds the low 12 bits.

Interactive · stage by stage adrp and add build an address from the PC Follow adrp and add building the address of a static value from the program counter, then ldr reading it.
Forming an address relative to the PC, step 4 of 4. Showing Program counter, adrp x0, value@PAGE, add x0, @PAGEOFF, ldr x0, [x0]. In focus: ldr x0, [x0].Program counterwhere code runsadrp x0, value@PAGEx0 = page baseadd x0, @PAGEOFFx0 = full addressldr x0, [x0]x0 = the value 42

The drawing shows the whole scene; its 4 steps are listed below.

Every step as text
  1. Start from the PC. The CPU knows where the current instruction is, but not where value ended up. Position-independent code works out the address from the PC.
  2. adrp finds the page. adrp loads the base of the 4 KiB page that holds value, measured relative to the PC. It gives the page, not the exact address.
  3. add finishes the address. add with value@PAGEOFF adds the offset of value within its page, so x0 now holds the complete runtime address.
  4. ldr reads the value. With the address in x0, ldr loads the 64-bit value. Nothing in the file hard-codes where value lives, so the loader may place the program anywhere.

The AArch64 program below puts 42 in .data, forms its address with adrp/add, loads it and returns it. The disassembly shows exactly the three instructions the figure describes, and the run returns 42.

AArch64: adrp + add reach static data S · aarch64/adrp_add.s
// Put a value in writable static data, form its address position-independently with
// adrp + add, load it and return it. adrp loads the 4 KiB page base relative to the PC;
// add with @PAGEOFF adds the offset within the page. The value 42 becomes the exit status.
.section __DATA,__data
.p2align 3
value:
    .quad 42
.text
.globl _main
.p2align 2
_main:
    adrp x0, value@PAGE         // page base of `value`, PC-relative
    add  x0, x0, value@PAGEOFF  // add the offset inside the page -> full address
    ldr  x0, [x0]               // load the 64-bit value
    ret                         // exit status = low byte = 42
Running the adrp/add program and the ldr =value failure Python · aarch64_static_session.py
"""Form a static-data address the right way (adrp + add) and the wrong way (ldr =label) on macOS.

The adrp/add program runs and returns 42 as its exit status. The ldr =value program is pre-assembled to a fixed
object, then linked; the link fails with a text-relocation error, which macOS raises for absolute addresses in code.
The sed filter trims the object's directory from the message so only its name shows.
"""

import pathlib
import sys

sys.path.insert(0, str(pathlib.Path(__file__).parent / "sessions"))
import session

session.run(
    """
# Build and run the position-independent version; it loads 42 and returns it.
clang -o aarch64/adrp_add aarch64/adrp_add.s
./aarch64/adrp_add; echo "exit=$?"
# The three instructions: adrp for the page, add for the offset, ldr for the value.
/opt/homebrew/opt/llvm/bin/llvm-objdump -d aarch64/adrp_add | sed -n '/<_main>:/,/ret/p'
# The wrong way: assemble the ldr =value version, then try to link it.
clang -c aarch64/ldr_literal.s -o aarch64/ldr_literal.o
clang -o aarch64/ldr_literal aarch64/ldr_literal.o 2>&1 | sed 's#(/[^)]*/#(#'
"""
)

Output

# Build and run the position-independent version; it loads 42 and returns it.
$ clang -o aarch64/adrp_add aarch64/adrp_add.s
$ ./aarch64/adrp_add; echo "exit=$?"
exit=42
# The three instructions: adrp for the page, add for the offset, ldr for the value.
$ /opt/homebrew/opt/llvm/bin/llvm-objdump -d aarch64/adrp_add | sed -n '/<_main>:/,/ret/p'
0000000100000370 <_main>:
100000370: 90000020    	adrp	x0, 0x100004000 <value>
100000374: 91000000    	add	x0, x0, #0x0
100000378: f9400000    	ldr	x0, [x0]
10000037c: d65f03c0    	ret
# The wrong way: assemble the ldr =value version, then try to link it.
$ clang -c aarch64/ldr_literal.s -o aarch64/ldr_literal.o
$ clang -o aarch64/ldr_literal aarch64/ldr_literal.o 2>&1 | sed 's#(/[^)]*/#(#'
Illegal text-relocations:
  text-relocation in '_main'+0x10 (ldr_literal.o) to 'value'
ld: Found illegal text-relocations
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 aarch64_static_session.py

RISC-V’s lla pseudo-instruction expands to the same kind of pair. The disassembly shows auipc followed by an addi (printed as mv while its offset is still zero), each with its relocation, plus the R_RISCV_RELAX markers that let the linker shrink the pair when it can.

RISC-V: lla expands to auipc + addi S · riscv/la.s
# lla (load local address) is a pseudo-instruction that expands to a PC-relative auipc +
# addi pair. GNU ld may relax such a pair to a single gp-relative addi when
# __global_pointer$ is in range, which is why startup code must load gp first (or the
# access faults). The linked program runs on a riscv64 Linux machine (or under qemu-user).
.section .data
.p2align 3
value:
    .dword 42
.text
.globl load_value
load_value:
    lla  a0, value          # -> auipc a0, %pcrel_hi(value); addi a0, a0, %pcrel_lo
    ld   a0, 0(a0)
    ret
Disassembling the RISC-V lla pair and its relocations Python · riscv_la_session.py
"""Assemble the RISC-V lla source and show the PC-relative auipc + addi pair with its relocations.

lla expands to auipc + addi; the R_RISCV_PCREL_HI20 and R_RISCV_PCREL_LO12_I relocations carry the two halves of the
offset, and the R_RISCV_RELAX markers let GNU ld shrink the pair to one gp-relative addi. The driver stops at the
disassembly; the linked program would run on a riscv64 Linux machine (or under qemu-user on another host).
"""

import pathlib
import sys

sys.path.insert(0, str(pathlib.Path(__file__).parent / "sessions"))
import session

session.run(
    """
# Assemble the RISC-V source into an ELF object for rv64gc.
/opt/homebrew/opt/llvm/bin/clang -c -target riscv64-linux-gnu -march=rv64gc riscv/la.s -o riscv/la.o
# Disassemble with relocations: the auipc + addi pair and the RELAX markers that enable gp relaxation.
/opt/homebrew/opt/llvm/bin/llvm-objdump -d -r riscv/la.o
"""
)

Output

# Assemble the RISC-V source into an ELF object for rv64gc.
$ /opt/homebrew/opt/llvm/bin/clang -c -target riscv64-linux-gnu -march=rv64gc riscv/la.s -o riscv/la.o
# Disassemble with relocations: the auipc + addi pair and the RELAX markers that enable gp relaxation.
$ /opt/homebrew/opt/llvm/bin/llvm-objdump -d -r riscv/la.o

riscv/la.o:	file format elf64-littleriscv

Disassembly of section .text:

0000000000000000 <load_value>:
       0: 00000517     	auipc	a0, 0x0
		0000000000000000:  R_RISCV_PCREL_HI20	value
		0000000000000000:  R_RISCV_RELAX	*ABS*
       4: 00050513     	mv	a0, a0
		0000000000000004:  R_RISCV_PCREL_LO12_I	.Lpcrel_hi0
		0000000000000004:  R_RISCV_RELAX	*ABS*
       8: 6108         	ld	a0, 0x0(a0)
       a: 8082         	ret

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 riscv_la_session.py

Key idea

Position-independent code reaches its data relative to the PC: one lea on x86-64, an adrp/add pair on AArch64, an auipc/addi pair on RISC-V, so the loader may place the program anywhere.

Checkpoint

Check: Where static data lives · PC-relative addressing

  1. Question 1 of 2 A program declares a one-million-byte array that starts as all zeros. Where does it belong, and what happens to the file size?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: In .bss, which records the size but stores no bytes; the loader allocates and zeroes the pages at run time.

    Zero-initialised writable data belongs in .bss, which records only the size. The loader allocates and zeroes the pages at run time, so the executable does not carry a million zero bytes.

  2. Question 2 of 2 On AArch64, why does reaching a global take two instructions (adrp then add) rather than one?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: adrp can only load the base of the 4 KiB page; add supplies the offset within the page to complete the address.

    adrp computes the PC-relative base of the 4 KiB page holding the symbol; add with @PAGEOFF adds the offset within that page. Two instructions because one 32-bit instruction cannot carry a full 64-bit address.

Three address errors, and their fixes

Three mistakes around addresses are common enough to recognise on sight. The first two appear in the sessions above and below; the third follows from the RISC-V psABI’s rule for gp.

NASM’s implicit DEFAULT ABS. In 64-bit mode, NASM still defaults [label] to an absolute address unless told otherwise. NASM 3.x deprecates that default: the session shows absrel.asm earning implicit DEFAULT ABS is deprecated, while absrel_fixed.asm with a default rel line assembles clean and disassembles to a RIP-relative mov. The absolute form also cannot be linked into a position-independent executable: it needs an R_X86_64_32S relocation, which stores the address in 32 signed bits, and a PIE may be loaded anywhere in the 64-bit space, so the linker refuses the relocation outright. The default rel line fixes both the warning and the link.

x86-64 NASM: absolute (warns) ASM · x86_64/absrel.asm
; 64-bit NASM defaults to absolute addressing for [label], which NASM 3.x deprecates
; and which fails to link as a position-independent executable.
section .data
value: dq 42
section .text
global _start
_start:
    mov rax, [value]        ; absolute [label] -> DEFAULT ABS warning, non-PIE relocation
    ret
x86-64 NASM: default rel (clean) ASM · x86_64/absrel_fixed.asm
; The fix: `default rel` makes [label] RIP-relative. No DEFAULT ABS warning, and the
; object links as a position-independent executable.
default rel
section .data
value: dq 42
section .text
global _start
_start:
    mov rax, [value]        ; RIP-relative now: lea-style PC-relative access
    ret
The DEFAULT ABS warning and the fixed assembly Python · x86_64_absrel_session.py
"""Assemble the absolute and the fixed x86-64 NASM sources and show the difference in warnings.

NASM 3.x deprecates the implicit DEFAULT ABS of 64-bit mode: absrel.asm earns the warning, absrel_fixed.asm (with
`default rel`) assembles clean. Linking the absolute object into a position-independent executable is the next step
(it fails with an R_X86_64_32S relocation error, per the x86-64 psABI), done with GNU ld or ld.lld on an x86-64
Linux machine; the session stops at the two assembles and the clean disassembly.
"""

import pathlib
import sys

sys.path.insert(0, str(pathlib.Path(__file__).parent / "sessions"))
import session

session.run(
    """
# Assemble the absolute version: NASM warns that implicit DEFAULT ABS is deprecated.
nasm -f elf64 x86_64/absrel.asm -o x86_64/absrel.o
# Assemble the fixed version with `default rel`: no warning, and it would link as a PIE.
nasm -f elf64 x86_64/absrel_fixed.asm -o x86_64/absrel_fixed.o
# Confirm the fixed object disassembles to a RIP-relative load.
/opt/homebrew/opt/llvm/bin/llvm-objdump -d x86_64/absrel_fixed.o
"""
)

Output

# Assemble the absolute version: NASM warns that implicit DEFAULT ABS is deprecated.
$ nasm -f elf64 x86_64/absrel.asm -o x86_64/absrel.o
x86_64/absrel.asm:8: warning: implicit DEFAULT ABS is deprecated [-w+implicit-abs-deprecated]
# Assemble the fixed version with `default rel`: no warning, and it would link as a PIE.
$ nasm -f elf64 x86_64/absrel_fixed.asm -o x86_64/absrel_fixed.o
# Confirm the fixed object disassembles to a RIP-relative load.
$ /opt/homebrew/opt/llvm/bin/llvm-objdump -d x86_64/absrel_fixed.o

x86_64/absrel_fixed.o:	file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <_start>:
       0: 48 8b 05 00 00 00 00         	movq	(%rip), %rax            # 0x7 <_start+0x7>
       7: c3                           	retq

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 x86_64_absrel_session.py

macOS text relocations. macOS arm64 refuses absolute addresses in code. The session above already showed it: ldr x1, =value asks for an absolute address through a literal pool, and the link fails with Found illegal text-relocations. The fix is the adrp/add pair.

RISC-V global-pointer relaxation. GNU ld relaxes an auipc/addi pair into one gp-relative addi when __global_pointer$ is within range, which is why the R_RISCV_RELAX markers appear in the disassembly. The RISC-V psABI assumes startup code loads __global_pointer$ into gp before any relaxed access runs. A bare _start that never does so leaves gp holding whatever value it entered with, so the relaxed gp-relative access computes its address from the wrong base. When that address is not mapped, Linux sends the process SIGSEGV (signal 11), which ends it, and a shell such as Bash reports that as status 139, which is 128 plus the signal number. Setting gp under .option norelax, or linking with --no-relax so the pair is left alone, avoids it.

Error Where Symptom Fix
Implicit DEFAULT ABS x86-64 NASM deprecation warning, PIE link fails add a default rel line
Text relocation macOS arm64 Found illegal text-relocations use adrp/add, not ldr =label
gp relaxation crash RISC-V bare _start SIGSEGV (status 139) set gp, or link --no-relax

Checkpoint

Check: Three address errors and their fixes

  1. Question 1 of 2 A 64-bit NASM file warns implicit DEFAULT ABS is deprecated and will not link as a PIE. What single line fixes it?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: default rel, which makes [label] operands RIP-relative

    default rel makes NASM treat [label] as RIP-relative in 64-bit mode. That clears the DEFAULT ABS warning and produces a PC-relative relocation the linker accepts in a position-independent executable.

  2. Question 2 of 2 A bare RISC-V _start that never sets gp crashes with SIGSEGV after la/lla. Why, and what fixes it?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: GNU ld relaxed the PC-relative pair to a gp-relative access, but gp was never loaded; set gp in startup, or link with --no-relax.

    When __global_pointer$ is in range, GNU ld relaxes an auipc/addi pair into one gp-relative addi. If startup never loaded gp, that access uses a garbage base and faults. Set gp in _start (under .option norelax) or link with --no-relax.

Practise

Each question gives its reason, right or wrong, from the three errors above and the section rules.

Practice that adapts to you

Practise this lesson

6 questions about 3 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 6 Where should a read-only string literal go?

    Choose one answer.

    Show the answer: Question 1 of 6

    Answer: .rodata (or __TEXT,__const on Mach-O)

    A string literal is read-only initialised data, so it belongs in .rodata (named __TEXT,__const or __cstring on Mach-O). It is stored in the file and mapped without write permission.

  2. Question 2 of 6 Which x86-64 instruction is the direct counterpart of the AArch64 adrp/add pair for forming a data address?

    Choose one answer.

    Show the answer: Question 2 of 6

    Answer: lea rsi, [rip + msg]

    x86-64 forms a PC-relative address with lea rsi, [rip + msg] (or a RIP-relative memory operand). It does in one instruction what AArch64 splits into adrp (page) and add (offset).

  3. Question 3 of 6 On macOS arm64, clang -o prog prog.o fails with Found illegal text-relocations. What is the most likely cause in prog.s?

    Choose one answer.

    Show the answer: Question 3 of 6

    Answer: An absolute address in code, such as ldr x1, =label; replace it with an adrp/add pair.

    macOS arm64 forbids absolute addresses in the text segment. ldr x1, =label loads an absolute address from a literal pool and triggers the error; forming the address with adrp/add is position-independent and links.

  4. Question 4 of 6 A library is loaded at a different address on every run, and the loader patches nothing inside the function. Why does its adrp/add pair still reach the right global?

    Choose one answer.

    Show the answer: Question 4 of 6

    Answer: Both instructions measure from the current program counter, so only the distance between the code and the data matters, and that distance is fixed inside the image.

    adrp computes a page address from the program counter and add supplies the offset inside that page, so the pair encodes the distance from the instruction to the global. The loader relocates code and data as one image, the distance is unchanged, and the computed address is correct wherever the library lands.

  5. Question 5 of 6 A program keeps a table of eight counters that start at 1, 2, 3 … 8 and are incremented at run time. Which section holds the table?

    Choose one answer.

    Show the answer: Question 5 of 6

    Answer: .data

    Writable data with non-zero starting values belongs in .data: the file carries the eight starting values and the loader maps them writable. Read-only constants go in .rodata, and data that starts as zeros goes in .bss.

  6. Question 6 of 6 What does GNU ld turn a RISC-V auipc/addi address pair into when __global_pointer$ is in range?

    Choose one answer.

    Show the answer: Question 6 of 6

    Answer: A single addi that uses gp as its base

    When the data lies within reach of __global_pointer$, GNU ld replaces the pair with one gp-relative addi. That saves an instruction and is the reason startup code must set gp before any relaxed access runs.

Interview questions

Warm-up: why is modern code position-independent, and how does it reach a global variable without knowing its address?

A position-independent executable or shared library can be loaded at any base address the operating system chooses, which lets address-space layout randomisation place it unpredictably and lets a shared library be mapped wherever there is room. Because the final address of a global is not fixed until load time, the code cannot hard-code it. Instead it computes the address from the program counter: the distance from the current instruction to the data is known at link time, even though the absolute address is not. x86-64 does this with a RIP-relative lea or memory operand; AArch64 uses adrp to get the page base and add for the offset within the page; RISC-V uses auipc for the PC-relative upper bits and addi for the lower bits. The linker fills in the PC-relative displacement, so the same bytes work wherever the program lands.

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Mid to senior

    Compare how x86-64, AArch64 and RISC-V form the address of a global variable in position-independent code. Why does one ISA need a single instruction and two need a pair?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    A program declares a 100 MB array of zeros and its executable is only a few kilobytes. A teammate is sure the file should be 100 MB. Explain who is right and why.

    Model answer, Pro. First step and follow-ups

Flashcards · 6 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. Where does an assembler put read-only data, writable data and zero-initialised data?

    In .rodata, .data and .bss. .data is stored in the file with its values; .bss records only a size, takes no file space, and the loader hands it zeroed pages at start-up.

  2. How does position-independent code form the address of a global on each ISA?

    From the program counter: one RIP-relative lea on x86-64, an adrp and add pair on AArch64, an auipc and addi pair on RISC-V. The distance to the data is known at link time even when the absolute address is not.

  3. Why is modern code position-independent?

    So the loader may place it at any base address: address-space layout randomisation can then put it somewhere unpredictable, and a shared library can be mapped wherever there is room.

  4. In NASM 64-bit code, what does default rel change?

    It makes a plain [label] operand RIP-relative instead of absolute, so the object can link into a position-independent executable. NASM 3.x deprecates the implicit absolute default.

  5. What happens on macOS arm64 if code contains an absolute address?

    The link fails with a text-relocation error: the platform refuses code that the loader would have to patch with absolute addresses.

  6. Why does RISC-V code depend on the gp register being set up?

    The linker may relax a PC-relative pair into a single gp-relative access to data near gp, so the code then works only if gp holds the value the linker assumed.

Key takeaways

  • The assembler places data in .rodata (read-only), .data (writable, in the file) or .bss (writable, zeroed, taking no file space), and alignment directives decide where the data starts.
  • Position-independent code forms a data address from the program counter: one lea on x86-64, an adrp/add pair on AArch64, an auipc/addi pair on RISC-V.
  • NASM 3.x deprecates the implicit DEFAULT ABS of 64-bit mode; a default rel line makes [label] RIP-relative and lets the object link as a PIE.
  • macOS arm64 rejects absolute addresses in code (a text-relocation error), and RISC-V code depends on gp being set because the linker relaxes PC-relative pairs to gp-relative addressing.

Terms in this lesson

.bss
The section that holds writable data initialised to zero. It records only a size in the executable and takes no file space; the loader allocates and zeroes its pages when the program starts.
position-independent code
Code that reaches its data and other code relative to the program counter rather than by absolute address, so the loader may place it at any base address. Formed with RIP-relative operands on x86-64, adrp/add on AArch64 and auipc/addi on RISC-V.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.