在Linux下使用firefox

在Linux上安装java并将java的libjavaplugin_oji.so静态库和firefox链接起来

- 在sun公司的网站上下载一个jre(注意firefox3.0不支持jre1.4);

- 安装jre
chmod +x jre-1_5_0-linux-i586-rpm.bin
./jre-1_5_0-linux-i586-rpm.bin
Do you agree to the above license terms?
[yes or no]
y
Unpacking…
Checksumming…
0
0
Extracting…
UnZipSFX 5.42 of 14 January 2001, by Info-ZIP (Zip-Bugs@…).

inflating: jre-1_5_0-linux-i586.rpm

Preparing… ########################################### [100%]

1:jre ########################################### [100%]

Done.

- link有两种方式:
一种是一个用户一个link;另一种是所有的用户使用一个link;两者的区别只是link的目录不同罢了;
一个用户一个link(假设你的名字叫test):
cd /home/test/.mozilla
mkdir plugins(如果没有这个目录的话,就是说firefox还没有安装plugin,这时就创建它)
ln -s /usr/java/jre1.5.0/plugin/i386/ns7/libjavaplugin_oji.so(这里的路径可以通过locate
libjavaplugin_oji.so命令得到)
所有的用户一个link:
在下面三个任何一个目录中建立一个link就好了;
/usr/lib/mozilla/plugins(在这个目录建立link后,会在下面的目录中出现相同的link,反之没有)
/usr/lib/mozilla/plugins-wrapped
/usr/lib/firefox-3.0b5/plugins/

- 最后重启firefox,在地址栏中输入about:plugins,检查java的plugins是否link成功;(使用rpm –ql
检查某个软件包的安装位置)
java cache的目录是: /home/test/.java/deployment/cache
java console的位置: System- >Preferences->Java

在TortoiseSVN下进行文件,文件夹的重命名

1.对文件的重命名

右键想要重命名的文件,选择TortoiseSVN → Rename; 选择该文件所在的目录,右键,Commit;
*写入Message,进行提交,OK了。

2.对文件夹的重命名

右键想要重命名的文件夹,选择TortoiseSVN → Rename;
这时会出现两个文件夹,一个是重命名后的,一个是重命名前的;
选择重命名前文件夹,右键,Commit,进行该文件夹的删除;
*选择重命名后文件夹,右键,Commit,进行该文件夹的追加;

*注意,TortoiseSVN 说可以进行文件和文件夹的移动,但是我试了一下,
结果不行,只能用删除重建的方法了。

Judge Swing Thread

  1. private void clear(){
  2. }
    1. if (SwingUtilities.isEventDispatchThread()) {
  3. clear(); //判断当前线程是否是EDT,是的话,就直接调用clear
  4. } else { //否则的话,将它放入EDT队列中
  5. SwingUtilities.invokeLater( new Runnable() {
  6. public void run() {
  7. clear();
  8. }
  9. });
  10. }

extern变量和inculde的区别

[root@localhost src2]# cat test.c
int a = 10;
static int b = 100;
[root@localhost src2]# cat main.c

#include <stdio.h>

#include “test.c”
extern int a;
extern int b;

int main(){
printf(“a is %d/n”, a);
printf(“b is %d/n”, b);
return 0;
}
[root@localhost src2]# gcc -o main main.c //static不起作用
[root@localhost src2]# ./main
a is 10
b is 100
[root@localhost src2]# cat main2.c

#include <stdio.h>

#include “test.c”
//extern int a; //这两句话不起作用
//extern int b;

int main(){
printf(“a is %d/n”, a);
printf(“b is %d/n”, b);
return 0;
}
[root@localhost src2]# gcc -o main2 main.c
[root@localhost src2]# ./main2
a is 10
b is 100
[root@localhost src2]# cat main3.c

#include <stdio.h>
//#include “test.c”
extern int a;
extern int b;

int main(){
printf(“a is %d/n”, a);
printf(“b is %d/n”, b);
return 0;
}
[root@localhost src2]# gcc -o main3 main3.c test.c
/tmp/ccm4VEgz.o(.text+0x37): In function main': //找不到b,static起作用了 : undefined reference tob’
collect2: ld returned 1 exit status
[root@localhost src2]# cat main4.c

#include <stdio.h>
//#include “test.c”
extern int a;
//extern int b;

int main(){
printf(“a is %d/n”, a);
// printf(“b is %d/n”, b);
return 0;
}
[root@localhost src2]# gcc -o main4 main4.c test.c
[root@localhost src2]# ./main4
a is 10 //ok,找到了外部的a
[root@localhost src2]# cat main5.c

#include <stdio.h>

#include “test.c”
extern int a;
extern int b;
extern int a;
extern int a; //不管多少个extern a,都不起作用

int main(){
printf(“a is %d/n”, a);
printf(“b is %d/n”, b);
return 0;
}
[root@localhost src2]# gcc -o main5 main5.c
[root@localhost src2]# ./main5
a is 10
b is 100
[root@localhost src2]# cat main6.c

#include <stdio.h>
//#include “test.c”
extern int a;
int b;
extern int a;
extern int a;

int a = 10; //定义了一个a

extern int a;
extern int a;

int main(){
printf(“a is %d/n”, a);
printf(“b is %d/n”, b);
return 0;
}
[root@localhost src2]# gcc -o main6 main6.c
/tmp/ccK8arcd.o(.text+0x37): In function main': : undefined reference tob’
collect2: ld returned 1 exit status
[root@localhost src2]# vi main6.c
[root@localhost src2]# gcc -o main6 main6.c
[root@localhost src2]# ./main6
a is 10
b is 0

但是注意,函数不能像变量那样重复声明了:
[root@localhost src2]# cat test.c

#include <stdlib.h>

int a = 10;
static int b = 100;
void test(){
printf(“function/n”);
}

static void testStatic(){
printf(“static functuion/n”);
}
[root@localhost src2]# cat main5.c

#include <stdio.h>

#include “test.c” //include了test.c
extern int a;
extern int b;
extern int a;
extern int a;
extern test(); // 声明了一个外部函数

int main(){
printf(“a is %d/n”, a);
printf(“b is %d/n”, b);
test();
return 0;
}
[root@localhost src2]# gcc -o main5 main5.c test.c
main5.c:7: error: conflicting types for ‘test’
test.c:5: error: previous definition of ‘test’ was here
[root@localhost src2]# cat main6.c

#include <stdio.h>
//#include “test.c”
extern int a;
int b;
extern int a;
extern int a;

int a = 10;

extern int a;
extern int a;
extern test(); // 声明了一个外部函数
extern test(); // 声明了一个外部函数
void test(){ // 定义了一个函数
printf(“function/nOB”);
}

int main(){
printf(“a is %d/n”, a);
printf(“b is %d/n”, b);
test();
return 0;
}
[root@localhost src2]# gcc -o main6 main6.c test.c
main6.c:14: error: conflicting types for ‘test’
main6.c:13: error: previous declaration of ‘test’ was here
main6.c:14: error: conflicting types for ‘test’
main6.c:13: error: previous declaration of ‘test’ was here

elf文件查找函数的过程

要反编译的程序:hello.c

#include<stdio.h>
int main()
{
printf(“Hello World!/n”);
return 0;
}

1.对hello进行反汇编,并查找包含”printf”的行;
可以看到它调用了地址0x80482b0的函数;
[root@localhost src]# objdump -d hello | grep printf
080482b0 <printf@plt>:
804838c: e8 1f ff ff ff call 80482b0 <printf@plt>

2.对hello的.got.plt部分进行反汇编;
[root@localhost src]# objdump -d -j .got.plt hello

hello: file format elf32-i386

Disassembly of section .got.plt:

08049570 <_GLOBAL_OFFSET_TABLE_>:
8049570: a4 94 04 08 00 00 00 00 00 00 00 00 a6 82 04 08 …………….
8049580: b6 82 04 08

3.对hello进行反汇编,并查找包含”.rel.plt”的行;
[root@localhost src]# objdump -s hello | grep .rel.plt -A3
Contents of section .rel.plt:
8048268 7c950408 07010000 80950408 07020000 |……………
Contents of section .init:
8048278 5589e583 ec08e861 000000e8 b4000000 U……a……..

4.显示所有的Section,并查找包含.got.plt 的行;
[root@localhost src]# readelf -S hello | grep .got.plt
[21] .got.plt PROGBITS 08049570 000570 000014 04 WA 0 0 4

5.对hello的plt部分进行反汇编,可以看到地址0x80482b0 处的汇编代码,
jmp 0x8049580 语句的意思是跳转到got,从第二步可以看到,它跳到了got中;
got中的内容为b6 82 04 08 ,它的小字节序是08 04 82 b6;
地址0x080482b又是plt中的push $0x8,0x8是在重定向表中的偏移量,可以从第三步看到;
jmp 8048290 <_init+0x18> 又跳到了plt的开头;
pushl 0x8049574 0x8049574位于got中;
jmp
0x8049578 然后调用位于0x8049578 的东西,但是该位置是零。

[root@localhost src]# objdump -d -j .plt hello

hello: file format elf32-i386

Disassembly of section .plt:

08048290 <__libc_start_main@plt-0x10>:
8048290: ff 35 74 95 04 08 pushl 0x8049574
8048296: ff 25 78 95 04 08 jmp *0x8049578
804829c: 00 00 add %al,(%eax)

080482a0 <__libc_start_main@plt>:
80482a0: ff 25 7c 95 04 08 jmp *0x804957c
80482a6: 68 00 00 00 00 push $0x0
80482ab: e9 e0 ff ff ff jmp 8048290 <_init+0x18>

080482b0 <printf@plt>:
80482b0: ff 25 80 95 04 08 jmp *0x8049580
80482b6: 68 08 00 00 00 push $0x8
80482bb: e9 d0 ff ff ff jmp 8048290 <_init+0x18>
….
这个过程叫做懒惰链接,因为重定向只发生在运行并且需要时,只要链接器完成了第一次查找,
链接器将会修改got表项,使得调用plt中的函数时,直接跳到got中,再跳到真正的函数哪里,而不是像
上面那样回到plt中,将重定向表中的偏移量入栈。

参考1:http://em386.blogspot.com/2006/10/resolving-elf-relocation-name-
symbols.html

c源代码到可执行文件的过程

编译器(Compiler) 是一个将一种语言的指令集转换成等价的另一种语言
的指令集的程序。
我们通常认为编译器总是将像C++一样的高级语言转换成目标计算机硬件能够执行的文件,但是不总是这样的。很多编译器只是将代码转换成汇编语言,还有一些将一种高级语言转换成另外一种高级语言。

以下是对GCC的编译过程的描述:

预处理(Pre-Processing)
预处理程序主要完成的工作是:头文件(Include file)的插入, 宏 (Macro) 的解开,条件编译(Conditional
compilation)的处理 。生成的还是文本文件,可以使用cat等命令参看其内容。

编译(Compiler)
在这个阶段中,GCC首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,GCC把代码翻译成汇编语言。生成的还是文本文件(其中的内容变成了汇编),可以使用cat等命令参看其内容。

汇编(Assembler)
将汇编语言转换成机器语言。生成的是对象文件(object file/code),可是使用readelf和objdump等命令参看其内容。
在对象文件中出现的只有机器码,但是它通常还包含代码执行时需要的数据,重定位信息,注释,栈,用来进行链接和调试
的程序符号(变量和程序的名字) ;

对象文件可以分为三类:

可重定向的对象文件(Relocatable object file)在编译阶段它可以和其它的可重定的对象文件生成可执行的对象文件。
可执行的对象文件(Executable object file)可以直接加载到内存中运行的程序。
共享的对象文件(Shared object file) 它是一种特殊的可重定向的对象文件 ,它还可以被加载到内存中并进行动态链接。
编译器和汇编器生成可重定向的对象文件 ,链接器将这些文件组合在一起生成可执行文件。

链接(Link)
链接就是将参与链接的对象文件合并成一个可执行文件。
链接的第一步可以在编译时完成,也可以在加载时(通过loader)或者运行时完成(通过应用程序)。
第一步中的关键是符号解析(Symbol resolution),静态链接的是静态库,动态链接的动态库;
静态链接是将需要的外部函数和/或符号直接加到生成的可执行代码中的,动态链接是将需要的外部函数和/或符号
分别记录在PLT(procedure linkage table)和GOT(global offset table,它里面存放的是所有可访问的全局变量)中,
在需要的时候通过动态加载机制将对应的表项填好共调用者使用;

第二步中的关键是重定向(Relocation):
编译器和汇编器生成的对象文件都是从零开始寻址的,重定向就是将所有参与链接的对象文件中的相同的部分放在一起,同时给新建立的每个部分(Section)每个符号一个唯一的虚拟地址;
然后将代码中所有的引用更 新,使得它们指向正确的调整过的地址;

实践

环境是:
Linux版本:Linux localhost.localdomain 2.6.9-78.ELsmp #1 SMP Wed Jul 9 15:39:47
EDT 2008 i686 i686 i386 GNU/Linux
GCC版本:gcc version 3.4.6 20060404 (Red Hat 3.4.6-10)

要编译的程序:hello.c

#include <stdio.h>
int main()
{
printf(“Hello World!/n”);
return 0;
}

gcc -E hello.c -o hello.i (预处理)

gcc -S hello.i (编译)

gcc -c hello.s (汇编)

gcc hello.o -o hello (动态链接,GCC默认使用的方式)

./hello (运行)

Hello World!

gcc -shared -fPIC -o libmyhello.so hello.o (建立动态库,要将之变成位置无关代码)

ar cr libmyhello.a hello.o (建立静态库,它不需要特殊的修改,只要打包就好了)

gcc hello.o -o hello (动态链接)

gcc -static hello.o -o hello.static (静态链接)

gcc -o hello main.c -L. –lmyhello (在程序中使用它们)

其他
PIC的意思是位置无关代码,不管将它加载到内存的什么地方都可以正常执行;

ELF文件有两个视图,一个是从程序运行角度来说的(segment),另一个是从程序链接角度来说的(section),
两个视图的情况可以在ELF文件的header中找到。
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x5d954 0x5d954 R E 0x1000
LOAD 0x05d954 0x080a6954 0x080a6954 0x00d40 0x020e8 RW 0x1000
NOTE 0x0000b4 0x080480b4 0x080480b4 0x00020 0x00020 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
上面是程序a的视图,其中的虚拟地址是通过基地址(Base Address) 来计算的。
基地址是在执行的过程中从下面三个数值计算出来的:
内存加载地址
最大页面大小
程序的可加载段的最低虚地址
简单的说,每个进程地址空间(process address space)被分为两部分,用户空间和内核空间,
用户空间的范围是:0x00000000-0xc0000000 ,内核空间的范围是0xc0000000 以上。
基地基地址计算出来后,只是加上各个segment对应的偏移量就形成了虚拟地址。

08048000-080a6000 r-xp 00000000 fd:00 88175
/root/workspace/test/src/hello.static
080a6000-080a8000 rw-p 0005d000 fd:00 88175
/root/workspace/test/src/hello.static
080a8000-080a9000 rw-p 080a8000 00:00 0
092dd000-092fe000 rw-p 092dd000 00:00 0
bff8b000-c0000000 rw-p bff8b000 00:00 0
ffffe000-fffff000 r-xp 00000000 00:00 0
上面是a运行时使用的虚拟内存区域(Virtual Memory Area–VMA),值得一提是的,最后一个segment
是程序的栈,它的结束地址是c0000000 ,开始地址是系统计算的。

参考1:http://hi.baidu.com/ahli/blog/item/45ddb31b48ab581c8718bfb4.html
参考2:http://www.linuxforums.org/misc/understanding_elf_using_readelf_and_objdump.html
参考3:http://www.linuxjournal.com/article/6463