Вы находитесь на странице: 1из 68

Foundations of ARM64 Linux

Debugging, Disassembling, and


Reversing: Analyze Code, Understand
Stack Memory Usage, and Reconstruct
Original C/C++ Code with ARM64 1st
Edition Dmitry Vostokov
Visit to download the full and correct content document:
https://ebookmass.com/product/foundations-of-arm64-linux-debugging-disassembling
-and-reversing-analyze-code-understand-stack-memory-usage-and-reconstruct-origin
al-c-c-code-with-arm64-1st-edition-dmitry-vostokov/
Foundations of ARM64
Linux Debugging,
Disassembling, and
Reversing
Analyze Code, Understand Stack
Memory Usage, and Reconstruct
Original C/C++ Code with ARM64

Dmitry Vostokov
Foundations of
ARM64 Linux
Debugging,
Disassembling, and
Reversing
Analyze Code, Understand
Stack Memory Usage,
and Reconstruct Original C/C++
Code with ARM64

Dmitry Vostokov
Foundations of ARM64 Linux Debugging, Disassembling, and Reversing:
Analyze Code, Understand Stack Memory Usage, and Reconstruct Original
C/C++ Code with ARM64

Dmitry Vostokov
Dublin, Ireland

ISBN-13 (pbk): 978-1-4842-9081-1 ISBN-13 (electronic): 978-1-4842-9082-8


https://doi.org/10.1007/978-1-4842-9082-8
Copyright © 2023 by Dmitry Vostokov
This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or
part of the material is concerned, specifically the rights of translation, reprinting, reuse of
illustrations, recitation, broadcasting, reproduction on microfilms or in any other physical way,
and transmission or information storage and retrieval, electronic adaptation, computer software,
or by similar or dissimilar methodology now known or hereafter developed.
Trademarked names, logos, and images may appear in this book. Rather than use a trademark
symbol with every occurrence of a trademarked name, logo, or image we use the names, logos,
and images only in an editorial fashion and to the benefit of the trademark owner, with no
intention of infringement of the trademark.
The use in this publication of trade names, trademarks, service marks, and similar terms, even if
they are not identified as such, is not to be taken as an expression of opinion as to whether or not
they are subject to proprietary rights.
While the advice and information in this book are believed to be true and accurate at the date of
publication, neither the authors nor the editors nor the publisher can accept any legal
responsibility for any errors or omissions that may be made. The publisher makes no warranty,
express or implied, with respect to the material contained herein.
Managing Director, Apress Media LLC: Welmoed Spahr
Acquisitions Editor: Celestin Suresh John
Development Editor: James Markham
Coordinating Editor: Mark Powers
Cover designed by eStudioCalamar
Cover image by Susan Wilkinson on Unsplash (www.unsplash.com)
Distributed to the book trade worldwide by Apress Media, LLC, 1 New York Plaza, New York, NY
10004, U.S.A. Phone 1-800-SPRINGER, fax (201) 348-4505, e-mail orders-ny@springer-sbm.com,
or visit www.springeronline.com. Apress Media, LLC is a California LLC and the sole member
(owner) is Springer Science + Business Media Finance Inc (SSBM Finance Inc). SSBM Finance
Inc is a Delaware corporation.
For information on translations, please e-mail booktranslations@springernature.com; for
reprint, paperback, or audio rights, please e-mail bookpermissions@springernature.com.
Apress titles may be purchased in bulk for academic, corporate, or promotional use. eBook
versions and licenses are also available for most titles. For more information, reference our Print
and eBook Bulk Sales web page at http://www.apress.com/bulk-sales.
Any source code or other supplementary material referenced by the author in this book is
available to readers on GitHub (https://github.com/Apress). For more detailed information,
please visit http://www.apress.com/source-code.
Printed on acid-free paper
Table of Contents
About the Author���������������������������������������������������������������������������������ix

About the Technical Reviewer�������������������������������������������������������������xi

Preface����������������������������������������������������������������������������������������������xiii

Chapter 1: Memory, Registers, and Simple Arithmetic�������������������������1


Memory and Registers Inside an Idealized Computer������������������������������������������1
Memory and Registers Inside ARM 64-Bit Computer��������������������������������������������2
“Arithmetic” Project: Memory Layout and Registers��������������������������������������������3
“Arithmetic” Project: A Computer Program�����������������������������������������������������������5
“Arithmetic” Project: Assigning Numbers to Memory Locations���������������������������5
Assigning Numbers to Registers���������������������������������������������������������������������������8
“Arithmetic” Project: Adding Numbers to Memory Cells���������������������������������������9
Incrementing/Decrementing Numbers in Memory and Registers�����������������������12
Multiplying Numbers�������������������������������������������������������������������������������������������15
Summary������������������������������������������������������������������������������������������������������������18

Chapter 2: Code Optimization�������������������������������������������������������������19


“Arithmetic” Project: C/C++ Program�����������������������������������������������������������������19
Downloading GDB�����������������������������������������������������������������������������������������������20
GDB Disassembly Output – No Optimization�������������������������������������������������������21
GDB Disassembly Output – Optimization������������������������������������������������������������27
Summary������������������������������������������������������������������������������������������������������������28

iii
Table of Contents

Chapter 3: Number Representations��������������������������������������������������29


Numbers and Their Representations�������������������������������������������������������������������29
Decimal Representation (Base Ten)��������������������������������������������������������������������30
Ternary Representation (Base Three)������������������������������������������������������������������30
Binary Representation (Base Two)����������������������������������������������������������������������31
Hexadecimal Representation (Base Sixteen)������������������������������������������������������32
Why Are Hexadecimals Used?�����������������������������������������������������������������������������32
Summary������������������������������������������������������������������������������������������������������������34

Chapter 4: Pointers�����������������������������������������������������������������������������35
A Definition���������������������������������������������������������������������������������������������������������35
“Pointers” Project: Memory Layout and Registers����������������������������������������������36
“Pointers” Project: Calculations��������������������������������������������������������������������������38
Using Pointers to Assign Numbers to Memory Cells�������������������������������������������39
Adding Numbers Using Pointers�������������������������������������������������������������������������46
Incrementing Numbers Using Pointers���������������������������������������������������������������51
Multiplying Numbers Using Pointers�������������������������������������������������������������������54
Summary������������������������������������������������������������������������������������������������������������58

Chapter 5: Bytes, Halfwords, Words, and Doublewords����������������������59


Using Hexadecimal Numbers������������������������������������������������������������������������������59
Byte Granularity��������������������������������������������������������������������������������������������������60
Bit Granularity�����������������������������������������������������������������������������������������������������60
Memory Layout���������������������������������������������������������������������������������������������������61
Summary������������������������������������������������������������������������������������������������������������64

iv
Table of Contents

Chapter 6: Pointers to Memory�����������������������������������������������������������65


Pointers Revisited�����������������������������������������������������������������������������������������������65
Addressing Types������������������������������������������������������������������������������������������������65
Registers Revisited���������������������������������������������������������������������������������������������70
NULL Pointers�����������������������������������������������������������������������������������������������������70
Invalid Pointers���������������������������������������������������������������������������������������������������70
Variables As Pointers������������������������������������������������������������������������������������������71
Pointer Initialization��������������������������������������������������������������������������������������������71
Initialized and Uninitialized Data�������������������������������������������������������������������������72
More Pseudo Notation�����������������������������������������������������������������������������������������72
“MemoryPointers” Project: Memory Layout�������������������������������������������������������73
Summary������������������������������������������������������������������������������������������������������������87

Chapter 7: Logical Instructions and PC����������������������������������������������89


Instruction Format����������������������������������������������������������������������������������������������89
Logical Shift Instructions������������������������������������������������������������������������������������90
Logical Operations����������������������������������������������������������������������������������������������90
Zeroing Memory or Registers�����������������������������������������������������������������������������91
Program Counter�������������������������������������������������������������������������������������������������92
Code Section�������������������������������������������������������������������������������������������������������93
Summary������������������������������������������������������������������������������������������������������������94

Chapter 8: Reconstructing a Program with Pointers��������������������������95


Example of Disassembly Output: No Optimization����������������������������������������������95
Reconstructing C/C++ Code: Part 1��������������������������������������������������������������������98
Reconstructing C/C++ Code: Part 2������������������������������������������������������������������100
Reconstructing C/C++ Code: Part 3������������������������������������������������������������������102

v
Table of Contents

Reconstructing C/C++ Code: C/C++ Program��������������������������������������������������103


Example of Disassembly Output: Optimized Program���������������������������������������104
Summary����������������������������������������������������������������������������������������������������������106

Chapter 9: Memory and Stacks��������������������������������������������������������107


Stack: A Definition���������������������������������������������������������������������������������������������107
Stack Implementation in Memory���������������������������������������������������������������������108
Things to Remember�����������������������������������������������������������������������������������������110
Stack Push Implementation������������������������������������������������������������������������������111
Stack Pop Implementation��������������������������������������������������������������������������������111
Register Review������������������������������������������������������������������������������������������������111
Application Memory Simplified�������������������������������������������������������������������������112
Stack Overflow��������������������������������������������������������������������������������������������������113
Jumps���������������������������������������������������������������������������������������������������������������114
Calls������������������������������������������������������������������������������������������������������������������115
Call Stack����������������������������������������������������������������������������������������������������������116
Exploring Stack in GDB�������������������������������������������������������������������������������������118
Summary����������������������������������������������������������������������������������������������������������121

Chapter 10: Frame Pointer and Local Variables�������������������������������123


Stack Usage������������������������������������������������������������������������������������������������������123
Register Review������������������������������������������������������������������������������������������������124
Addressing Array Elements�������������������������������������������������������������������������������124
Stack Structure (No Function Parameters)�������������������������������������������������������126
Function Prolog�������������������������������������������������������������������������������������������������127
Raw Stack (No Local Variables and Function Parameters)�������������������������������127
Function Epilog�������������������������������������������������������������������������������������������������129

vi
Table of Contents

“Local Variables” Project����������������������������������������������������������������������������������130


Disassembly of Optimized Executable��������������������������������������������������������������133
Summary����������������������������������������������������������������������������������������������������������134

Chapter 11: Function Parameters�����������������������������������������������������135


“FunctionParameters” Project��������������������������������������������������������������������������135
Stack Structure�������������������������������������������������������������������������������������������������136
Function Prolog and Epilog�������������������������������������������������������������������������������138
Project Disassembled Code with Comments����������������������������������������������������139
Parameter Mismatch Problem��������������������������������������������������������������������������144
Summary����������������������������������������������������������������������������������������������������������145

Chapter 12: More Instructions����������������������������������������������������������147


PSTATE Flags�����������������������������������������������������������������������������������������������������147
Testing for 0������������������������������������������������������������������������������������������������������147
TST – Logical Compare�������������������������������������������������������������������������������������148
CMP – Compare Two Operands�������������������������������������������������������������������������149
TST or CMP?�����������������������������������������������������������������������������������������������������150
Conditional Jumps��������������������������������������������������������������������������������������������150
Function Return Value���������������������������������������������������������������������������������������151
Summary����������������������������������������������������������������������������������������������������������152

Chapter 13: Function Pointer Parameters����������������������������������������153


“FunctionPointerParameters” Project���������������������������������������������������������������153
Commented Disassembly���������������������������������������������������������������������������������154
Summary����������������������������������������������������������������������������������������������������������161

vii
Table of Contents

Chapter 14: Summary of Code Disassembly Patterns����������������������163


Function Prolog/Epilog��������������������������������������������������������������������������������������163
ADR (Address)���������������������������������������������������������������������������������������������������164
Passing Parameters������������������������������������������������������������������������������������������164
Accessing Saved Parameters and Local Variables��������������������������������������������165
Summary����������������������������������������������������������������������������������������������������������166

Index�������������������������������������������������������������������������������������������������167

viii
About the Author
Dmitry Vostokov is an internationally
recognized expert, speaker, educator, scientist,
and author. He is the founder of the pattern-
oriented software diagnostics, forensics,
and prognostics discipline and Software
Diagnostics Institute (DA+TA: DumpAnalysis.
org + TraceAnalysis.org). Vostokov has also
authored more than 50 books on software
diagnostics, anomaly detection and analysis,
software and memory forensics, root cause analysis and problem solving,
memory dump analysis, debugging, software trace and log analysis,
reverse engineering, and malware analysis. He has more than 25 years
of experience in software architecture, design, development, and
maintenance in various industries, including leadership, technical, and
people management roles. Dmitry also founded Syndromatix, Anolog.
io, BriteTrace, DiaThings, Logtellect, OpenTask Iterative and Incremental
Publishing (OpenTask.com), Software Diagnostics Technology and
Services (former Memory Dump Analysis Services; PatternDiagnostics.
com), and Software Prognostics. In his spare time, he presents various
topics on Debugging TV and explores Software Narratology, its further
development as Narratology of Things and Diagnostics of Things (DoT),
Software Pathology, and Quantum Software Diagnostics. His current
areas of interest are theoretical software diagnostics and its mathematical
and computer science foundations, application of formal logic, artificial
intelligence, machine learning and data mining to diagnostics and anomaly
detection, software diagnostics engineering and diagnostics-driven

ix
About the Author

development, and diagnostics workflow and interaction. Recent areas


of interest also include cloud native computing, security, automation,
functional programming, and applications of category theory to software
development and big data.

x
About the Technical Reviewer
Sundar Pandian has more than three
years of experience in embedded software
development, including development of device
drivers, middleware software, and application
services for the infotainment system on the
Android platform. He’s also developed CAN
protocol drivers for the automotive braking
system on the Autosar platform.
He’s developed software with C, C++,
and Java and worked in the automotive,
semiconductor, and telecom industries. He has
a bachelor’s in electronics and communication engineering. Currently, he
serves as a firmware/middleware engineer for audio DSPs.

xi
Preface
The book covers topics ranging from ARM64 assembly language
instructions and writing programs in assembly language to pointers, live
debugging, and static binary analysis of compiled C and C++ code.
Diagnostics of core memory dumps, live and postmortem debugging
of Linux applications, services, and systems, memory forensics, malware,
and vulnerability analysis require an understanding of ARM64 assembly
language and how C and C++ compilers generate code, including
memory layout and pointers. This book is about background knowledge
and practical foundations that are needed to understand internal Linux
program structure and behavior, start working with the GDB debugger, and
use it for disassembly and reversing. It consists of practical step-by-step
exercises of increasing complexity with explanations and many diagrams,
including some necessary background topics.
By the end of the book, you will have a solid understanding of how
Linux C and C++ compilers generate binary code. In addition, you will be
able to analyze such code confidently, understand stack memory usage,
and reconstruct original C/C++ code.
The book will be useful for

• Software support and escalation engineers, cloud


security engineers, SRE, and DevSecOps

• Software engineers coming from JVM background

• Software testers

• Engineers coming from non-Linux environments, for


example, Windows or Mac OS X

xiii
Preface

• Engineers coming from non-ARM environments, for


example, x86/x64

• Linux C/C++ software engineers without assembly


language background

• Security researchers without assembly language


background

• Beginners learning Linux software reverse engineering


techniques

This book can also be used as an ARM64 assembly language and Linux
debugging supplement for relevant undergraduate-level courses.

Source Code
All source code used in this book can be downloaded from ­github.com/
apress/arm64-linux-debugging-disassembling-reversing.

xiv
CHAPTER 1

Memory, Registers,
and Simple Arithmetic
 emory and Registers Inside an
M
Idealized Computer
Computer memory consists of a sequence of memory cells, and each cell
has a unique address (location). Every cell contains a “number.” We refer
to these “numbers” as contents at addresses (locations). Because memory
access is slower than arithmetic instructions, there are so-called registers
to speed up complex operations that require memory to store temporary
results. We can also think about them as stand-alone memory cells. The
name of a register is its address. Figure 1-1 illustrates this.

© Dmitry Vostokov 2023 1


D. Vostokov, Foundations of ARM64 Linux Debugging, Disassembling, and Reversing,
https://doi.org/10.1007/978-1-4842-9082-8_1
Chapter 1 Memory, Registers, and Simple Arithmetic

Figure 1-1. Computer memory represented as a sequence of memory


cells and locations

 emory and Registers Inside ARM


M
64-Bit Computer
Here, addresses for memory locations containing integer values usually
differ by four or eight, and we also show two registers called X0 and X1.
The first halves of them are called W0 and W1 as shown in Figure 1-2.

2
Chapter 1 Memory, Registers, and Simple Arithmetic

Figure 1-2. Typical ARM 64-bit memory and register layout

Because memory cells contain “numbers,” we start with simple


arithmetic and ask a processor to compute the sum of two numbers to see
how memory and registers change their values.

“ Arithmetic” Project: Memory Layout


and Registers
For our project, we have two memory addresses (locations) that we call
“a” and “b.” We can think about “a” and “b” as names of their respective
addresses (locations). Now we introduce a special notation where (a)

3
Chapter 1 Memory, Registers, and Simple Arithmetic

means contents at the memory address (location) “a.” If we use the C


or C++ language to write our project, we declare and define memory
locations “a” and “b” as

static int a, b;

By default, when we load a program, static memory locations are filled


with zeroes, and we can depict our initial memory layout after loading the
program, as shown in Figure 1-3.

Figure 1-3. Initial memory layout after loading the program

4
Chapter 1 Memory, Registers, and Simple Arithmetic

“Arithmetic” Project: A Computer Program


We can think of a computer program as a sequence of instructions for
the manipulation of contents of memory cells and registers. For example,
addition operation: add the contents of memory cell №12 to the contents
of memory cell №14. In our pseudo-code, we can write

[14] <- [14] + [12]

Our first program in pseudo-code is shown on the left of the table:

[a] <- 1 Here, we put assembly instructions corresponding to


[b] <- 1 pseudo-code.
[b] <- [b] + [a]
[a] <- [a] + 1
[b] <- [b] * [a]

“<-” means moving (assigning) the new value to the contents of a


memory location (address). “//” is a comment sign, and the rest of the line
is a comment. “=” shows the current value at a memory location (address).
To remind, a code written in a high-level programming language is
translated to a machine language by a compiler. However, the machine
language can be readable if its digital codes are represented in some
mnemonic system called assembly language. For example, ADD X1, X1, #1
is increment by one of what is stored in the register memory cell X1.

“ Arithmetic” Project: Assigning Numbers


to Memory Locations
We remind that “a” means the location (address) of the memory cell,
and it is also the name of the location (address) 00000000004b2b00 (see
Figure 1-3). [a] means the contents (number) stored at the address “a.”

5
Chapter 1 Memory, Registers, and Simple Arithmetic

If we use the C or C++ language, “a” is called “the variable a,” and we
write the assignment as

a = 1;

In ARM64 assembly language, we write several instructions for that:

adr x0, a
mov w1, #1
str w1, [x0]

In the GDB disassembly output, we may see the following code:

adrp    x0, 0x4b2000
add     x0, x0, #0xb00
mov     w1, #0x1
str     w1, [x0]

We show the translation of our pseudo-code into assembly language in


the right column:

[a] <- 1          // x0 = a adr  x0, a


// w1 = 1 mov  w1, #1
// [a] = 1 str  w1, [x0]
[b] <- 1          // x0 = b adr  x0, b
// w1 = 1 mov  w1, #1
// [b] = 1 str  w1, [x0]
[b] <- [b] + [a]
[a] <- [a] + 1
[b] <- [b] * [a]

adrp x0, 0x4b2000, and subsequent add x0, x0, #0xb00 is how the
compiler generates code to calculate the address “a” instead of specifying it
directly. Such code is required for addressing large regions of memory, and

6
Chapter 1 Memory, Registers, and Simple Arithmetic

the compiler uses it even for smaller regions where just one adr instruction
is sufficient.
Literal constants have the # prefix, for example, #0x1. The 0x prefix
means the following number is hexadecimal. We explain such numbers
in Chapter 3. Please also notice that the movement direction is the same
in both the disassembly output and the pseudo-code: from right to left
(except for the str instruction).
After executing the first three assembly language instructions, we have
the memory layout shown in Figure 1-4A.

Figure 1-4A. Memory layout after executing the first three assembly
language instructions

7
Chapter 1 Memory, Registers, and Simple Arithmetic

After executing the next three assembly language instructions, we have


the memory layout shown in Figure 1-4B.

Figure 1-4B. Memory layout after executing the next three assembly
language instructions

Assigning Numbers to Registers


In the previous section, we saw that assigning numbers was a part of
memory assignments. We can write in pseudo-code:

register <- 1
register <- [a]

8
Chapter 1 Memory, Registers, and Simple Arithmetic

Note that we do not use brackets when we refer to register contents.


The latter instruction means assigning (copying) the number at the
location (address) “a” to a register.
In assembly language, we write

mov  w1, #1              // 1 is copied to the first half of X1


register
mov  x1, #1              // full contents of X1 register are
replaced with 1
adr  x0, a               // copy the location (address) "a" to
X0 register
ldr  w1, [x0]            // copy the number at the location
stored in X0 to
                         // the first half of X1 register
Ldr  x1, [x0]            // copy the number at the location
stored in X0 to X1

In the GDB disassembly output, we may see the output where one
adr instruction is replaced by adrp/add instructions with parts of the
address value:

adrp x0, 0x4b2000        // 0x4b2000 + 0xb00 = 0x4b2b00


("a" address)
add  x0, x0, #0xb00
ldr  w1, [x0]

“ Arithmetic” Project: Adding Numbers


to Memory Cells
Now let's look at the following pseudo-code statement in more detail:

[b] <- [b] + [a]

9
Chapter 1 Memory, Registers, and Simple Arithmetic

To recall, “a” and “b” mean the names of locations (addresses)


00000000004b2b00 and 00000000004b2b04, respectively (see Figures 1-4A
and 1-4B). [a] and [b] mean contents at addresses “a” and “b,” respectively,
simply some numbers stored there.
In the C or C++ language, we write the following statement:

b = b + a;
b += a;

In assembly language, we use the instruction ADD. Because of ARM


architecture limitations, we cannot use memory addresses in one step
(instruction), for example, add b, b, a. We can only use the add register1,
register1, register0 instruction to add the value stored in the register0 to
the value stored in the register1 and move the result to the register1. So,
first, we need to load (ldr) contents at addresses “a” and “b” to registers
and store (str) the result back to the memory location “b” after addition.
Recall that a register is like a temporary memory cell itself here:

Register2 <- [b]


Register1 <- [a]
Register2 <- register2 + register1
[b] <- register2

In assembly language, we write

adr  x0, b
ldr  w1, [x0]
adr  x0, a
ldr  w0, [x0]
add  w1, w1, w0
adr  x0, b
str  w1, [x0]

In the GDB disassembly output, we may see the following code:

10
Chapter 1 Memory, Registers, and Simple Arithmetic

adrp x0, 0x4b2000


add  x0, x0, #0xb00
ldr  w1, [x0]
adrp x0, 0x4b2000
add  x0, x0, #0xb04
ldr  w0, [x0]
add  w1, w1, w0
adrp x0, 0x4b2000
add  x0, x0, #0xb04
str  w1, [x0]

Now we can translate our pseudo-code into assembly language:

[a] <- 1          // x0 = a adr  x0, a


// w1 = 1 mov  w1, #1
// [a] = 1 str  w1, [x0]
[b] <- 1          // x0 = b adr  x0, b
// w1 = 1 mov  w1, #1
// [b] = 1 str  w1, [x0]
[b] <- [b] + [a]  // x0 = b adr  x0, b
// w1 = 1 ldr  w1, [x0]
// x0 = a adr  x0, a
// w0 = 1 ldr  w0, [x0]
// w1 = 2 add  w1, w1, w0
// x0 = b adr  x0, b
// [b] = 2 str  w1, [x0]
[a] <- [a] + 1
[b] <- [b] * [a]

After executing ADR, LDR, ADD, and STR instructions, we have the
memory layout illustrated in Figure 1-5.

11
Chapter 1 Memory, Registers, and Simple Arithmetic

Figure 1-5. Memory layout after executing ADR, LDR, ADD, and STR
instructions

Incrementing/Decrementing Numbers
in Memory and Registers
In pseudo-code, it looks simple and means increment (decrement) a
number stored at the location (address) “a”:

[a] <- [a] + 1


[a] <- [a] – 1

In the C or C++ language, we can write this using three possible ways:

12
Chapter 1 Memory, Registers, and Simple Arithmetic

a = a + 1;
++a;
a++;
b = b – 1;
--b;
b--;

In assembly language, we use instructions ADD and SUB and write

add  x0, x0, #1
sub  x0, x0, #1

adr  x0, a
ldr  w1, [x0]
add  w1, w1, #1
str  w1, [x0]

adr  x0, b
ldr  w1, [x0]
sub  w1, w1, #1
str  w1, [x0]

In the GDB disassembly output, we may see the similar instructions:

adrp x0, 0x4b2000


add  x0, x0, #0xb00
ldr  w0, [x0]
add  w1, w0, #0x1
adrp x0, 0x4b2000
add  x0, x0, #0xb00
str  w1, [x0]

13
Chapter 1 Memory, Registers, and Simple Arithmetic

Now we add the assembly language translation of increment:

[a] <- 1          // x0 = a adr  x0, a


                  // w1 = 1 mov  w1, #1
                  // [a] = 1 str  w1, [x0]
[b] <- 1          // x0 = b adr  x0, b
                  // w1 = 1 mov  w1, #1
                  // [b] = 1 str  w1, [x0]
[b] <- [b] + [a]  // x0 = b adr  x0, b
                  // w1 = 1 ldr  w1, [x0]
                  // x0 = a adr  x0, a
                  // w0 = 1 ldr  w0, [x0]
                  // w1 = 2 add  w1, w1, w0
                  // x0 = b adr  x0, b
                  // [b] = 2 str  w1, [x0]
[a] <- [a] + 1    // x0 = a adr  x0, a
                  // w1 = 1 ldr  w1, [x0]
                  // w1 = 2 add  w1, w1, #1
                  // [a] = 2 str  w1, [x0]
[b] <- [b] * [a]

After the execution of the ADD instruction, we have the memory layout
illustrated in Figure 1-6.

14
Chapter 1 Memory, Registers, and Simple Arithmetic

Figure 1-6. Memory layout after the execution of the ADD instruction

Multiplying Numbers
In pseudo-code, we write

[b] <- [b] * [a]

It means that we multiply the number at the location (address) “b” by


the number at the location (address) “a.”
In the C or C++ language, we can write that using two ways:

b =  b * a;
b *= a;

15
Chapter 1 Memory, Registers, and Simple Arithmetic

In assembly language, we use instruction MUL and write

adr  x0, b
ldr  w1, [x0]
adr  x0, a
ldr  w0, [x0]
mul  w1, w1, w0
adr  x0, b
str  w1, [x0]

In the GDB disassembly output, we may see the following code:

adrp x0, 0x4b2000


add  x0, x0, #0xb04
ldr  w1, [x0]
adrp x0, 0x4b2000
add  x0, x0, #0xb00
ldr  w0, [x0]
mul  w1, w1, w0
adrp x0, 0x4b2000
add  x0, x0, #0xb04
str  w1, [x0]

Now we add additional assembly instructions to our pseudo-code


assembly language translation:

16
Chapter 1 Memory, Registers, and Simple Arithmetic

[a] <- 1          // x0 = a adr  x0, a


                  // w1 = 1 mov  w1, #1
                  // [a] = 1 str  w1, [x0]
[b] <- 1          // x0 = b adr  x0, b
                  // w1 = 1 mov  w1, #1
                  // [b] = 1 str  w1, [x0]
[b] <- [b] + [a]  // x0 = b adr  x0, b
                  // w1 = 1 ldr  w1, [x0]
                  // x0 = a adr  x0, a
                  // w0 = 1 ldr  w0, [x0]
                  // w1 = 2 add  w1, w1, w0
                  // x0 = b adr  x0, b
                  // [b] = 2 str  w1, [x0]
[a] <- [a] + 1    // x0 = a adr  x0, a
                  // w1 = 1 ldr  w1, [x0]
                  // w1 = 2 add  w1, w1, #1
                  // [a] = 2 str  w1, [x0]
[b] <- [b] * [a]  // x0 = b adr  x0, b
                  // w1 = 2 ldr  w1, [x0]
                  // x0 = a adr  x0, a
                  // w0 = 2 ldr  w0, [x0]
                  // w1 = 4 mul  w1, w1, w0
                  // x0 = b adr  x0, b
                  // [b] = 4 str  w1, [x0]

After the execution of the STR instruction, we have the memory layout
illustrated in Figure 1-7.

17
Chapter 1 Memory, Registers, and Simple Arithmetic

Figure 1-7. Memory layout after the execution of the STR instruction

Summary
This chapter introduced CPU registers and explained the memory layout
of a simple arithmetic program. We learned basic ARM64 commands,
including loading values from and storing values in memory. We also
manually translated C and C++ code to assembly language.
The next chapter looks at assembly language code produced by a
debugger via disassembling binary code. Then, we reverse it to C and C++
code. We also compare the disassembly output of nonoptimized code to
optimized code.

18
CHAPTER 2

Code Optimization
“Arithmetic” Project: C/C++ Program
Let's rewrite our “Arithmetic” program in C/C++. Corresponding assembly
language instructions are put in comments:

int a, b;

int main(int argc, char* argv[])


{
      a = 1;              // adr  x0, a
                          // mov  w1, #1
                          // str  w1, [x0]

      b = 1;              // adr  x0, b


                          // mov  w1, #1
                          // str  w1, [x0]

      b = b + a;          // adr  x0, b


                          // ldr  w1, [x0]
                          // adr  x0, a
                          // ldr  w0, [x0]
                          // add  w1, w1, w0
                          // adr  x0, b
                          // str  w1, [x0]

© Dmitry Vostokov 2023 19


D. Vostokov, Foundations of ARM64 Linux Debugging, Disassembling, and Reversing,
https://doi.org/10.1007/978-1-4842-9082-8_2
Chapter 2 Code Optimization

      ++a;                // adr  x0, a
                          // ldr  w1, [x0]
                          // add  w1, w1, #1
                          // str  w1, [x0]

      b = b * a;          // adr  x0, b


                          // ldr  w1, [x0]
                          // adr  x0, a
                          // ldr  w0, [x0]
                          // mul  w1, w1, w0
                          // adr  x0, b
                          // str  w1, [x0]

                          // results: [a] = 2 and [b] = 4


      return 0;
}

Downloading GDB
We used one of the free ARM64 Linux compute instances available from
cloud providers. In our case, GDB was already available after provisioning.
If, in your case, GDB is not available, you need to install it together with
basic build tools. For example, in Debian:

$ sudo apt install build-essential


$ sudo apt install gdb

You may also need to download git to clone source code:

$ sudo apt install git


$ cd ~
$ git clone github.com/apress/arm64-linux-debugging-­
disassembling-reversing .

20
Chapter 2 Code Optimization

On our RHEL-type system, we updated the tools (git included)


and GDB via

$ sudo yum group install "Development Tools"


$ sudo yum install gdb

GDB Disassembly Output – No Optimization


The source code can be downloaded from the following location:

github.com/apress/arm64-linux-debugging-disassembling-
reversing/Chapter2/

If we compile and link the program in no optimization mode (default):

$ gcc ArithmeticProjectC.cpp -o ArithmeticProjectC

we get the binary executable module we can load in GDB and inspect
assembly code.
First, we run GDB with the program as a parameter:

$ gdb ./ArithmeticProjectC
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.0.2.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/
licenses/gpl.html>
This is free software: you are free to change and
redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type
"show copying"
and "show warranty" for details.
This GDB was configured as "aarch64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...

21
Chapter 2 Code Optimization

Reading symbols from /home/coredump/pflddr/A64/Chapter2/


ArithmeticProjectC...(no debugging symbols found)...done.
(gdb)

Next, we put a breakpoint at our main C/C++ function to allow the


program execution to stop at that point and give us a chance to inspect
memory and registers. Symbolic names/function names like "main" can be
used instead of code memory locations:

(gdb) break main


Breakpoint 1 at 0x4005bc

Then we start the execution of the program (let it run). The program
then stops at the previously set breakpoint:

Starting program: /home/coredump/pflddr/A64/Chapter2/./


ArithmeticProjectC

Breakpoint 1, 0x00000000004005bc in main ()


Missing separate debuginfos, use: debuginfo-install
glibc-2.17-325.0.2.el7_9.aarch64

Now we disassemble the main function:

(gdb) disass main


Dump of assembler code for function main:
   0x00000000004005b8 <+0>:     sub     sp, sp, #0x10
=> 0x00000000004005bc <+4>:     str     w0, [sp,#12]
   0x00000000004005c0 <+8>:     str     x1, [sp]
   0x00000000004005c4 <+12>:    adrp    x0, 0x420000
   0x00000000004005c8 <+16>:    add     x0, x0, #0x20
   0x00000000004005cc <+20>:    mov     w1, #0x1          // #1
   0x00000000004005d0 <+24>:    str     w1, [x0]
   0x00000000004005d4 <+28>:    adrp    x0, 0x420000
   0x00000000004005d8 <+32>:    add     x0, x0, #0x24

22
Chapter 2 Code Optimization

   0x00000000004005dc <+36>:    mov     w1, #0x1          // #1


   0x00000000004005e0 <+40>:    str     w1, [x0]
   0x00000000004005e4 <+44>:    adrp    x0, 0x420000
   0x00000000004005e8 <+48>:    add     x0, x0, #0x24
   0x00000000004005ec <+52>:    ldr     w1, [x0]
   0x00000000004005f0 <+56>:    adrp    x0, 0x420000
   0x00000000004005f4 <+60>:    add     x0, x0, #0x20
   0x00000000004005f8 <+64>:    ldr     w0, [x0]
   0x00000000004005fc <+68>:    add     w1, w1, w0
   0x0000000000400600 <+72>:    adrp    x0, 0x420000
   0x0000000000400604 <+76>:    add     x0, x0, #0x24
   0x0000000000400608 <+80>:    str     w1, [x0]
   0x000000000040060c <+84>:    adrp    x0, 0x420000
   0x0000000000400610 <+88>:    add     x0, x0, #0x20
   0x0000000000400614 <+92>:    ldr     w0, [x0]
   0x0000000000400618 <+96>:    add     w1, w0, #0x1
   0x000000000040061c <+100>:   adrp    x0, 0x420000
   0x0000000000400620 <+104>:   add     x0, x0, #0x20
   0x0000000000400624 <+108>:   str     w1, [x0]
---Type <return> to continue, or q <return> to quit---
   0x0000000000400628 <+112>:   adrp    x0, 0x420000
   0x000000000040062c <+116>:   add     x0, x0, #0x24
   0x0000000000400630 <+120>:   ldr     w1, [x0]
   0x0000000000400634 <+124>:   adrp    x0, 0x420000
   0x0000000000400638 <+128>:   add     x0, x0, #0x20
   0x000000000040063c <+132>:   ldr     w0, [x0]
   0x0000000000400640 <+136>:   mul     w1, w1, w0
   0x0000000000400644 <+140>:   adrp    x0, 0x420000
   0x0000000000400648 <+144>:   add     x0, x0, #0x24
   0x000000000040064c <+148>:   str     w1, [x0]

23
Chapter 2 Code Optimization

   0x0000000000400650 <+152>:   mov     w0, #0x0          // #0


   0x0000000000400654 <+156>:   add     sp, sp, #0x10
   0x0000000000400658 <+160>:   ret
End of assembler dump.

We repeat the part of the formatted disassembly output here that


corresponds to our C/C++ code:

   0x00000000004005c4 <+12>:    adrp    x0, 0x420000


   0x00000000004005c8 <+16>:    add     x0, x0, #0x20
   0x00000000004005cc <+20>:    mov     w1, #0x1
   0x00000000004005d0 <+24>:    str     w1, [x0]
   0x00000000004005d4 <+28>:    adrp    x0, 0x420000
   0x00000000004005d8 <+32>:    add     x0, x0, #0x24
   0x00000000004005dc <+36>:    mov     w1, #0x1
   0x00000000004005e0 <+40>:    str     w1, [x0]
   0x00000000004005e4 <+44>:    adrp    x0, 0x420000
   0x00000000004005e8 <+48>:    add     x0, x0, #0x24
   0x00000000004005ec <+52>:    ldr     w1, [x0]
   0x00000000004005f0 <+56>:    adrp    x0, 0x420000
   0x00000000004005f4 <+60>:    add     x0, x0, #0x20
   0x00000000004005f8 <+64>:    ldr     w0, [x0]
   0x00000000004005fc <+68>:    add     w1, w1, w0
   0x0000000000400600 <+72>:    adrp    x0, 0x420000
   0x0000000000400604 <+76>:    add     x0, x0, #0x24
   0x0000000000400608 <+80>:    str     w1, [x0]
   0x000000000040060c <+84>:    adrp    x0, 0x420000
   0x0000000000400610 <+88>:    add     x0, x0, #0x20
   0x0000000000400614 <+92>:    ldr     w0, [x0]
   0x0000000000400618 <+96>:    add     w1, w0, #0x1
   0x000000000040061c <+100>:   adrp    x0, 0x420000
   0x0000000000400620 <+104>:   add     x0, x0, #0x20

24
Chapter 2 Code Optimization

   0x0000000000400624 <+108>:   str     w1, [x0]


   0x0000000000400628 <+112>:   adrp    x0, 0x420000
   0x000000000040062c <+116>:   add     x0, x0, #0x24
   0x0000000000400630 <+120>:   ldr     w1, [x0]
   0x0000000000400634 <+124>:   adrp    x0, 0x420000
   0x0000000000400638 <+128>:   add     x0, x0, #0x20
   0x000000000040063c <+132>:   ldr     w0, [x0]
   0x0000000000400640 <+136>:   mul     w1, w1, w0
   0x0000000000400644 <+140>:   adrp    x0, 0x420000
   0x0000000000400648 <+144>:   add     x0, x0, #0x24
   0x000000000040064c <+148>:   str     w1, [x0]

We can directly translate it to bare assembly code we used in the


previous chapter and put corresponding pseudo-code in comments
(memory addresses may be different on your system):

   0x00000000004005c4 <+12>:    adrp    x0, 0x420000      


// [a] <- 1
   0x00000000004005c8 <+16>:    add     x0, x0, #0x20
   0x00000000004005cc <+20>:    mov     w1, #0x1
   0x00000000004005d0 <+24>:    str     w1, [x0]
   0x00000000004005d4 <+28>:    adrp    x0, 0x420000      
// [b] <- 1
   0x00000000004005d8 <+32>:    add     x0, x0, #0x24
   0x00000000004005dc <+36>:    mov     w1, #0x1
   0x00000000004005e0 <+40>:    str     w1, [x0]
   0x00000000004005e4 <+44>:    adrp    x0, 0x420000      
// [b] <- [b] + [a]
   0x00000000004005e8 <+48>:    add     x0, x0, #0x24
   0x00000000004005ec <+52>:    ldr     w1, [x0]
   0x00000000004005f0 <+56>:    adrp    x0, 0x420000
   0x00000000004005f4 <+60>:    add     x0, x0, #0x20

25
Chapter 2 Code Optimization

   0x00000000004005f8 <+64>:    ldr     w0, [x0]


   0x00000000004005fc <+68>:    add     w1, w1, w0
   0x0000000000400600 <+72>:    adrp    x0, 0x420000
   0x0000000000400604 <+76>:    add     x0, x0, #0x24
   0x0000000000400608 <+80>:    str     w1, [x0]
   0x000000000040060c <+84>:    adrp    x0, 0x420000      
// [a] <- [a] + 1
   0x0000000000400610 <+88>:    add     x0, x0, #0x20
   0x0000000000400614 <+92>:    ldr     w0, [x0]
   0x0000000000400618 <+96>:    add     w1, w0, #0x1
   0x000000000040061c <+100>:   adrp    x0, 0x420000
   0x0000000000400620 <+104>:   add     x0, x0, #0x20
   0x0000000000400624 <+108>:   str     w1, [x0]
   0x0000000000400628 <+112>:   adrp    x0, 0x420000      
// [b] <- [b] * [a]
   0x000000000040062c <+116>:   add     x0, x0, #0x24
   0x0000000000400630 <+120>:   ldr     w1, [x0]
   0x0000000000400634 <+124>:   adrp    x0, 0x420000
   0x0000000000400638 <+128>:   add     x0, x0, #0x20
   0x000000000040063c <+132>:   ldr     w0, [x0]
   0x0000000000400640 <+136>:   mul     w1, w1, w0
   0x0000000000400644 <+140>:   adrp    x0, 0x420000
   0x0000000000400648 <+144>:   add     x0, x0, #0x24
   0x000000000040064c <+148>:   str     w1, [x0]

Now we can exit GDB:

(gdb) q
A debugging session is active.

        Inferior 1 [process 11103] will be killed.

Quit anyway? (y or n) y
$

26
Chapter 2 Code Optimization

GDB Disassembly Output – Optimization


If we compile and link the program in optimization mode:

$ gcc ArithmeticProjectC.cpp -O1 -o ArithmeticProjectC

and after repeating the same steps in GDB, we get the following output:

(gdb) disass main


Dump of assembler code for function main:
   0x00000000004005b8 <+0>:     adrp    x0, 0x420000
   0x00000000004005bc <+4>:     add     x1, x0, #0x20
   0x00000000004005c0 <+8>:     mov     w2, #0x2          // #2
=> 0x00000000004005c4 <+12>:    str     w2, [x0,#32]
   0x00000000004005c8 <+16>:    mov     w0, #0x4          // #4
   0x00000000004005cc <+20>:    str     w0, [x1,#4]
   0x00000000004005d0 <+24>:    mov     w0, #0x0          // #0
   0x00000000004005d4 <+28>:    ret

This corresponds to the following pseudo-code:

[a] <- 2
[b] <- 4

The calculation of memory addresses is a bit more complex:

   0x00000000004005b8 <+0>:   adrp  x0, 0x420000


   0x00000000004005bc <+4>:   add   x1, x0, #0x20     
// x1 = x0 +
0x20 = a
   0x00000000004005c0 <+8>:   mov   w2, #0x2          // w2 = 2
=> 0x00000000004005c4 <+12>:  str   w2, [x0,#32]      // #32 in 0x20
                                                      // [a] = [x0
+ 0x20] = 2
   0x00000000004005c8 <+16>:  mov   w0, #0x4          // w0 = 4
   0x00000000004005cc <+20>:  str   w0, [x1,#4]       // [b] = [a
+ 4] = 4

27
Chapter 2 Code Optimization

What happened to all our assembly code in this executable? This


code seems to be directly placing the end result into “a” and “b” memory
cells if we observe. Why is this happening? The answer lies in compiler
optimization. When the code is compiled in optimization mode, the
compiler can calculate the final result from the simple C/C++ source
code itself and generate only the necessary code to update corresponding
memory locations.

Summary
In this chapter, we looked at assembly language code produced by a
debugger via disassembling binary code. Then, we reversed it to C and C++
code. We also compared the disassembly output of nonoptimized code to
optimized code and understood why.
The next chapter refreshes number representations, especially the
hexadecimal one.

28
CHAPTER 3

Number
Representations
Numbers and Their Representations
Imagine a herder in ancient times trying to count his sheep. He has a
certain number of stones (twelve):

However, he can only count up to three and arranges the total into
groups of three:

© Dmitry Vostokov 2023 29


D. Vostokov, Foundations of ARM64 Linux Debugging, Disassembling, and Reversing,
https://doi.org/10.1007/978-1-4842-9082-8_3
Chapter 3 Number Representations

The last picture is a representation (a kind of notation) of the number


of stones. We have one group of three groups of three stones plus a
separate group of three stones. If he could count up to ten, we would see a
different representation of the same number of stones. We would have one
group of ten stones and another group of two stones.

Decimal Representation (Base Ten)


Let’s now see how twelve stones are represented in arithmetic notation if
we can count up to ten. We have one group of ten numbers plus two:

12dec = 1 * 10 + 2 or 1 * 101 + 2 * 100

Here is another exercise with 123 stones. We have 1 group of ten by


ten stones, another group of 2 groups of ten stones, and the last group of
3 stones:

123dec = 1 * 10*10 + 2 * 10 + 3 or 1 * 102 + 2 * 101 + 3 * 100

We can formalize it in the following summation notation:

Ndec = an*10n + an-1*10n-1 + … + a2*102 + a1*101 + a0*100


0 <= ai <= 9
Using the summation symbol, we have this formula:

n
Ndec = ∑ ai*10i
i=0

Ternary Representation (Base Three)


Now we come back to our herder’s example of twelve stones. We have
1 group of three by three stones, 1 group of three stones, and an empty
(0) group (which is not empty if we have one stone only or have thirteen

30
Chapter 3 Number Representations

stones instead of twelve). We can write down the number of groups


sequentially: 110. Therefore, 110 is a ternary representation (notation) of
twelve stones, and it is equivalent to 12 written in decimal notation:

12dec = 1*32 + 1*31 + 0*30

Ndec = an*3n + an-1*3n-1 + … + a2*32 + a1*31 + a0*30


ai = 0 or 1 or 2

n
Ndec = ∑ ai*3i
i=0

Binary Representation (Base Two)


In the case of counting up to two, we have more groups for twelve stones:
1100. Therefore, 1100 is a binary representation (notation) for 12 in
decimal notation:

12dec = 1*23 + 1*22 + 0*21 + 0*20

123dec = 1*26 + 1*25 + 1*24 + 1*23 + 0*22 + 1*21 + 1*20 or


11110112
Ndec = an*2n + an-1*2n-1 + … + a2*22 + a1*21 + a0*20
ai = 0 or 1

n
Ndec = ∑ ai*2i
i=0

31
Another random document with
no related content on Scribd:
The Project Gutenberg eBook of Phoenix
This ebook is for the use of anyone anywhere in the United States
and most other parts of the world at no cost and with almost no
restrictions whatsoever. You may copy it, give it away or re-use it
under the terms of the Project Gutenberg License included with this
ebook or online at www.gutenberg.org. If you are not located in the
United States, you will have to check the laws of the country where
you are located before using this eBook.

Title: Phoenix

Author: Marion Zimmer Bradley


Ted White

Illustrator: Virgil Finlay

Release date: December 13, 2023 [eBook #72397]

Language: English

Original publication: New York, NY: Ziff-Davis Publishing Company,


1962

Credits: Greg Weeks, Mary Meehan and the Online Distributed


Proofreading Team at http://www.pgdp.net

*** START OF THE PROJECT GUTENBERG EBOOK PHOENIX ***


PHOENIX

By TED WHITE and


MARION ZIMMER BRADLEY

Illustrated by FINLAY

From nowhere had come the flames ... giving him life
and death together. Now he summoned them again, not
knowing the depths—or heights—of his power.

[Transcriber's Note: This etext was produced from


Amazing Stories February 1963.
Extensive research did not uncover any evidence that
the U.S. copyright on this publication was renewed.]
"... he relaxed and gave himself up to the heady luxury of the
roaring fire which clothed his body. He basked in flames."

He lived. He was aware. He was everything in his world. He was....


Flames wrapped themselves around his body, pouring sinuously
around him. For a few seconds, as he stood in the center of the floor,
he writhed; pure reflex; then he relaxed and gave himself up to the
heady luxury of the roaring fire which clothed his body. He basked in
flames.
His mind was afire, too. It feels like ... like satin ice! No, it's different.
It's ... it was something new; his senses were still adjusting
themselves to the new reality, and his mind contained no images with
which to compare it. He didn't see or hear Fran open the door.
"MAX!"
He shook his flaming body and a few brief cinders fell away in sparks.
Then, suddenly, he had snuffed out the aura of flames; he was
standing nude on a smoking carpet, grinning tentatively at the girl. He
swallowed and said "Hell of a time for you to show up, Fran."
She seemed to stare at him without seeing him, her face taut, without
expression. He blinked, slowly coming down or up to reality again.
Good God, yes, she thought he'd been burning up. The odor of the
carpet—it smelled like scorching hair.
"I forgot about the carpet." He watched her glance down at it. Acrid
smoke still curled away from two singed-bare patches where he'd
been standing.
Slowly, Fran raised her eyes back to his. She said "Max—!"
She took one faltering step toward him; then she crumpled and
swayed forward. He caught her in his arms as she fell, straining her
close. The physical contact of their bodies brought him back to the
level of reality again, to a complete realization of Fran's plight. He
tried to make his grip as firm, as reassuring as he could—to bring her
back to a world in which men were not, one minute, cloaked in
streaming flame, and the next minute alive and human and—
"Max!" She straightened, "You don't have any clothes on!"
"I know. I lose more pajamas that way," he said, lightly, keeping his
voice casual. "Sit down, Fran, and I'll put on a pair of pants, at least."

Her face was chalk-white; the color had drained from her mouth,
leaving the lipstick like paint on a corpse. She was rigid with shock.
She hardly seemed to hear him, and let him lead her, like a child, to
the sofa. Oh, God, why did she have to come in just now?
"Lie down here for a minute, Fran. Here, put your feet up on the arm.
Fran, it's all right, I'm all right; take it easy, now. I'll be right back."
He retreated into the bedroom, quietly closed the door behind him,
and leaned against it for a moment. His whole body slumped.
The room was quiet, just a third-floor bedroom in an old house, now a
converted rooming-house for students, half filled with sunlight. Max
heard his own breathing loud in the silence, looked down at his naked
body, then at his pants, draped over the bed. He stared at them and
closed his eyes. His body grew rigid.
Slowly, the pants began to stir as if with a breeze; but all else was
still. Sunlight cut across the stationary dust-motes suspended in mid-
air, and the warm summer noon seemed to hold its breath. The pants
legs flapped.
Then, suddenly, the room was filled with a timeless density. The
silence of the moment before thickened into a tangible, measurable
dimension, possessing a reality of its own. He could taste the silence.
He rose three feet into the air, his head clearing the ceiling by inches.
As he did so, the tension dissolved from his muscles; he lay loose-
flung on the air and watched articles of clothing, first his briefs, then
pants, sweatshirt, socks and finally shoes, moving to him and draping
themselves over, around, up and onto his body, flowing onto him as if
themselves fluid.
The door opened before he reached it. He took a deep breath, set his
feet on the floor, and walked into the other room. Fran started upright
as he came in, and flinched away.
"Fran, are you afraid of me?"
She nodded, moving her mouth mutely.
Easy, for God's sake, easy, the girl's on the ragged edge of hysterics.
Take the light touch.
"Afraid of me? Now that I'm fully clothed again and didn't even
attempt felonious rape?"
"Don't laugh," she said, finding her voice. "I know what you're trying to
do. But—don't. And don't tell me that I didn't see—what I saw." Her
eyes moved quickly, a little rabbit movement, to the charred carpet,
and away again.
"Fran." He seated himself beside her and took her face in his hands.
"I'm not denying anything. What you saw—it happened, yes—but it
wasn't—" he ran out of words.
"I'm not crazy! And it wasn't an illusion!"
"Okay, then! I'm a warlock! I weave dark spells! I've sold my soul to
the devil! Do you like that any better?" He flung the words at her,
bitterly.
"Are you, Max?" she asked softly, when he had run down.
"I don't know. I don't—Fran!" He fell against her, and felt her arms
reach out for him, hold him as he collapsed at her side.

The touch did what words had failed to do; he felt the rigid, frozen
fright flow out of her as she held him; hard, clasping his spent body in
her arms. With a sigh, she drew his head against the softness of her
breast and let him lie there.
This was the best way. It had come to him without words; perhaps
there were no words. But what had he done to Fran, to this shy girl
who held him now so tightly? He sensed, through the tension of her
terror and its release, that she loved him—did he love her? When he
had asked himself that, he could not answer—yet now, in his
response to her, he sensed his own answer.
Words, more words—what did they mean? Reasoning was a barrier,
not a path. He had always felt most apart from her when he had tried
to think out their relationship into words. Better to let the words go,
better to react.
They lay together unmoving on the sofa for a moment which was, for
them, timeless—perhaps fifteen minutes, perhaps two or three hours.
They exchanged no words, no gestures, not even a kiss. They simply
were, sharing a moment of that meshed, tangible silence in which
there was no Max, no Fran; instead a gestalt, a separate emotional
entity.
"Tell me about it," she said finally.
It was like surfacing after a deep dive. He blinked. "I don't know what
happened."
"How did it begin?"
He turned slightly, snuggling closer to her, his cheek buried against
her neck, his shoulder tucked under her arm, her arms warm around
his body. He paused, then reached out for words and found that the
words were there.
"If you want to be rational about it—that is, if we can be rational about
it—I guess it's what you'd call a wild talent."
"Wild is right," she said with a shaky laugh.
"Psi power, I guess you'd call it—I can make things move, or—things
happen.

"I had a dream last night. It was a very strange kind of dream—you
know how sometimes you have dreams about flying? Like, you're
running along on the ground, and sometimes you can jump, and pull
your feet into the air, and then you paddle yourself along with your
hands—? I dreamed I'd done this and I was floating and weightless,
pulling myself around with handholds like an astronaut in a
spaceship, only the handholds were the branches of a tree. I was
floating, and pulling myself into the tree.
"Things began feeling strange. Like they were happening in double—
like the dream was fading out into sleepwalking. And then I woke up.
"Fran, I was holding on to the curtains of the window next to my bed,
and I was floating about even with the top of the open window!"
He felt her arms tighten around him, but she neither moved nor
interrupted him. Blessing her, he went on;
"It scared me silly, but my first thought was; Migod, I nearly flew out
the window—just as matter-of-fact about it as if I'd been sleepwalking
and woke up and said, Oh, I almost walked down those stairs. And
then I guess I woke up the rest of the way and really realized what
was happening, and the next thing I knew, I was lying cross-ways on
the bed, with all the breath knocked out of me."
His body had tensed again with the growing excitement in his voice;
sensing it, he shivered and moved closer into the warmth of her arms.
"Fran, don't let me do anything now!"
Slowly, under the reassurance of her touch, he felt the spasm
dissolve, flow into words again.
"When I woke up, I thought it had all been a dream—I mean, I wanted
to believe I'd dreamed it, but I knew better. I wandered out here into
the living room, and just kind of went through the motions of
breakfast, without noticing what I was doing. After a while I—well,
located myself sitting at the table, staring at my coffee and realizing it
had gone cold. I wasn't thinking, Fran, I wasn't thinking about
anything. I was just staring at that coffee and wishing it was good and
hot again, and—and then it started steaming.
"Fran, I didn't touch it, I just looked at it. I looked at it, and suddenly I
wasn't just looking at a cup-of-coffee any more. I began to see it—
really see it. I began to see the relationships of every component in
the cup and the coffee, the chemical and molecular—no, that's not
what I mean, either—I could see, not really with my eyes, the entire
series of relationships between all the overlapping fields of energy—
no—" he broke off again, helplessly.
"I can't make sense of it for you. I don't have the words, maybe there
aren't any—I could see it, you understand; I didn't try to explain it,
even to myself. I can't, now. It's just—everything's motion, and I could
reach out and—and speed it up, or slow it down, and I'd heated it up
—" he shook his head a little and was silent, clinging to her.
Her voice was levelled when she spoke, a flat surface spread thin
over panic; toneless. "I'm not a—a nuclear physicist, but it sounds as
if you were trying to put the theory of atoms and force-fields into one
word. Like—matter not being solid but just little bits of loose energy
whirling around and building up into atoms and the atoms into
molecules."
"That—I guess so. As if I'd learned to—oh, to see into them. But
how? Why?"
"It takes in a lot of territory," she said, still the flat stretched toneless
voice. "Just to wake up and find out you had it, whatever it was."
He hardly heard. He drew himself upright, his hands clasped, tensing,
searching for words. "Like—like ice and water and steam are all the
same thing, only we see them differently. We just see different
aspects of the same thing, and it's all the same, all this—motion. And
I could control it! I could control everything!"
"Max—"
"Yes, I know. It's frightening. I'm still afraid, and I think I've been afraid
ever since I woke up from that dream. I'm afraid to really try anything
—oh, thank God you came, Fran! Thank God! I think I'd have cracked
up if you hadn't!"
But the moment of complete and intense rapport was gone; Fran had
drawn away from him again, and he felt cold and afraid. He had said
too much; she was afraid of him again, and her fear, like her love,
communicated itself to him through the impalpable fibres in his very
skin. He soaked up her fear and babbled it forth again.
"I've been afraid to really try anything, because that's playing God.
I've been doing parlor tricks, Fran, because I haven't really wanted to
face the fact that I could do so much more than that!
"Think about it! I turned the air around myself to flames—and burned
off my pajamas before I thought to do more than protect my body—
because that was sort of wild and weird and ego-inflating. I've wished
my clothes on, and levitated, and moved things around—but these
are little things! Petty things. But Fran, I could have done so much
more—I could wipe out war—there are a thousand ways I could do it.
I could feed the starving and house the homeless—Hell, that's
minuscule, I could change this whole damn planet, I could change
myself, make my body so I could go anywhere, anywhere in the
whole physical universe—Fran, I could be God!"
His whole body shook. "I could be God, and I'm playing with burning
carpets! Fran—oh, Fran, it's too much for me! I'm not God, I don't
want it, I'm too small for it—I wish it was only a dream and now I
could really wake up and find it never happened—oh, Fran, Fran, tell
me what I am, tell me what to do!"
Aware only of pain and terror, he felt his face wet and did not even
know he was sobbing.
"You're Max, Max," Fran wept, "You're Max, and I love you—"

Again the touch calmed him. He clutched at her desperately, clinging


to reality, to the wholeness and rightness of her body in his arms, in a
sort of senseless terror lest that, too, should dissolve suddenly into a
flux of intermingling atoms and force-fields. He was aware only of
Fran, close and warm against him, their mingling breath, his own
rising hunger and need. He wanted to melt into her, lose himself in
her flesh and her reality. The clothes she was wearing separated
them, were a senseless intrusion into his longing for contact, for one-
ness. He moved. They were gone, her body warm and naked in his
arms.
"Fran!"
But she was white and rigid in his arms, thrusting him away, gasping
with terror. "What are you doing? Max, no!"
It was an icy shock, a rejection like a flood of ice, thrusting him back
into the wild senselessness of his sudden mad universe. He felt only
the desolation of being alone. He wept, feeling the tears on his cheek.
Did God cry?
Crying? Clutching desperately at this frigid slip of a girl for salvation,
when the whole universe awaited him? He pushed himself up, away
from her; he heard her voice as if from a great distance, making
words, but he was past words.
Blackness closed in about them; time slowed, the eddies of air
swirled to a halt, and lances of fire swirled through his mind. Then,
beyond the realm of three dimensions he saw her, clearly.
His mind shattered into a thousand crystals, reflecting prismatically
pounding emotions he could not directly face. He looked at Fran, into
her, through her, beyond her.
He saw; not the immobilized figure of a frightened woman, her body
helpless beneath his own on the sofa. In fact, he saw not even the
sofa.
He saw; beyond the immediacy of the fields of motion contained in
finite space as his body, he comprehended other patterns of sub-
atomic flux. Below him was a geometrically ordered matrix, a precise
framework simply constructed. But above it he found an area of
disorder. Complexity, confusion, patterns and sub-patterns of a
structure so immensely varied and subtly differentiated as to be
nearly incomprehensible. The motion! Movement! Life!
It offended him. It was vulgar, teeming, unruly, impossible! He began
to reach out to it....
"MAX!"
A body had flung itself at him, kicking, scratching, screaming. He was
toppled back and suddenly lying on the floor, back in the narrow
confines of a single body again. His head rang and her words were
slowly becoming words again.
They hurt his ears, jangling with their ridiculous cadences against the
sublime expanse of perception. "No, no! I loved you, but you—you're
mad—you're not Max—"
And then she had flung herself through the door and was gone, her
running footsteps growing fainter on the stairs.
Slowly, Max surrendered himself to a chair, without any awareness of
his human motions. The old chair enveloped him with the old
overstuffed cushioned arms and gave him a musty embrace and for a
moment he was part of all its enfolding past, the weariness that had
come into its unrejecting depths for comfort and rest. His face was
still wet with the tears he had shed before, and now they began to
swell and flow again, erupting and cascading almost without volition.
Fran was gone.
She was gone, and she had been all he had, all that was ever really
real to him. Dimly he sensed, without knowing, that it had been a
double failure. Fran had refused, rejected his need—but was it Fran's
fault, that he had been unable to reach her? Had he ever been able
to reach any human creature? Had he ever wanted to, except in his
own selfish desire? He spoke of loving Fran, and yet he had shied
away from that answer—until he needed her.
And so his vast paranormal powers were meaningless, because the
physical universe itself was without meaning. Ordered, yes. Finely
structured. But with no more meaning than an alarm clock. He could
be God, and yet the only safety and sanity he had felt was when Fran
drew him back from the brink of the bewildering nothingness into the
shelter of her breast.
But for all his control of things, he had been unable to achieve that
blending that meant power. He had only a meaningless power over
things which now, in essence, were only nothingness in various rates
of flow....
The sun had set and he had turned none of the lights on. The gloom
of dusk settled, blanketing his body with darkness and his mind with
despair.
If Fran—If. A meaningless word now. If Fran had only accepted him—
if he could control his own emotions as easily as the magic-show
flames he had donned! But he had feared to surrender himself to any
emotion, he had given too little of himself to Fran—and when the
moment of his need came, she had nothing of him that could call him
back safe from the borderland of bleak despair.
He wasn't fit. Like a baby given a straight razor, he could not cope
with his gift, and the outcome was inevitable. There was only one
answer.
Best do it now.

Suddenly the darkness was pierced by flames, a flickering, growing


fire which enveloped and covered his body. His clothes vanished in a
flare of flame, spreading to and attacking the soft upholstery of the
chair.
A small thing to salvage, his ego. But this was the grandiose way, the
big way—for the big failure.
He sat for long moments, crowned in golden flames, lost in
contemplation of the streams of superheated glowing ions radiated
from the burning carbon. Then the chair shifted as cloth burned
through, fibre straps released their hold on the metal springs of the
seat.
Time.
Deliberately, without emotion, he released his hold on the lines of
force which demarcated the limits of his body.
His hair vanished instantly in a shower of sparks and simultaneously
a furnace blast beat in on him. Then his skin was blistering and
blackening; gone. He collapsed into his funeral pyre, flinging out
limbs in reflex spasm and struggle, and he was....
He lived. He was aware. He was everything in his world and still
nothing; streams of force, patterns of sub-atomic flux. He was a
moment when all fear and all perception had vanished, blending into
a gestalt that was more than himself....
In her uneasy sleep, Francine floated five inches above the surface of
her bed.
THE END
*** END OF THE PROJECT GUTENBERG EBOOK PHOENIX ***

Updated editions will replace the previous one—the old editions will
be renamed.

Creating the works from print editions not protected by U.S.


copyright law means that no one owns a United States copyright in
these works, so the Foundation (and you!) can copy and distribute it
in the United States without permission and without paying copyright
royalties. Special rules, set forth in the General Terms of Use part of
this license, apply to copying and distributing Project Gutenberg™
electronic works to protect the PROJECT GUTENBERG™ concept
and trademark. Project Gutenberg is a registered trademark, and
may not be used if you charge for an eBook, except by following the
terms of the trademark license, including paying royalties for use of
the Project Gutenberg trademark. If you do not charge anything for
copies of this eBook, complying with the trademark license is very
easy. You may use this eBook for nearly any purpose such as
creation of derivative works, reports, performances and research.
Project Gutenberg eBooks may be modified and printed and given
away—you may do practically ANYTHING in the United States with
eBooks not protected by U.S. copyright law. Redistribution is subject
to the trademark license, especially commercial redistribution.

START: FULL LICENSE


THE FULL PROJECT GUTENBERG LICENSE
PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK

To protect the Project Gutenberg™ mission of promoting the free


distribution of electronic works, by using or distributing this work (or
any other work associated in any way with the phrase “Project
Gutenberg”), you agree to comply with all the terms of the Full
Project Gutenberg™ License available with this file or online at
www.gutenberg.org/license.

Section 1. General Terms of Use and


Redistributing Project Gutenberg™
electronic works
1.A. By reading or using any part of this Project Gutenberg™
electronic work, you indicate that you have read, understand, agree
to and accept all the terms of this license and intellectual property
(trademark/copyright) agreement. If you do not agree to abide by all
the terms of this agreement, you must cease using and return or
destroy all copies of Project Gutenberg™ electronic works in your
possession. If you paid a fee for obtaining a copy of or access to a
Project Gutenberg™ electronic work and you do not agree to be
bound by the terms of this agreement, you may obtain a refund from
the person or entity to whom you paid the fee as set forth in
paragraph 1.E.8.

1.B. “Project Gutenberg” is a registered trademark. It may only be


used on or associated in any way with an electronic work by people
who agree to be bound by the terms of this agreement. There are a
few things that you can do with most Project Gutenberg™ electronic
works even without complying with the full terms of this agreement.
See paragraph 1.C below. There are a lot of things you can do with
Project Gutenberg™ electronic works if you follow the terms of this
agreement and help preserve free future access to Project
Gutenberg™ electronic works. See paragraph 1.E below.

Вам также может понравиться