Chapter 3-01

来源:互联网 发布:人工智能培训机构 编辑:程序博客网 时间:2024/04/30 18:42

Please indicate the source if you want to reprint: http://blog.csdn.net/gaoxiangnumber1.
3.1 A Historical Perspective
3.2 Program Encodings
unix> gcc -O1 -o p p1.c p2.c
-O1 instructs the compiler to apply level-one optimizations. Increasing the level of optimization makes the final program run faster, but may increased compilation time and difficulties running debugging tools on the code. Invoking higher levels of optimization can generate code that is so heavily transformed that the relationship between the generated machine code and the original source code is difficult to understand.
First, the C preprocessor expands the source code to include any files specified with #include commands and to expand any macros, specified with #define declarations. Second, the compiler generates assembly-code versions of the two source files having names p1.s and p2.s.
Next, the assembler converts the assembly code into binary object-code files p1.o and p2.o. Object code is one form of machine code—it contains binary representations of all of the instructions, but the addresses of global values are not yet filled in.
Finally, the linker merges these two object-code files along with code implementing library functions (e.g., printf) and generates the final executable code file p. Executable code is the second form of machine code that is the exact form of code executed by the processor.
3.2.1 Machine-Level Code
The format and behavior of a machine-level program is defined by the instruction set architecture(“ISA”), which defining the processor state, the format of the instructions, and the effect each of these instructions will have on the state.
Most ISAs (IA32 and x86-64) describe the behavior of a program as if each instruction is executed in sequence. The processor hardware executes many instructions concurrently, but they employ safeguards to ensure that the overall behavior matches the sequential operation dictated by the ISA.
The memory addresses used by a machine-level program are virtual addresses, providing a memory model that appears to be a very large byte array.
Parts of the processor state are visible:
***The program counter (i.e.: “PC,” and called %eip in IA32) indicates the address in memory of the next instruction to be executed.
***The integer register file contains eight named locations storing 32-bit values. These registers can hold addresses (C pointers) or integer data.
***The condition code registers hold status information about the most recently executed arithmetic or logical instruction. These are used to implement conditional changes in the control or data flow(e.g.: if, while).
***A set of floating-point registers store floating-point data.
C provides a model in which objects of different data types can be declared and allocated in memory, machine code views the memory as simply a large, byte-addressable array. Aggregate data types in C such as arrays and structures are represented in machine code as contiguous collections of bytes. For scalar data types, assembly code makes no distinctions between signed or unsigned integers, between different types of pointers, or even between pointers and integers.
The program memory contains the executable machine code for the program, some information required by the operating system, a run-time stack for managing procedure calls and returns, and blocks of memory allocated by the user (for example, by using the malloc library function).
The program memory is addressed using virtual addresses. At any given time, only limited subranges of virtual addresses are considered valid. For example, although the 32-bit addresses of IA32 potentially span a 4-gigabyte range of address values, a typical program will only have access to a few megabytes. The operating system manages this virtual address space, translating virtual addresses into the physical addresses of values in the actual processor memory.
3.2.2 Code Examples
unix> gcc -O1 -S code.c
Make gcc to run the compiler, generating an assembly file code.s, and go no further. Each indented line in the code.s corresponds to a single machine instruction.
unix> gcc -O1 -c code.c
Generate an object-code file code.o that is in binary format and cannot be viewed directly.
The program actually executed by the machine is simply a sequence of bytes encoding a series of instructions. The machine has very little information about the source code from which these instructions were generated.
Disassemblers generate a format similar to assembly code from the machine code.
unix> objdump -d code.o
Show the disassembly of codes in binary file code.o.
3.3 Data Formats
All pointers are stored as 4-byte double words.
Most assembly-code instructions generated by gcc have a single-character suffix denoting the size of the operand. For example, the data movement instruction has three variants: movb (move byte), movw (move word), and movl (move double word).
3.4 Accessing Information
An IA32 CPU contains a set of eight registers storing 32-bit values. These registers are used to store integer data and pointers.
Under most conditions, the first six registers can be considered general-purpose registers with no restrictions placed on their use. The final two registers (%ebp and %esp) contain pointers to important places in the program stack.
3.4.1 Operand Specifiers
Source values can be given as constants or read from registers or memory. Results can be stored in either registers or memory.
The different operand possibilities can be classified into three types.
The first type, immediate, is for constant values. In ATT-format assembly code, these are written with a ‘followedbyanintegerusingstandardCnotation,forexample,-577 or $0x1F. Any value that fits into a 32-bit word can be used.
The second type, register, denotes the contents of one of the registers, either one of the eight 32-bit registers (e.g., %eax), one of the eight 16-bit registers (e.g., %ax), or one of the eight single-byte register elements (e.g., %al). We use the notation Ea to denote an arbitrary register a, and indicate its value with the reference R[Ea ], viewing the set of registers as an array R indexed by register identifiers.
The third type is memory reference, we access some memory location according to a computed address, often called the effective address. The most general form: Imm(Eb,Ei ,s), an immediate offset Imm, a base register Eb, an index register Ei , and a scale factor s, where s must be 1, 2, 4, or 8. The effective address = Imm + R[Eb] + R[Ei ] * s.
3.4.2 Data Movement Instructions

The instructions in the mov class copy their source values to their destinations. The source operand designates a value that is immediate, stored in a register, or stored in memory. The destination operand designates a location that is either a register or a memory address.
In IA32, a move instruction cannot have both operands refer to memory locations. Copying a value from one memory location to another requires two instructions—the first to load the source value into a register, and the second to write this register value to the destination.
The following mov instruction examples show the five possible combinations of source and destination types.
Both movs and movz instruction classes copy a smaller amount of source data to a larger data location, filling in the upper bits by either sign expansion (movs) or by zero expansion (movz).
All set the low-order byte of register %eax to the second byte of %edx.
movb: not change the other 3 bytes.
movsbl: sets the other 3 bytes to either all ones or all zeros, depending on the high-order bit of the source byte.
movzbl: sets the other 3 bytes to all zeros in any case.
The stack grows downward such that the top element of the stack has the lowest address of all stack elements. The stack pointer %esp holds the address of the top stack element.
Each of these instructions takes a single operand—the data source for pushing and the data destination for popping.
Pushing a double-word value onto the stack involves first decrementing the stack pointer by 4 and then writing the value at the new top of stack address.
-> pushl %ebp is equivalent to

The first two columns in Figure 3.5: pushl %eax when %esp is 0x108 and %eax is 0x123. First %esp is decremented by 4, giving 0x104, and then 0x123 is stored at memory address 0x104.
Popping a double word involves reading from the top of stack location and then incrementing the stack pointer by 4. Therefore, the instruction popl %eax is equivalent to the following pair of instructions:

3.4.3 Data Movement Example
xp and y are stored at offsets 8 and 12 relative to the address in register %ebp. Instructions 1 read parameter xp from memory and store it in register %edx. Instruction 2 uses register %edx and reads x into register %eax, a direct implementation of the operation x = *xp in the C program. Later, register %eax will be used to return a value from this function, and so the return value will be x. Instruction 3 loads parameter y into register %ecx. Instruction 4 then writes this value to the memory location designated by xp in register %edx, a direct implementation of the operation *xp = y.
Pointer in C are addresses. Dereferencing a pointer involves copying that pointer into a register, and then using this register in a memory reference.
Local variables such as x are often kept in registers rather than stored in memory locations. Register access is much faster than memory access.

Please indicate the source if you want to reprint: http://blog.csdn.net/gaoxiangnumber1.

0 0
原创粉丝点击