Hello World 1

本文装载自:http://www.cnblogs.com/xuqiang/archive/2010/03/29/1953689.html

微笑

首先的感谢那些无私奉献的大牛们,深入Hello World下载地址在http://blog.linux.org.tw/~jserv/archives/001844.html。在上面

还有源码的下载地址链接,同时还要感谢那些网上的勤勤恳恳写blog的bloger们。

Hello World是学习程序设计语言的第一个程序浅出 Hello World。我们试图分析自linux上的Hello World运行的整个过程,主要

包括下面的几个过程:

1.hello程序的编译链接过程和hello上可执行文件格式

2.hello可执行程序的加载及如何开始执行

3.hello在内存中镜像

4.寻址

5.调度程序

6.内存管理

7.系统调用

8.hello程序卸载

首先是hello可执行文件的连接过程,然后hello可执行文件如何被加载到内存中,然后从那里开始执行?在执行的过程中,可

能需要寻址,如何实现?可能的内核调度如何实现?内存管理如何实现?系统调用如何实现?hello程序执行完成之后,kernel

执行了那些清理的工作?

hello程序编译链接过程和hello可执行elf文件格式

vim hello.c

#include <stdio.h>
int main (int atgc, char* argv[])
{
    printf ("Hello World");
    return 0;
}

gcc hello.c -o hello

首先在Text Editor中编辑hello的source code,gcc在编译source code时,先执行预处理,完成将source code中的宏定义展开de

等功能,这个过程是由cpp完成,最终生成hello.i文件,然后由complier编译成hello.s,这个过程是由ccl完成,然后使用as

将上面的hello.s编译成hello.o,最后使用ld将上面生成的hello.o连接成可执行文件hello。

我们可以使用下面的命令来观察生成的hello程序:

file hello

hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux

2.6.15, not stripped

ldd hello

    linux-gate.so.1 =>  (0xb7f01000)
    libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7d89000)
    /lib/ld-linux.so.2 (0xb7f02000)
objdump -x hello

hello:     file format elf32-i386
hello
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x08048310
Program Header:
    PHDR off    0x00000034 vaddr 0x08048034 paddr 0x08048034 align 2**2
         filesz 0x00000100 memsz 0x00000100 flags r-x
  INTERP off    0x00000134 vaddr 0x08048134 paddr 0x08048134 align 2**0
         filesz 0x00000013 memsz 0x00000013 flags r--
    LOAD off    0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12
         filesz 0x000004c0 memsz 0x000004c0 flags r-x
    LOAD off    0x00000f0c vaddr 0x08049f0c paddr 0x08049f0c align 2**12
         filesz 0x00000108 memsz 0x00000110 flags rw-
DYNAMIC off    0x00000f20 vaddr 0x08049f20 paddr 0x08049f20 align 2**2
         filesz 0x000000d0 memsz 0x000000d0 flags rw-
    NOTE off    0x00000148 vaddr 0x08048148 paddr 0x08048148 align 2**2
         filesz 0x00000020 memsz 0x00000020 flags r--
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2
         filesz 0x00000000 memsz 0x00000000 flags rw-
   RELRO off    0x00000f0c vaddr 0x08049f0c paddr 0x08049f0c align 2**0
         filesz 0x000000f4 memsz 0x000000f4 flags r--
反汇编:objdump -d hello

hello:     file format elf32-i386
Disassembly of section .init:
08048294 <_init>:
8048294:    55                       push   %ebp
8048295:    89 e5                    mov    %esp,%ebp
8048297:    53                       push   %ebx
8048298:    83 ec 04                 sub    $0x4,%esp
804829b:    e8 00 00 00 00           call   80482a0 <_init+0xc>
80482a0:    5b                       pop    %ebx
80482a1:    81 c3 54 1d 00 00        add    $0x1d54,%ebx
80482a7:    8b 93 fc ff ff ff        mov    -0x4(%ebx),%edx
80482ad:    85 d2                    test   %edx,%edx
80482af:    74 05                    je     80482b6 <_init+0x22>
80482b1:    e8 1e 00 00 00           call   80482d4 <__gmon_start__@plt>
80482b6:    e8 e5 00 00 00           call   80483a0 <frame_dummy>
80482bb:    e8 a0 01 00 00           call   8048460 <__do_global_ctors_aux>
80482c0:    58                       pop    %eax
80482c1:    5b                       pop    %ebx
80482c2:    c9                       leave 
80482c3:    c3                       ret   
Disassembly of section .plt:
...

经过上面的观察,产生下面的疑问:ldd链接的文件的作用是什么?链接程序时发生了什么?可执行文件的格式是怎样的?为了解决上面的问题还

是得首先了解一下计算机程序的基础知识。

1.bss/data/code段,对于下面的程序:

int a;

int k = 3;

int foo (void)

{

    return (k);

}

int b  = 12;

int bar(void)

{

    a = 0;

    return (a + b);

}

在生成的汇编文件中,bss/data/text段如下:

------------------------

a = 0                                bss

-----------------------

k = 3 b = 12                    data

----------------------

ret                                   text

----------------------

于是根据汇编文件生成的可执行文件镜像大致结构如下:

---------------------

12                                      data

3

---------------------

ret                                      text

----------------------

header

---------------------

那么汇编文件中的bss段怎么没有了?在可执行文件的镜像中,在header中包含有该文件bss段的信息 。在kernel装载该hello可执行文件时,会产生

如下的内存镜像 :

-----------------------           bss,kernel根据hello的文件头header来得到bss段的信息

0

-----------------------

12 3                                   data

----------------------

ret                                      text

---------------------

上面的只是一个文件的情况,没有涉及到链接,那如果是两个.o文件,ld链接程序是如何链接程序的?首先需要说明的是每个object file都是具有相同的address space,在链接多个.o文件时,ld所作的工作就是分别将各个object file的bss text data段分别组成到新的bss text data段。最终在生成的elf

文件中:

-----------------------

elf header                        include magic number, file type, machine,...

-----------------------

program header table    page size, virtual address memory segment, segment size

-----------------------

.text section                     code

-----------------------

.data section                    initialized data

-----------------------

.bss section                      bass data (0)

-----------------------

.symtab                              symbol table

------------------------

.rel.text                               relocation information for text section

-------------------------

.rel.data                              relocation information for .data section

-------------------------

.debug                                debug information 

------------------------

section header table

------------------------

对于上面的elf文件elf header可以使用readelf -h filename来读取文件头。

readelf -h hello

ELF Header:

  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 

  Class:                             ELF32

  Data:                              2's complement, little endian

  Version:                           1 (current)

  OS/ABI:                            UNIX - System V

  ABI Version:                       0

  Type:                              EXEC (Executable file)

  Machine:                           Intel 80386

  Version:                           0x1

  Entry point address:               0x8048310

  Start of program headers:          52 (bytes into file)

  Start of section headers:          5996 (bytes into file)

  Flags:                             0x0

  Size of this header:               52 (bytes)

  Size of program headers:           32 (bytes)

  Number of program headers:         8

  Size of section headers:           40 (bytes)

  Number of section headers:         36

  Section header string table index: 33

对于上面的输出需要注意的是Entry point address: 0x8048310,Entry point address定义了elf文件执行的开始地址。下面是详细的解释:

http://blog.sina.com.cn/s/blog_5b9ea9840100avgg.html###

When executed, program will start running from virtual address 0x80482c0 (see entry point address). The "0x" prefix here means it is a hexadecimal number. This address doesn't point to our main() procedure, but to a procedure named _start. Never felt you had created such thing? Of course you don't. _start procedure is created by the linker whose purpose is to initialize your program.

同时可以使用gdb反汇编:

gdb hello

(gdb) disassemble 0x8048310

Dump of assembler code for function _start:

0x08048310 <_start+0>: xor    %ebp,%ebp

0x08048312 <_start+2>: pop    %esi

0x08048313 <_start+3>: mov    %esp,%ecx

0x08048315 <_start+5>: and    $0xfffffff0,%esp

0x08048318 <_start+8>: push   %eax

0x08048319 <_start+9>: push   %esp

0x0804831a <_start+10>: push   %edx

0x0804831b <_start+11>: push   $0x80483f0

0x08048320 <_start+16>: push   $0x8048400

0x08048325 <_start+21>: push   %ecx

0x08048326 <_start+22>: push   %esi

0x08048327 <_start+23>: push   $0x80483c4

0x0804832c <_start+28>: call   0x80482e4 <__libc_start_main@plt>

0x08048331 <_start+33>: hlt    

可见程序是在地址0x8048310开始执行(注意的是此时还是虚地址),然后通过设置调用printf函数前的相关工作,然后call   0x80482e4 <__libc_start_main@plt>调用printf函数。

综上,hello.c源程序,通过编译生成.o文件,然后在通过ld程序链接成elf可执行文件。ld在链接时,默认使用的ld  script可以使用命令查看:

ld --verbose。关于链接脚本的解释参见:http://fxl.blogbus.com/logs/12451338.在ld script中指定elf文件的加载地址和虚地址,上面的两种地址

在一般的情况下是相同的,但是在一些嵌入式的程序中加载地址和执行地址是不同的。

既然在elf文件中定义了程序在加载的虚地址,那么程序是如何被加载的?加载的内存镜像又是什么样子的?如何查看实际的物理地址?呵呵,有时间

接着开始...

你可能感兴趣的:(Hello World 1)