LINUX_book
456 pág.

LINUX_book


DisciplinaLinux712 materiais1.841 seguidores
Pré-visualização50 páginas
code used to compile the /lib/i686/libc.so.6 library, we
could then easily determine the exact line of source code by creating an assembly
listing.
We will focus more on stack tracebacks, problem determination facilities,
and using a debugger in other chapters. For now, we will concentrate on the
steps needed to create an assembly listing and how to correlate an offset with
a line of code.
4.5.2 Generating Assembly Listings
For most high-level language programmers, looking at raw assembly isn\u2019t very
easy and could take a great deal of time. Fortunately, mixing the high-level
source code with the assembly is an option.
There are two methods to achieving this. One is with the objdump(1) utility.
Another way to do this is by telling the compiler to stop at the assembly phase
and write the listing to a file. This raw assembly listing can then be run through
the system assembler with certain command line parameters to produce an
output file that intermixes assembly and high-level source code. An example
of this is done with the source code in the code saved in file as_listing.c and
listed here:
#include <stdio.h>
int main( void )
{
 int a = 5;
 int b = 3;
 int c = 0;
 char s[] = \u201cThe result is\u201d;
 c = a + b;
 printf( \u201c%s %d\n\u201d, s, c );
136 Compiling Chap. 4
 return 0;
}
A typical compilation of this source code would consist of running:
gcc -o as_listing as_listing.c
This produces the executable file as_listing. For gcc, specifying the -S flag
causes the compilation to stop before the assembling phase and dump out the
generated assembly code. By default, if -o is not used to specify an output
filename, gcc converts the input source filename extension (such as .c) to .s. To
be able to properly intermix the assembly and high-level source code, it is also
required to use the -g flag to produce debug symbols and line number
information. For the code in as_listing.c, to produce an assembly output, run
the following command:
gcc as_listing.c -S -g
The resulting as_listing.s text file can be examined, but unless you know
assembly very well, it likely won\u2019t make much sense. This is where the
importance of mixing in the high-level language comes to play. To do this, run
the system assembler, as, with the command line arguments, which turn on
listings to include assembly and high-level source:
as -alh as_listing.s > as_listing.s_c
Note: Certain compilations done using make files will compile a
source file from a different directory rather than the current one. In this
case, it may be necessary to run objdump or as -alh from the same directory
in which the make process compiled the file.
4.5.3 Reading and Understanding an Assembly Listing
For the most part, the reason for examining an assembly listing is to determine
how the compiler interpreted the high-level language and what assembly
language resulted. When first looking at an assembly listing, it can be quite
intimidating. It\u2019s important to understand that there is a lot of information in
this file that is only of use to a system\u2019s assembler. Generally, much of this
data can be ignored as often only a very specific area of the code will be desired
and referred to by a function name and an offset in a stack dump or stack
traceback for example. With this information and the assembly listing in hand,
1374.5 Assembly Listings
the first thing to do is to search for the function name in the assembly listing.
The assembly listing from the code in as_listing.c is shown below:
 16 .globl main
 17 .type main, @function
 18 main:
 19 .LFB3:
 20 .file 1 \u201cas_listing.c\u201d
 1:as_listing.c **** #include <stdio.h>
 2:as_listing.c ****
 3:as_listing.c **** int main( void )
 4:as_listing.c **** {
 21 .loc 1 4 0
 22 0000 55 pushl %ebp
 23 .LCFI0:
 24 0001 89E5 movl %esp, %ebp
 25 .LCFI1:
 26 0003 83EC28 subl $40, %esp
 27 .LCFI2:
 28 0006 83E4F0 andl $-16, %esp
 29 0009 B8000000 movl $0, %eax
 29 00
 30 000e 29C4 subl %eax, %esp
 5:as_listing.c **** int a = 5;
 31 .loc 1 5 0
 32 .LBB2:
 33 0010 C745F405 movl $5, -12(%ebp)
 33 000000
 6:as_listing.c **** int b = 3;
 34 .loc 1 6 0
 35 0017 C745F003 movl $3, -16(%ebp)
 35 000000
 7:as_listing.c **** int c = 0;
 36 .loc 1 7 0
 37 001e C745EC00 movl $0, -20(%ebp)
 37 000000
 8:as_listing.c **** char s[] = \u201cThe result is\u201d;
^LGAS LISTING as_listing.s page 2
 38 .loc 1 8 0
 39 0025 A1000000 movl .LC0, %eax
 39 00
 40 002a 8945D8 movl %eax, -40(%ebp)
 41 002d A1040000 movl .LC0+4, %eax
 41 00
 42 0032 8945DC movl %eax, -36(%ebp)
 43 0035 A1080000 movl .LC0+8, %eax
 43 00
 44 003a 8945E0 movl %eax, -32(%ebp)
 45 003d 66A10C00 movw .LC0+12, %ax
138 Compiling Chap. 4
 45 0000
 46 0043 668945E4 movw %ax, -28(%ebp)
 9:as_listing.c ****
 10:as_listing.c **** c = a + b;
As you can see, assembly is quite terse and much more lengthy than C! The
numbers at the far left that start at 16 and go up to 46 are the assembly listing
line numbers. If you open up as_listing.s and look at line 16 for example, you
will see:
.globl main
Some of the assembly listing lines will have four digit hexadecimal numbers to
the right of them. This is the offset number and is a very important number. In
the preceding assembly listing we can see that the start of the main() function
has an offset of 0:
22 0000 55 pushl %ebp
It\u2019s also important to understand that the first assembly instruction in any
function on x86 based hardware is pushl %ebp, which pushes the calling function\u2019s
frame pointer onto the stack.
Note: Some architectures such as x86-64 support the -fomit-frame-
pointer optimization flag, which is turned on by default with the -O2
optimization level (see the section, \u201cCompiler Optimization,\u201d for more
information). When an object is compiled with this flag, there will not be
any instructions at the beginning of a function to save and set up the
frame registers. Doing this is advantageous for performance reasons
because an extra register is freed up for other uses along with a few less
instructions being run. Compiling with this option can make debugging
a little more difficult, so caution may be warranted depending on your
application\u2019s need. The SuSE distributions on x86-64 are compiled with
this flag, so manual stack analysis using the frame registers is not
possible. The x86 architecture does not support the -fomit-frame-pointer
flag because debugging is impossible with it.
Note also that the start of a function is not necessarily always at offset 0;
it could potentially be any value. If it isn\u2019t 0 and you have an offset into the
function to look for, simply subtract the offset at the beginning of the function
from the offset you are looking for. This is a rule of thumb to always follow. In
the example of main(), just shown, we would be subtracting 0 from the offset,
which simply gives us the offset itself.
139
Now, looking at the line immediately above assembly line 22, we see this:
 21 .loc 1 4 0
The .loc is an assembly directive that stands for \u201cline of code.\u201d The numbers
that follow it each have their own significance. The first